From 75f5f1b7ccb55fdb5c1d9c69909b02aa321923a2 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 12:38:07 +0530 Subject: [PATCH 01/94] Search activity, image search fragment added --- app/src/main/AndroidManifest.xml | 6 + .../category/CategoryImagesActivity.java | 25 ++++ .../nrw/commons/di/ActivityBuilderModule.java | 4 + .../nrw/commons/di/FragmentBuilderModule.java | 4 + .../nrw/commons/explore/SearchActivity.java | 135 +++++++++++++++++ .../explore/images/SearchImageFragment.java | 140 ++++++++++++++++++ .../explore/images/SearchImageItem.java | 74 +++++++++ .../images/SearchImagesAdapterFactory.java | 25 ++++ .../explore/images/SearchImagesRenderer.java | 69 +++++++++ .../mwapi/ApacheHttpClientMediaWikiApi.java | 34 +++++ .../free/nrw/commons/mwapi/MediaWikiApi.java | 3 + .../commons/theme/NavigationBaseActivity.java | 8 + .../ic_arrow_back_primary_24dp.png | Bin 0 -> 215 bytes .../drawable-hdpi/ic_search_white_24dp.png | Bin 0 -> 492 bytes .../res/drawable-ldpi/ic_explore_24dp.xml | 27 ++++ .../ic_arrow_back_primary_24dp.png | Bin 0 -> 170 bytes .../drawable-mdpi/ic_search_white_24dp.png | Bin 0 -> 306 bytes .../ic_arrow_back_primary_24dp.png | Bin 0 -> 238 bytes .../drawable-xhdpi/ic_search_white_24dp.png | Bin 0 -> 574 bytes .../ic_arrow_back_primary_24dp.png | Bin 0 -> 377 bytes .../drawable-xxhdpi/ic_search_white_24dp.png | Bin 0 -> 899 bytes .../ic_arrow_back_primary_24dp.png | Bin 0 -> 366 bytes .../drawable-xxxhdpi/ic_search_white_24dp.png | Bin 0 -> 1143 bytes app/src/main/res/layout/activity_search.xml | 62 ++++++++ .../main/res/layout/fragment_browse_image.xml | 31 ++++ .../res/layout/layout_category_images.xml | 1 - app/src/main/res/menu/menu_search.xml | 13 ++ app/src/main/res/values/strings.xml | 4 + 28 files changed, 664 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java create mode 100644 app/src/main/res/drawable-hdpi/ic_arrow_back_primary_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_search_white_24dp.png create mode 100644 app/src/main/res/drawable-ldpi/ic_explore_24dp.xml create mode 100644 app/src/main/res/drawable-mdpi/ic_arrow_back_primary_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_search_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_arrow_back_primary_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_arrow_back_primary_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_arrow_back_primary_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png create mode 100644 app/src/main/res/layout/activity_search.xml create mode 100644 app/src/main/res/layout/fragment_browse_image.xml create mode 100644 app/src/main/res/menu/menu_search.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17f6770d2f..53e3a78f9c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -96,6 +96,12 @@ android:label="@string/title_activity_featured_images" android:parentActivityName=".contributions.ContributionsActivity" /> + + onBackPressed()); + supportFragmentManager = getSupportFragmentManager(); + setBrowseImagesFragment(); + RxTextView.textChanges(etSearchKeyword) + .takeUntil(RxView.detaches(etSearchKeyword)) + .debounce(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( query -> { + //update image list + if (!TextUtils.isEmpty(query)) { + this.query = query.toString(); + searchImageFragment.updateImageList(query.toString()); + }else { + // open search history fragment + } + } + ); + } + + + private void setBrowseImagesFragment() { + searchImageFragment = new SearchImageFragment(); + FragmentTransaction transaction = supportFragmentManager.beginTransaction(); + transaction.add(R.id.fragmentContainer, searchImageFragment).commit(); + } + + @Override + public Media getMediaAtPosition(int i) { + return searchImageFragment.getImageAtPosition(i); + } + + @Override + public int getTotalMediaCount() { + return searchImageFragment.getTotalImagesCount(); + } + + @Override + public void notifyDatasetChanged() { + + } + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + + } + + public void onSearchImageClicked(SearchImageItem searchImageItem, int index) { + this.searchImageItem = searchImageItem; + ViewUtil.hideKeyboard(this.findViewById(R.id.searchBox)); + toolbar.setVisibility(View.GONE); + setToolbarVisibility(true); + if (mediaDetails == null || !mediaDetails.isVisible()) { + // set isFeaturedImage true for featured images, to include author field on media detail + mediaDetails = new MediaDetailPagerFragment(false, true); + FragmentManager supportFragmentManager = getSupportFragmentManager(); + supportFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, mediaDetails) + .addToBackStack(null) + .commit(); + supportFragmentManager.executePendingTransactions(); + } + mediaDetails.showImage(index); + } + + @Override + public void onBackPressed() { + if (getSupportFragmentManager().getBackStackEntryCount()==1){ + if (!TextUtils.isEmpty(query)) { + searchImageFragment.updateImageList(query); + } + toolbar.setVisibility(View.VISIBLE); + setToolbarVisibility(false); + }else { + toolbar.setVisibility(View.GONE); + setToolbarVisibility(true); + } + super.onBackPressed(); + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java new file mode 100644 index 0000000000..8f263645c7 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -0,0 +1,140 @@ +package fr.free.nrw.commons.explore.images; + + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.pedrogomez.renderers.RVRendererAdapter; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import butterknife.BindView; +import butterknife.ButterKnife; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.explore.SearchActivity; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +/** + * Displays the image search screen. + */ + +public class SearchImageFragment extends CommonsDaggerSupportFragment { + + public static final int SEARCH_IMAGES_LIMIT = 25; + + @BindView(R.id.imagesListBox) + RecyclerView imagesList; + @BindView(R.id.imageSearchInProgress) + ProgressBar imageSearchInProgress; + @BindView(R.id.imagesNotFound) + TextView imagesNotFoundView; + + @Inject + MediaWikiApi mwApi; + @Inject @Named("default_preferences") SharedPreferences prefs; + + private RVRendererAdapter imagesAdapter; + private List images=new ArrayList<>(); + private List queryList = new ArrayList<>(); + + private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { + int index = queryList.indexOf(item); + ((SearchActivity)getContext()).onSearchImageClicked(item,index); +// Add images to recently searched images db table and open to Media Details Fragment + }); + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); + ButterKnife.bind(this, rootView); + + imagesList.setLayoutManager(new LinearLayoutManager(getContext())); + + ArrayList items = new ArrayList<>(); + + imagesAdapter = adapterFactory.create(items); + imagesList.setAdapter(imagesAdapter); + + return rootView; + } + + public void updateImageList(String query) { + queryList.clear(); + Observable.fromIterable(images) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnSubscribe(disposable -> { + imageSearchInProgress.setVisibility(View.VISIBLE); + imagesNotFoundView.setVisibility(View.GONE); + imagesAdapter.clear(); + }) + .observeOn(Schedulers.io()) + .concatWith( + searchImages(query) + ) + .distinct() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + s ->{ + imagesAdapter.add(s); + queryList.add(s); + }, + Timber::e, + () -> { + imagesAdapter.notifyDataSetChanged(); + imageSearchInProgress.setVisibility(View.GONE); + + if (imagesAdapter.getItemCount() == images.size()) { + if (TextUtils.isEmpty(query)) { + imagesAdapter.clear(); + + } else { + imagesNotFoundView.setText(getString(R.string.images_not_found, query)); + imagesNotFoundView.setVisibility(View.VISIBLE); + } + } + } + ); + } + + private Observable searchImages(String query) { + return mwApi.searchImages(query, SEARCH_IMAGES_LIMIT) + .map(s -> new SearchImageItem(s,false)); + } + + public int getTotalImagesCount(){ + if (imagesAdapter == null) { + return 0; + }else { + return imagesAdapter.getItemCount(); + } + } + + public Media getImageAtPosition(int i) { + if (imagesAdapter.getItem(i).getName() == null) { + // not yet ready to return data + return null; + } else { + return new Media(imagesAdapter.getItem(i).getName()); + } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java new file mode 100644 index 0000000000..26b1dd9c1a --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java @@ -0,0 +1,74 @@ +package fr.free.nrw.commons.explore.images; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SearchImageItem implements Parcelable { + private final String name; + private boolean image_stored_in_history; + + public static Creator CREATOR = new Creator() { + @Override + public SearchImageItem createFromParcel(Parcel parcel) { + return new SearchImageItem(parcel); + } + + @Override + public SearchImageItem[] newArray(int i) { + return new SearchImageItem[0]; + } + }; + + public SearchImageItem(String name, boolean image_stored_in_history) { + this.name = name; + this.image_stored_in_history = image_stored_in_history; + } + + private SearchImageItem(Parcel in) { + name = in.readString(); + image_stored_in_history = in.readInt() == 1; + } + + public String getName() { + return name; + } + + public boolean isImage_stored_in_history() { + return image_stored_in_history; + } + + public void setImage_stored_in_history(boolean image_stored_in_history) { + this.image_stored_in_history = image_stored_in_history; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(name); + parcel.writeInt(image_stored_in_history ? 1 : 0); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + SearchImageItem that = (SearchImageItem) o; + + return name.equals(that.name); + + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java new file mode 100644 index 0000000000..2d9e2a1e74 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java @@ -0,0 +1,25 @@ +package fr.free.nrw.commons.explore.images; + +import com.pedrogomez.renderers.ListAdapteeCollection; +import com.pedrogomez.renderers.RVRendererAdapter; +import com.pedrogomez.renderers.RendererBuilder; + +import java.util.Collections; +import java.util.List; + + +class SearchImagesAdapterFactory { + private final SearchImagesRenderer.ImageClickedListener listener; + + SearchImagesAdapterFactory(SearchImagesRenderer.ImageClickedListener listener) { + this.listener = listener; + } + + public RVRendererAdapter create(List searchImageItemList) { + RendererBuilder builder = new RendererBuilder() + .bind(SearchImageItem.class, new SearchImagesRenderer(listener)); + ListAdapteeCollection collection = new ListAdapteeCollection<>( + searchImageItemList != null ? searchImageItemList : Collections.emptyList()); + return new RVRendererAdapter<>(builder, collection); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java new file mode 100644 index 0000000000..f4390baea4 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java @@ -0,0 +1,69 @@ +package fr.free.nrw.commons.explore.images; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +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; + +class SearchImagesRenderer extends Renderer { + @BindView(R.id.categoryImageTitle) TextView tvImageName; + @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; + @BindView(R.id.categoryImageView) + MediaWikiImageView browseImage; + + private final ImageClickedListener listener; + + SearchImagesRenderer(ImageClickedListener listener) { + this.listener = listener; + } + + @Override + protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { + return layoutInflater.inflate(R.layout.layout_category_images, viewGroup, false); + } + + @Override + protected void setUpView(View view) { + ButterKnife.bind(this, view); + } + + @Override + protected void hookListeners(View view) { + view.setOnClickListener(v -> { + SearchImageItem item = getContent(); + if (listener != null) { + listener.imageClicked(item); + } + }); + } + + @Override + public void render() { + SearchImageItem item = getContent(); + Media media = new Media(item.getName()); + tvImageName.setText(item.getName()); + browseImage.setMedia(media); + setAuthorView(media, categoryImageAuthor); + } + + interface ImageClickedListener { + void imageClicked(SearchImageItem item); + } + + private void setAuthorView(Media item, TextView author) { + if (item.getCreator() != null && !item.getCreator().equals("")) { + String uploadedByTemplate = getContext().getString(R.string.image_uploaded_by); + author.setText(String.format(uploadedByTemplate, item.getCreator())); + } else { + author.setVisibility(View.VISIBLE); + } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index e962bdaf34..33a2599621 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -514,6 +514,40 @@ public List getCategoryImages(String categoryName) { return CategoryImageUtils.getMediaList(childNodes); } + @Override + @NonNull + public Observable searchImages(String filterValue, int searchImagesLimit) { + return Single.fromCallable(() -> { + List imageNodes = null; + try { + imageNodes = api.action("query") + .param("format", "xml") + .param("list", "search") + .param("srwhat", "text") + .param("srnamespace", "6") + .param("srlimit", searchImagesLimit) + .param("srsearch", filterValue) + .get() + .getNodes("/api/query/search/p/@title"); + } catch (IOException e) { + Timber.e("Failed to obtain searchImages", e); + } + + if (imageNodes == null) { + return new ArrayList(); + } + + List categories = new ArrayList<>(); + for (ApiResult imageNode : imageNodes) { + String imgName = imageNode.getDocument().getTextContent(); + categories.add(imgName); + } + + return categories; + }).flatMapObservable(Observable::fromIterable); + } + + /** * For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages * https://www.mediawiki.org/wiki/API:Raw_query_continue diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index c0bd2fd879..9a8dcb7214 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -37,6 +37,9 @@ public interface MediaWikiApi { List getCategoryImages(String categoryName); + @NonNull + Observable searchImages(String title, int searchImagesLimit); + @NonNull UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener) throws IOException; diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index 4a7322b57a..24eff4d03f 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -186,4 +186,12 @@ public static void startActivityWithFlags(Context context, Class cls, int } context.startActivity(intent); } + + public void setToolbarVisibility(boolean show){ + if (show){ + toolbar.setVisibility(View.VISIBLE); + }else { + toolbar.setVisibility(View.GONE); + } + } } diff --git a/app/src/main/res/drawable-hdpi/ic_arrow_back_primary_24dp.png b/app/src/main/res/drawable-hdpi/ic_arrow_back_primary_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fe508b4505217477e2256b9ef0195ee0eb806dac GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB=6Sj}hE&{od)=3-K|!D`ap3~r z_BR^ucDWpX@jNZp`(P`NQr5qokGl8%Iv=y>s=UdvoW+5G@rmE8hz64U`a{nmYY>eF8biyS{Mbkq5HXTD>_P9~NK>&4afpHmDvbLc0~ Or3{{~elF{r5}E*=ds}A! literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..a3210742d6e9f08c93a03e4b055b6b8e2f8b640f GIT binary patch literal 492 zcmVAt55XVyq#L0FR>CnZcpFkvwo4bAmaVT_hX~uxNgV4#bUqIU|C4L8?OE#e(og6w8 z2~AJ9z;Pt@CEiO)$-#e-cjwQ0_wL@)h@!~&OEM~;{|?jy4L}o+0~&)mAhT$o9>@oM zN6Gd9azPE`Z|DkW}ZOT3F|IEL(q<_ z{RFiofqD$)K_OTR6c8U@63AznQ4M$tIuPHk1W=QlrZ8ojPU(>V%_V>aB;Yzn+HK;$ zkpP;IfU_Jx4OZM|134t1n*+#XZQ^X8F$uWH0dzzBcM?Dy5^#%sCQHx}@%JQvOq@-z z4Kr@n8}Yr9>O(F|drL9kl=$sIS5iQiI4ENau<4!3%#?Nad+Awb + + + + + + + + diff --git a/app/src/main/res/drawable-mdpi/ic_arrow_back_primary_24dp.png b/app/src/main/res/drawable-mdpi/ic_arrow_back_primary_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..07e187a26b892628d89ee51f285ea8ae35a6bccb GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjg`O^sAr-fhA9&6`Wa3qx_uohR6`b>tTF+x)c8*MV_X6AkA&ubki zrL&}*p9X}ag4B>Y%sDBr1*}L%IX(n@lK_43J1HVDl1t*GnA9o|V?d}B-wuR!3dA=B z6bMF|U;^LS6wn~ZG{H4|l_{V@(A5Mz_?}Z&z%{`Qe6=YcM=;g|Q}|A%fB?Z3x8+-C zPkO+2n9>JAfh0Ik58IL|#WR~u7Mf1ZFqgXX8l(HT=M`>CjXm$Tr3B zb*Oi2pJR^VgOEAid=+x}gv|A^FhCXq^bq!6fATDP0iV(ib#w1YW&i*H07*qoM6N<$ Eg8S-w-2eap literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_arrow_back_primary_24dp.png b/app/src/main/res/drawable-xhdpi/ic_arrow_back_primary_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..91d7ddcbf18ba21a21c5403ad10c66fb95febee7 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUtt)4E9Ar-gY-rUXGYQW?6kmrEi zn!^%PBwG}3HnfYVbeT-h*w+8=(BGdbdMf)eqqd1uPGVr@3 z-v3CyrqK}fr|zr_qk+jj^Mr~T{|3(*{f5XI{)WsN?E^OZSRW|;(?8(*N4{b8kM#`Y zALALe{s_Fxv!O?v;UkA)-I0##8jqaYEW8x>iaQ>gJaTH@5u~K|wPPz+(#F;ZU8QNe mJHGDvw@}c5fsy5dy?SC*5cA5GS+9T|VDNPHb6Mw<&;$TQyS->P)_M-M$#e+yGC_aZ$U&H!eeT7~seGO4c{iC*}_agWJ)o0Lx=?GgW z!=&VAcQteeJ`f}``I1d`cB2`Fp?Mii2`B+2@P7p|Ko6J!8(_yc{GI|`AbpAgMc~D- z9t)sw3Iajk!EhdZKtGni8Srknk2fGHTOeq1@_S$alz=$k1N=Y&C66ejM(vuJ?~G3K)_id_*Fb1*WK2nS($D<(!HX=%Qjn4g!xTXGf$!nwWTUE{a_d`7T7a5(`vt$=U+D zg(AQT<(kXrg$l$~o7j|y5R>Ab-4QA6bf1t|vfjqZuvrnu-^pAL=xh-}#fY^BN%oEp zuvcY;cwm3xFu%lPFDavcNZ1KHIksgk>UFk~auRzsY+}TJE!o>DdA1m_8gul(Qqerd z##V~;^JCUkZdq@$W;1NZWWmxgk!W~nYq8Bs;+XrIEfa~lmo|5sCK5F-?M^pLByPO4 zy>2E^_0so&x)X_0`(RP?x`4*Uz9)y!Z>m;KUVY?K0!ly$C;``e11^I(^pCv{-2eap M07*qoM6N<$f*{%P#Q*>R literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_arrow_back_primary_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_back_primary_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ec21c6d594f91f5469217b27e77908e6f7e9ab46 GIT binary patch literal 377 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2V07?waSW-r_4f8@-a`fgEe}N+ zChQQ-=nZNTh`k`WKB&Rt|4evWxY_cq)FeI*21XVE2Qa~+(7?n{ z>dfHvpr&mOqu1k}oeWcsNf!6=nY3Ph{`a(=)&-$C$yWqZn3ju8kenmsP;8;EI_HwZ zvjQ)snMX8PQ^c0D0980&IlQWJj%j1egEg#A*wT3ySk76lI_HkVyMiL7okvcxJ`q!A zpO8JLzVTT>NHEI@Ek8kp(-sVdM|&7HdZgcZcKb#C=XDdkd-bC6EJ-Ki5ov@47} zPO18Fs(h6y=-;x`V;y9{Umv>q{joim{E zh*f97P+&D2kMc9h6_9G5vtT&5n!?vRj)0~jrj?BVRnu*P8=y@^Le5(X@FwVp@cW7r zpbzDdGPC7eK>`T5Qg8kn@UKY>-nm zzd;0gh^vf^07C#j2*0nn9d}GB&SqX%3h<@E=RJ-miXRjK*9-^uNa1Un>yhhN#9498 zAe?i>?)fg~uEvvyuETl{IcYFrT7~wQd)p_6{VA0l;YnbwgE8k73At@M4z?9WI;)(zD??VoS9 zA(<)#>WWpTTk|m?XK`9lwrdlT$-zdk<7vXSFImci2uL1xKy6K6Q?Jz_lopl`)DC&? zDj?ZRa6{O)TEgx_6VH%+JPB;#dCnqME@Pq$FH8@$gBBf>cN=Aghp)bakm{15(oNcj-WCbOI@f zm*9%{qzIrcAT`oH#V)GwAEZXwle7v_Bkjw46r@6$H;QsCTmT6m0VIF~kVpUtAOYlF Ze*qRnf}$J5j9UNz002ovPDHLkV1mQMj!6Ij literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_primary_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_primary_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7bac2fce5a4646fbddf87974fba85fc7b55f460d GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U^Mk~aSW-r_4dwg-a`QbE`j0? zlon3bZOPL(BQQfka6#mL+k++r{VgBMZ|C=)EfQvAU|?oAz`$U@z`z4%#)m&=Xn7?6 zoXbJ)-%`P_&icrfsgL4?61V=Cf0yyeBm4We89Vl~9JusxH?xlXe&YijKVlh=9eK}T zpjBtyQ25cBv8U%gYeLQ+K8D4Qmopwa?$6XA&mv&Yp-?B_@JFHHha9v@HBN7GAQg&WM*iZ{fR-L!hoBB zh5tEI!vj`c27x_G*cqC{w=z0>*vQ8qP_u!Pfkpl;^N0ABPr_52HP|1@IQ^8K<*cEe mWZ7~nwhP6ZFdv)T<38=@elIqv?j0~389ZJ6T-G@yGywoD0er^* literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..47cc04fbef0475eb8cb71a9a5e4f9c8cab23102d GIT binary patch literal 1143 zcmV--1c>{IP)VYihD(q7*-Yld4dA*JN~yRAmLNORo4ZScD2l8DMFo`U{H=reeB<_lC2Ghn| z6HtX5=kEaJ0VWa$c9HXh3ReW=B4@ZeA@#q!OWH%kv+9a~Mr5~B9%4e3$dT9$7X)Ay z)>(Zt$kC3j#O4w4UA9@kC}Nynq%pA=VxI8OW&yH?DYFJ7_gE6+maPI} zh>v%*0m;=O;w-jNK$|3~4+ahMl9E_kZ4@vqNovv{0goiHPS_}5QIb@XK?0g3vCi8l zK=!dVX{SP55^KUn0sE4q3JelZD2erpjRH<2N#z+NAYT&eF$vHsAWdD6-Y)2XZo+zR z!buwiELb!Qy)B7#$;FA-ltBV!B(cu8xD=5vr1<=a-6t29B1#a8Tr~y=s6)gVvvoOo zTawtE0Ro;#V%)U(dB6x_e0@n{Vx@@P8Y4CfC_{X_pJ_~N5fR@N+aJoWAdI}{xW|G$zg2}S zemdlhVxer#9Y=1AJql>z(~j(Rc)Rb~=G@F-ybXqL^NJ=wrO44vroA?7sa)v+4Zgi2 z<)Ij9v2VxE8Lm1p$?|ggk}siOFs=A11!VT&95U}MDRwV3ixE?yc#x7WevXS(%^Aen z&RN25xh^0_4DSw^9c|b}0hv!i%76l=n0$Zekc$E`_k?wg#eWN26c2}($r6HoAU?*z z{i*`~AOC>;eHahZ23-{J=X2TB5Q_imlw}WQvV<@%#*YOp2T=Hz?Cc{)UfO`m0y6NE zDg1E6Ru)Cy6zlnzCFOe_-@D&+0sr2t`SQesm?0;`n$EH4&kw{rVM)vbT7Ax0diD}JNPv2J!jeG6KAmn7V21?if&!*1sN$1=>?Cy1Ff?2`RTrNG zc&9^U@kxNAJL3ovKmrI7KmrI7KmrI7KmrI7KmrI7Kmv&W@DubK=*O$7;Qasq002ov JPDHLkV1hGl{7V1; literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml new file mode 100644 index 0000000000..e6aedc5e6e --- /dev/null +++ b/app/src/main/res/layout/activity_search.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_browse_image.xml b/app/src/main/res/layout/fragment_browse_image.xml new file mode 100644 index 0000000000..4879f31892 --- /dev/null +++ b/app/src/main/res/layout/fragment_browse_image.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_category_images.xml b/app/src/main/res/layout/layout_category_images.xml index 9b3ffee0f3..7cb6dd7575 100644 --- a/app/src/main/res/layout/layout_category_images.xml +++ b/app/src/main/res/layout/layout_category_images.xml @@ -1,6 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ba982e6254..2e2aecd02f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -282,5 +282,9 @@ Share App Coordinates were not specified during image selection Error fetching nearby places. + Search + Search Commons + No Images matching %1$s found + Search From 5bb1d195d3949ba619c298fa4c3f1788a64c0491 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 12:39:34 +0530 Subject: [PATCH 02/94] Removed explore icon xml --- .../res/drawable-ldpi/ic_explore_24dp.xml | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 app/src/main/res/drawable-ldpi/ic_explore_24dp.xml diff --git a/app/src/main/res/drawable-ldpi/ic_explore_24dp.xml b/app/src/main/res/drawable-ldpi/ic_explore_24dp.xml deleted file mode 100644 index 6b0553ff1a..0000000000 --- a/app/src/main/res/drawable-ldpi/ic_explore_24dp.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - From 2aac404f7ff3ab5221463c6ad7b5d319fde8ce1b Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 13:26:32 +0530 Subject: [PATCH 03/94] Updated Javadocs for search Activity and Navigation Base Activity --- .../nrw/commons/explore/SearchActivity.java | 18 ++++++++++-------- .../explore/images/SearchImageFragment.java | 9 ++++----- .../explore/images/SearchImageItem.java | 13 +------------ .../explore/images/SearchImagesRenderer.java | 4 ++++ .../commons/theme/NavigationBaseActivity.java | 6 +++++- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 0fd58474be..3101bc20fe 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -19,7 +19,6 @@ import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.explore.images.SearchImageFragment; -import fr.free.nrw.commons.explore.images.SearchImageItem; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.ViewUtil; @@ -37,7 +36,6 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai private SearchImageFragment searchImageFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; - SearchImageItem searchImageItem; private String query; @Override @@ -98,11 +96,14 @@ public void unregisterDataSetObserver(DataSetObserver observer) { } - public void onSearchImageClicked(SearchImageItem searchImageItem, int index) { - this.searchImageItem = searchImageItem; + /** + * Open media detail pager fragment on click of image in search results + * @param index item index that should be opened + */ + public void onSearchImageClicked(int index) { ViewUtil.hideKeyboard(this.findViewById(R.id.searchBox)); toolbar.setVisibility(View.GONE); - setToolbarVisibility(true); + setNavigationBaseToolbarVisibility(true); if (mediaDetails == null || !mediaDetails.isVisible()) { // set isFeaturedImage true for featured images, to include author field on media detail mediaDetails = new MediaDetailPagerFragment(false, true); @@ -120,14 +121,15 @@ public void onSearchImageClicked(SearchImageItem searchImageItem, int index) { @Override public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount()==1){ + // back to search so show search toolbar and hide navigation toolbar + toolbar.setVisibility(View.VISIBLE); + setNavigationBaseToolbarVisibility(false); if (!TextUtils.isEmpty(query)) { searchImageFragment.updateImageList(query); } - toolbar.setVisibility(View.VISIBLE); - setToolbarVisibility(false); }else { toolbar.setVisibility(View.GONE); - setToolbarVisibility(true); + setNavigationBaseToolbarVisibility(true); } super.onBackPressed(); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 8f263645c7..133fe8bd3c 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -57,13 +57,12 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { int index = queryList.indexOf(item); - ((SearchActivity)getContext()).onSearchImageClicked(item,index); -// Add images to recently searched images db table and open to Media Details Fragment + ((SearchActivity)getContext()).onSearchImageClicked(index); + //TODO : Add images to recently searched images db table }); @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); ButterKnife.bind(this, rootView); @@ -118,7 +117,7 @@ public void updateImageList(String query) { private Observable searchImages(String query) { return mwApi.searchImages(query, SEARCH_IMAGES_LIMIT) - .map(s -> new SearchImageItem(s,false)); + .map(s -> new SearchImageItem(s)); } public int getTotalImagesCount(){ diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java index 26b1dd9c1a..12e76cbaae 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java @@ -5,7 +5,6 @@ public class SearchImageItem implements Parcelable { private final String name; - private boolean image_stored_in_history; public static Creator CREATOR = new Creator() { @Override @@ -19,27 +18,18 @@ public SearchImageItem[] newArray(int i) { } }; - public SearchImageItem(String name, boolean image_stored_in_history) { + public SearchImageItem(String name) { this.name = name; - this.image_stored_in_history = image_stored_in_history; } private SearchImageItem(Parcel in) { name = in.readString(); - image_stored_in_history = in.readInt() == 1; } public String getName() { return name; } - public boolean isImage_stored_in_history() { - return image_stored_in_history; - } - - public void setImage_stored_in_history(boolean image_stored_in_history) { - this.image_stored_in_history = image_stored_in_history; - } @Override public int describeContents() { @@ -49,7 +39,6 @@ public int describeContents() { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(name); - parcel.writeInt(image_stored_in_history ? 1 : 0); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java index f4390baea4..b45a3711a1 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java @@ -13,6 +13,10 @@ import fr.free.nrw.commons.MediaWikiImageView; import fr.free.nrw.commons.R; +/** + * presentation logic of individual image in search is handled here + */ + class SearchImagesRenderer extends Renderer { @BindView(R.id.categoryImageTitle) TextView tvImageName; @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index 24eff4d03f..ff525a2649 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -187,7 +187,11 @@ public static void startActivityWithFlags(Context context, Class cls, int context.startActivity(intent); } - public void setToolbarVisibility(boolean show){ + /** + * Handles visibility of navigation base toolbar + * @param show : Used to handle visibility of toolbar + */ + public void setNavigationBaseToolbarVisibility(boolean show){ if (show){ toolbar.setVisibility(View.VISIBLE); }else { From 021905ab87fc031b92b802d4355f21a4096c977c Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 13:45:36 +0530 Subject: [PATCH 04/94] SearchImageItem class updated --- .../explore/images/SearchImageFragment.java | 3 +- .../explore/images/SearchImageItem.java | 52 +------------------ 2 files changed, 2 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 133fe8bd3c..ea8341b1b4 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -116,8 +116,7 @@ public void updateImageList(String query) { } private Observable searchImages(String query) { - return mwApi.searchImages(query, SEARCH_IMAGES_LIMIT) - .map(s -> new SearchImageItem(s)); + return mwApi.searchImages(query, SEARCH_IMAGES_LIMIT).map(s -> new SearchImageItem(s)); } public int getTotalImagesCount(){ diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java index 12e76cbaae..b14d9fded0 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java @@ -1,63 +1,13 @@ package fr.free.nrw.commons.explore.images; -import android.os.Parcel; -import android.os.Parcelable; - -public class SearchImageItem implements Parcelable { +public class SearchImageItem { private final String name; - public static Creator CREATOR = new Creator() { - @Override - public SearchImageItem createFromParcel(Parcel parcel) { - return new SearchImageItem(parcel); - } - - @Override - public SearchImageItem[] newArray(int i) { - return new SearchImageItem[0]; - } - }; - public SearchImageItem(String name) { this.name = name; } - private SearchImageItem(Parcel in) { - name = in.readString(); - } - public String getName() { return name; } - - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(name); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - SearchImageItem that = (SearchImageItem) o; - - return name.equals(that.name); - - } - - @Override - public int hashCode() { - return name.hashCode(); - } } From 4422329376e135ef035697bcb7a597a8207d4057 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 15:55:48 +0530 Subject: [PATCH 05/94] Javadocs added for search activity --- .../fr/free/nrw/commons/category/CategoryImagesActivity.java | 1 - .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 2 +- .../fr/free/nrw/commons/explore/images/SearchImageFragment.java | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index aa7f00479f..2cb15534c8 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -19,7 +19,6 @@ import fr.free.nrw.commons.explore.SearchActivity; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; -import timber.log.Timber; /** * This activity displays pictures of a particular category diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 3101bc20fe..f650223ccc 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -120,7 +120,7 @@ public void onSearchImageClicked(int index) { @Override public void onBackPressed() { - if (getSupportFragmentManager().getBackStackEntryCount()==1){ + if (getSupportFragmentManager().getBackStackEntryCount() == 1){ // back to search so show search toolbar and hide navigation toolbar toolbar.setVisibility(View.VISIBLE); setNavigationBaseToolbarVisibility(false); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index ea8341b1b4..c923ee5447 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -33,7 +33,7 @@ import timber.log.Timber; /** - * Displays the image search screen. + * Displays the image search screen. It has a recyclerview */ public class SearchImageFragment extends CommonsDaggerSupportFragment { From 092d2a30729a2c1184f859b29ae734d8f0d09810 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 18:07:22 +0530 Subject: [PATCH 06/94] removed redundant code and added javadocs for search image modules --- .../explore/images/SearchImageFragment.java | 131 ++++++++++-------- .../explore/images/SearchImageItem.java | 13 -- .../images/SearchImagesAdapterFactory.java | 12 +- .../explore/images/SearchImagesRenderer.java | 19 +-- .../mwapi/ApacheHttpClientMediaWikiApi.java | 56 ++++---- .../free/nrw/commons/mwapi/MediaWikiApi.java | 2 +- .../res/layout/layout_category_images.xml | 1 + 7 files changed, 126 insertions(+), 108 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index c923ee5447..251bca3d52 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -5,21 +5,17 @@ import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; - import com.pedrogomez.renderers.RVRendererAdapter; - import java.util.ArrayList; import java.util.List; - +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; - import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.Media; @@ -27,33 +23,38 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.NetworkUtils; +import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + /** * Displays the image search screen. It has a recyclerview */ public class SearchImageFragment extends CommonsDaggerSupportFragment { - public static final int SEARCH_IMAGES_LIMIT = 25; + private static int TIMEOUT_SECONDS = 15; @BindView(R.id.imagesListBox) - RecyclerView imagesList; + RecyclerView imagesRecyclerView; @BindView(R.id.imageSearchInProgress) - ProgressBar imageSearchInProgress; + ProgressBar progressBar; @BindView(R.id.imagesNotFound) TextView imagesNotFoundView; + String query; @Inject MediaWikiApi mwApi; @Inject @Named("default_preferences") SharedPreferences prefs; - private RVRendererAdapter imagesAdapter; - private List images=new ArrayList<>(); - private List queryList = new ArrayList<>(); + private RVRendererAdapter imagesAdapter; + private List queryList = new ArrayList<>(); private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { int index = queryList.indexOf(item); @@ -65,60 +66,76 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); ButterKnife.bind(this, rootView); - - imagesList.setLayoutManager(new LinearLayoutManager(getContext())); - - ArrayList items = new ArrayList<>(); - + imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + ArrayList items = new ArrayList<>(); imagesAdapter = adapterFactory.create(items); - imagesList.setAdapter(imagesAdapter); - + imagesRecyclerView.setAdapter(imagesAdapter); return rootView; } + public void updateImageList(String query) { + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + progressBar.setVisibility(View.VISIBLE); queryList.clear(); - Observable.fromIterable(images) + imagesAdapter.clear(); + Observable.fromCallable(() -> mwApi.searchImages(query)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .doOnSubscribe(disposable -> { - imageSearchInProgress.setVisibility(View.VISIBLE); - imagesNotFoundView.setVisibility(View.GONE); - imagesAdapter.clear(); - }) - .observeOn(Schedulers.io()) - .concatWith( - searchImages(query) - ) - .distinct() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - s ->{ - imagesAdapter.add(s); - queryList.add(s); - }, - Timber::e, - () -> { - imagesAdapter.notifyDataSetChanged(); - imageSearchInProgress.setVisibility(View.GONE); - - if (imagesAdapter.getItemCount() == images.size()) { - if (TextUtils.isEmpty(query)) { - imagesAdapter.clear(); - - } else { - imagesNotFoundView.setText(getString(R.string.images_not_found, query)); - imagesNotFoundView.setVisibility(View.VISIBLE); - } - } - } - ); + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + /** + * Handles the success scenario + * it initializes the recycler view by adding items to the adapter + * @param mediaList + */ + private void handleSuccess(List mediaList) { + queryList = mediaList; + if(mediaList == null || mediaList.isEmpty()) { + initErrorView(); + }else { + + progressBar.setVisibility(View.GONE); + imagesAdapter.addAll(mediaList); + imagesAdapter.notifyDataSetChanged(); + } + } + + /** + * Logs and handles API error scenario + * @param throwable + */ + private void handleError(Throwable throwable) { + Timber.e(throwable, "Error occurred while loading queried images"); + initErrorView(); + } + + /** + * Handles the UI updates for a error scenario + */ + private void initErrorView() { + ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); + progressBar.setVisibility(GONE); + imagesNotFoundView.setVisibility(VISIBLE); + imagesNotFoundView.setText(getString(R.string.images_not_found, query)); } - private Observable searchImages(String query) { - return mwApi.searchImages(query, SEARCH_IMAGES_LIMIT).map(s -> new SearchImageItem(s)); + /** + * Handles the UI updates for no internet scenario + */ + private void handleNoInternet() { + progressBar.setVisibility(GONE); + ViewUtil.showSnackbar(imagesRecyclerView, R.string.no_internet); } + /** + * returns total number of images present in the recyclerview adapter. + */ public int getTotalImagesCount(){ if (imagesAdapter == null) { return 0; @@ -127,12 +144,16 @@ public int getTotalImagesCount(){ } } + /** + * returns Media Object at position + * @param i position of Media in the recyclerview adapter. + */ public Media getImageAtPosition(int i) { - if (imagesAdapter.getItem(i).getName() == null) { + if (imagesAdapter.getItem(i).getFilename() == null) { // not yet ready to return data return null; } else { - return new Media(imagesAdapter.getItem(i).getName()); + return new Media(imagesAdapter.getItem(i).getFilename()); } } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java deleted file mode 100644 index b14d9fded0..0000000000 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageItem.java +++ /dev/null @@ -1,13 +0,0 @@ -package fr.free.nrw.commons.explore.images; - -public class SearchImageItem { - private final String name; - - public SearchImageItem(String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java index 2d9e2a1e74..b095669952 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java @@ -7,6 +7,8 @@ import java.util.Collections; import java.util.List; +import fr.free.nrw.commons.Media; + class SearchImagesAdapterFactory { private final SearchImagesRenderer.ImageClickedListener listener; @@ -15,11 +17,11 @@ class SearchImagesAdapterFactory { this.listener = listener; } - public RVRendererAdapter create(List searchImageItemList) { - RendererBuilder builder = new RendererBuilder() - .bind(SearchImageItem.class, new SearchImagesRenderer(listener)); - ListAdapteeCollection collection = new ListAdapteeCollection<>( - searchImageItemList != null ? searchImageItemList : Collections.emptyList()); + public RVRendererAdapter create(List searchImageItemList) { + RendererBuilder builder = new RendererBuilder() + .bind(Media.class, new SearchImagesRenderer(listener)); + ListAdapteeCollection collection = new ListAdapteeCollection<>( + searchImageItemList != null ? searchImageItemList : Collections.emptyList()); return new RVRendererAdapter<>(builder, collection); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java index b45a3711a1..aa9db68779 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java @@ -17,7 +17,7 @@ * presentation logic of individual image in search is handled here */ -class SearchImagesRenderer extends Renderer { +class SearchImagesRenderer extends Renderer { @BindView(R.id.categoryImageTitle) TextView tvImageName; @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; @BindView(R.id.categoryImageView) @@ -42,7 +42,7 @@ protected void setUpView(View view) { @Override protected void hookListeners(View view) { view.setOnClickListener(v -> { - SearchImageItem item = getContent(); + Media item = getContent(); if (listener != null) { listener.imageClicked(item); } @@ -51,19 +51,22 @@ protected void hookListeners(View view) { @Override public void render() { - SearchImageItem item = getContent(); - Media media = new Media(item.getName()); - tvImageName.setText(item.getName()); - browseImage.setMedia(media); - setAuthorView(media, categoryImageAuthor); + Media item = getContent(); + tvImageName.setText(item.getFilename()); + browseImage.setMedia(item); + setAuthorView(item, categoryImageAuthor); } interface ImageClickedListener { - void imageClicked(SearchImageItem item); + void imageClicked(Media item); } + /** + * formats author name as "Uploaded by: authorName" and sets it in textview + */ private void setAuthorView(Media item, TextView author) { if (item.getCreator() != null && !item.getCreator().equals("")) { + author.setVisibility(View.GONE); String uploadedByTemplate = getContext().getString(R.string.image_uploaded_by); author.setText(String.format(uploadedByTemplate, item.getCreator())); } else { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 33a2599621..510b5acce3 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -514,37 +514,41 @@ public List getCategoryImages(String categoryName) { return CategoryImageUtils.getMediaList(childNodes); } + /** + * This method takes search keyword as input and returns a list of Media objects filtered using search query + * It uses the generator query API to get the images searched using a query, 25 at a time. + * @param query keyword to search images on commons + * @return + */ @Override @NonNull - public Observable searchImages(String filterValue, int searchImagesLimit) { - return Single.fromCallable(() -> { - List imageNodes = null; - try { - imageNodes = api.action("query") - .param("format", "xml") - .param("list", "search") - .param("srwhat", "text") - .param("srnamespace", "6") - .param("srlimit", searchImagesLimit) - .param("srsearch", filterValue) - .get() - .getNodes("/api/query/search/p/@title"); - } catch (IOException e) { - Timber.e("Failed to obtain searchImages", e); - } + public List searchImages(String query) { + List imageNodes = null; + try { + imageNodes = api.action("query") + .param("format", "xml") + .param("list", "search") + .param("srwhat", "text") + .param("srnamespace", "6") + .param("srlimit", "25") + .param("srsearch", query) + .get() + .getNodes("/api/query/search/p/@title"); + } catch (IOException e) { + Timber.e("Failed to obtain searchImages", e); + } - if (imageNodes == null) { - return new ArrayList(); - } + if (imageNodes == null) { + return new ArrayList(); + } - List categories = new ArrayList<>(); - for (ApiResult imageNode : imageNodes) { - String imgName = imageNode.getDocument().getTextContent(); - categories.add(imgName); - } + List images = new ArrayList<>(); + for (ApiResult imageNode : imageNodes) { + String imgName = imageNode.getDocument().getTextContent(); + images.add(new Media(imgName)); + } - return categories; - }).flatMapObservable(Observable::fromIterable); + return images; } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index 9a8dcb7214..bbd680e106 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -38,7 +38,7 @@ public interface MediaWikiApi { List getCategoryImages(String categoryName); @NonNull - Observable searchImages(String title, int searchImagesLimit); + List searchImages(String title); @NonNull UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener) throws IOException; diff --git a/app/src/main/res/layout/layout_category_images.xml b/app/src/main/res/layout/layout_category_images.xml index 7cb6dd7575..9b3ffee0f3 100644 --- a/app/src/main/res/layout/layout_category_images.xml +++ b/app/src/main/res/layout/layout_category_images.xml @@ -1,5 +1,6 @@ Date: Mon, 28 May 2018 18:11:52 +0530 Subject: [PATCH 07/94] Javadocs added for updateImageList method --- .../nrw/commons/explore/images/SearchImageFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 251bca3d52..5cb14a5dd7 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -34,7 +34,7 @@ import static android.view.View.VISIBLE; /** - * Displays the image search screen. It has a recyclerview + * Displays the image search screen. */ public class SearchImageFragment extends CommonsDaggerSupportFragment { @@ -73,7 +73,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav return rootView; } - + /** + * Checks for internet connection and then initializes the recycler view with 25 images of the searched query + * Clearing imageAdapter every time new keyword is searched so that user can see only new results + */ public void updateImageList(String query) { if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { handleNoInternet(); From 98b3d69d616aede820a91c571958816a6c2d1f98 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 30 May 2018 10:31:55 +0530 Subject: [PATCH 08/94] Rename featuredImage to explore --- .../commons/theme/NavigationBaseActivity.java | 4 +-- .../res/drawable-ldpi/ic_explore_24dp.xml | 27 +++++++++++++++++++ .../main/res/drawable/ic_star_black_24dp.xml | 9 ------- app/src/main/res/menu/drawer.xml | 6 ++--- app/src/main/res/values/strings.xml | 4 +-- 5 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 app/src/main/res/drawable-ldpi/ic_explore_24dp.xml delete mode 100644 app/src/main/res/drawable/ic_star_black_24dp.xml diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index ff525a2649..38ff8c1898 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -156,9 +156,9 @@ public boolean onNavigationItemSelected(@NonNull final MenuItem item) { drawerLayout.closeDrawer(navigationView); NotificationActivity.startYourself(this); return true; - case R.id.action_featured_images: + case R.id.action_explore: drawerLayout.closeDrawer(navigationView); - CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_featured_images), FEATURED_IMAGES_CATEGORY); + CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_explore), FEATURED_IMAGES_CATEGORY); return true; default: Timber.e("Unknown option [%s] selected from the navigation menu", itemId); diff --git a/app/src/main/res/drawable-ldpi/ic_explore_24dp.xml b/app/src/main/res/drawable-ldpi/ic_explore_24dp.xml new file mode 100644 index 0000000000..6b0553ff1a --- /dev/null +++ b/app/src/main/res/drawable-ldpi/ic_explore_24dp.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_star_black_24dp.xml b/app/src/main/res/drawable/ic_star_black_24dp.xml deleted file mode 100644 index a87ca098da..0000000000 --- a/app/src/main/res/drawable/ic_star_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml index ae6e0cce2e..ceb8028670 100644 --- a/app/src/main/res/menu/drawer.xml +++ b/app/src/main/res/menu/drawer.xml @@ -11,9 +11,9 @@ android:title="@string/navigation_item_nearby" /> + android:id="@+id/action_explore" + android:icon="@drawable/ic_explore_24dp" + android:title="@string/navigation_item_explore"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2e2aecd02f..4cff8e275e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,7 +81,7 @@ Categories Settings Sign Up - Featured Images + Explore About The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. Wikimedia Commons @@ -217,7 +217,7 @@ Logout Tutorial Notifications - Featured + Explore Nearby places cannot be displayed without location permissions no description found Commons file page From dd4f42f90d83a7937882f34275cb16e5632b057c Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Fri, 1 Jun 2018 17:13:44 +0530 Subject: [PATCH 09/94] Fixed null query issue --- .../fr/free/nrw/commons/explore/images/SearchImageFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 5cb14a5dd7..28c94e0991 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -78,6 +78,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav * Clearing imageAdapter every time new keyword is searched so that user can see only new results */ public void updateImageList(String query) { + this.query = query; if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { handleNoInternet(); return; From b3816f1f378d5206b0c51a5bed16128946e29256 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Fri, 1 Jun 2018 17:01:10 +0530 Subject: [PATCH 10/94] changed cisibility to gone in case of successful Fetch --- .../fr/free/nrw/commons/explore/images/SearchImageFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 28c94e0991..8e5d486e74 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -99,6 +99,7 @@ public void updateImageList(String query) { * @param mediaList */ private void handleSuccess(List mediaList) { + imagesNotFoundView.setVisibility(GONE); queryList = mediaList; if(mediaList == null || mediaList.isEmpty()) { initErrorView(); From fb51ae7721689bfce71224a4df4b19d77a8224f5 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sat, 9 Dec 2017 23:01:47 -0600 Subject: [PATCH 11/94] Consolidate the networking libraries - drop volley in favor of OkHttp --- app/build.gradle | 1 - app/proguard-rules.txt | 2 +- .../nrw/commons/caching/CacheController.java | 6 +- .../category/CategorizationFragment.java | 6 +- .../commons/di/CommonsApplicationModule.java | 21 +- .../free/nrw/commons/upload/CategoryApi.java | 165 ++++++++++++ .../free/nrw/commons/upload/MwVolleyApi.java | 249 ------------------ .../nrw/commons/upload/ShareActivity.java | 6 +- 8 files changed, 194 insertions(+), 262 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java diff --git a/app/build.gradle b/app/build.gradle index 7faf3661df..fad3eccd8c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,6 @@ dependencies { implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'in.yuvi:http.fluent:1.3' implementation 'com.github.chrisbanes:PhotoView:2.0.0' - implementation 'com.android.volley:volley:1.0.0' implementation 'ch.acra:acra:4.9.2' implementation 'org.mediawiki:api:1.3' implementation 'commons-codec:commons-codec:1.10' diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index bbf3a3f0d2..f3e00b3f58 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -1,5 +1,5 @@ -dontobfuscate -keep class org.apache.http.** { *; } -dontwarn org.apache.http.** --keep class fr.free.nrw.commons.upload.MwVolleyApi$Page {*;} +-keep class fr.free.nrw.commons.upload.CategoryApi$Page {*;} -keep class android.support.v7.widget.ShareActionProvider { *; } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java index ff6ceece40..1b48c3c50b 100644 --- a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java +++ b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java @@ -7,7 +7,7 @@ import java.util.Arrays; import java.util.List; -import fr.free.nrw.commons.upload.MwVolleyApi; +import fr.free.nrw.commons.upload.CategoryApi; import timber.log.Timber; public class CacheController { @@ -31,8 +31,8 @@ public void setQtPoint(double decLongitude, double decLatitude) { public void cacheCategory() { List pointCatList = new ArrayList<>(); - if (MwVolleyApi.GpsCatExists.getGpsCatExists()) { - pointCatList.addAll(MwVolleyApi.getGpsCat()); + if (CategoryApi.GpsCatExists.getGpsCatExists()) { + pointCatList.addAll(CategoryApi.getGpsCat()); Timber.d("Categories being cached: %s", pointCatList); } else { Timber.d("No categories found, so no categories cached"); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java index e804189ab6..514b2fb5fd 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java @@ -39,7 +39,7 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; -import fr.free.nrw.commons.upload.MwVolleyApi; +import fr.free.nrw.commons.upload.CategoryApi; import fr.free.nrw.commons.utils.StringSortingUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; @@ -288,8 +288,8 @@ private Observable directCategories() { private Observable gpsCategories() { return Observable.fromIterable( - MwVolleyApi.GpsCatExists.getGpsCatExists() - ? MwVolleyApi.getGpsCat() : new ArrayList<>()) + CategoryApi.GpsCatExists.getGpsCatExists() + ? CategoryApi.getGpsCat() : new ArrayList<>()) .map(name -> new CategoryItem(name, false)); } diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 55281be7e3..3d55169350 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -4,9 +4,11 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.support.v4.util.LruCache; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import javax.inject.Named; import javax.inject.Singleton; @@ -14,7 +16,6 @@ import dagger.Module; import dagger.Provides; import fr.free.nrw.commons.BuildConfig; -import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.caching.CacheController; @@ -24,6 +25,8 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; import fr.free.nrw.commons.upload.UploadController; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; @@ -69,6 +72,12 @@ public ContentProviderClient provideModificationContentProviderClient(Context co return context.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY); } + @Provides + @Singleton + public OkHttpClient provideOkHttpClient() { + return new OkHttpClient.Builder().build(); + } + @Provides @Named("application_preferences") public SharedPreferences providesApplicationSharedPreferences(Context context) { @@ -126,6 +135,14 @@ public MediaWikiApi provideMediaWikiApi(Context context, return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); } + @Provides + @Named("commons_mediawiki_url") + @NonNull + @SuppressWarnings("ConstantConditions") + public HttpUrl provideMwUrl() { + return HttpUrl.parse("https://commons.wikimedia.org/"); + } + @Provides @Singleton public LocationServiceManager provideLocationServiceManager(Context context) { @@ -139,7 +156,7 @@ public LocationServiceManager provideLocationServiceManager(Context context) { @Provides @Singleton public Gson provideGson() { - return new Gson(); + return new GsonBuilder().create(); } @Provides diff --git a/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java b/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java new file mode 100644 index 0000000000..8a7dfcc6e8 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java @@ -0,0 +1,165 @@ +package fr.free.nrw.commons.upload; + +import android.support.annotation.NonNull; + +import com.google.gson.Gson; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.ResponseBody; +import timber.log.Timber; + +/** + * Uses the OkHttp library to implement asynchronous calls to the Commons MediaWiki API to match + * GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list + * of relevant categories. + */ +public class CategoryApi { + + private static Set categorySet; + private static List categoryList; + private final OkHttpClient okHttpClient; + private final HttpUrl mwUrl; + private final Gson gson; + + @Inject + public CategoryApi(OkHttpClient okHttpClient, @Named("commons_mediawiki_url") HttpUrl mwUrl, Gson gson) { + this.okHttpClient = okHttpClient; + this.mwUrl = mwUrl; + this.gson = gson; + categorySet = new HashSet<>(); + } + + public static List getGpsCat() { + return categoryList; + } + + public static void setGpsCat(List cachedList) { + categoryList = new ArrayList<>(); + categoryList.addAll(cachedList); + Timber.d("Setting GPS cats from cache: %s", categoryList); + } + + public void request(String coords) { + String apiUrl = buildUrl(coords); + Timber.d("URL: %s", apiUrl); + + Call call = okHttpClient.newCall(new Request.Builder().get().url(apiUrl).build()); + call.enqueue(new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + Timber.e(e); + GpsCatExists.setGpsCatExists(false); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull okhttp3.Response response) { + categoryList = new ArrayList<>(); + categorySet = new HashSet<>(); + ResponseBody body = response.body(); + if (body == null) { + return; + } + QueryResponse queryResponse = gson.fromJson(body.charStream(), QueryResponse.class); + if (queryResponse != null && queryResponse.query != null && queryResponse.query.pages != null) { + for (Page page : queryResponse.query.pages) { + if (page.categories != null) { + for (Category category : page.categories) { + String categoryString = category.title.replace("Category:", ""); + categorySet.add(categoryString); + } + categoryList = new ArrayList<>(categorySet); + } + } + } + GpsCatExists.setGpsCatExists(!categorySet.isEmpty()); + } + }); + } + + /** + * Builds URL with image coords for MediaWiki API calls + * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 + * + * @param coords Coordinates to build query with + * @return URL for API query + */ + private String buildUrl(String coords) { + return mwUrl.newBuilder() + .addPathSegment("w") + .addPathSegment("api.php") + .addQueryParameter("action", "query") + .addQueryParameter("prop", "categories|coordinates|pageprops") + .addQueryParameter("format", "json") + .addQueryParameter("clshow", "!hidden") + .addQueryParameter("coprop", "type|name|dim|country|region|globe") + .addQueryParameter("codistancefrompoint", coords) + .addQueryParameter("generator", "geosearch") + .addQueryParameter("ggscoord", coords) + .addQueryParameter("ggsradius", "10000") + .addQueryParameter("ggslimit", "10") + .addQueryParameter("ggsnamespace", "6") + .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") + .addQueryParameter("ggsprimary", "all") + .addQueryParameter("formatversion", "2") + .build().toString(); + } + + public static class GpsCatExists { + private static boolean gpsCatExists; + + public static void setGpsCatExists(boolean gpsCat) { + gpsCatExists = gpsCat; + } + + public static boolean getGpsCatExists() { + return gpsCatExists; + } + } + + private static class QueryResponse { + public Query query; + + public QueryResponse() { + } + } + + private static class Query { + public Page[] pages; + + public Query() { + pages = new Page[0]; + } + } + + private static class Page { + public String title; + public Category[] categories; + public Category category; + + public Page() { + } + } + + private static class Category { + public String title; + + public Category() { + } + } +} + + + diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java b/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java deleted file mode 100644 index a530e79e6c..0000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java +++ /dev/null @@ -1,249 +0,0 @@ -package fr.free.nrw.commons.upload; - -import android.content.Context; -import android.net.Uri; - -import com.android.volley.Cache; -import com.android.volley.NetworkResponse; -import com.android.volley.Request; -import com.android.volley.RequestQueue; -import com.android.volley.Response; -import com.android.volley.VolleyError; -import com.android.volley.toolbox.HttpHeaderParser; -import com.android.volley.toolbox.JsonRequest; -import com.android.volley.toolbox.Volley; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import timber.log.Timber; - -/** - * Uses the Volley library to implement asynchronous calls to the Commons MediaWiki API to match - * GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list - * of relevant categories. - */ -public class MwVolleyApi { - - private static RequestQueue REQUEST_QUEUE; - private static final Gson GSON = new GsonBuilder().create(); - - private static Set categorySet; - private static List categoryList; - - private static final String MWURL = "https://commons.wikimedia.org/"; - private final Context context; - - public MwVolleyApi(Context context) { - this.context = context; - categorySet = new HashSet<>(); - } - - public static List getGpsCat() { - return categoryList; - } - - public static void setGpsCat(List cachedList) { - categoryList = new ArrayList<>(); - categoryList.addAll(cachedList); - Timber.d("Setting GPS cats from cache: %s", categoryList); - } - - public void request(String coords) { - String apiUrl = buildUrl(coords); - Timber.d("URL: %s", apiUrl); - - JsonRequest request = new QueryRequest(apiUrl, - new LogResponseListener<>(), new LogResponseErrorListener()); - getQueue().add(request); - } - - /** - * Builds URL with image coords for MediaWiki API calls - * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 - * @param coords Coordinates to build query with - * @return URL for API query - */ - private String buildUrl(String coords) { - - Uri.Builder builder = Uri.parse(MWURL).buildUpon(); - - builder.appendPath("w") - .appendPath("api.php") - .appendQueryParameter("action", "query") - .appendQueryParameter("prop", "categories|coordinates|pageprops") - .appendQueryParameter("format", "json") - .appendQueryParameter("clshow", "!hidden") - .appendQueryParameter("coprop", "type|name|dim|country|region|globe") - .appendQueryParameter("codistancefrompoint", coords) - .appendQueryParameter("generator", "geosearch") - .appendQueryParameter("ggscoord", coords) - .appendQueryParameter("ggsradius", "10000") - .appendQueryParameter("ggslimit", "10") - .appendQueryParameter("ggsnamespace", "6") - .appendQueryParameter("ggsprop", "type|name|dim|country|region|globe") - .appendQueryParameter("ggsprimary", "all") - .appendQueryParameter("formatversion", "2"); - - return builder.toString(); - } - - private synchronized RequestQueue getQueue() { - if (REQUEST_QUEUE == null) { - REQUEST_QUEUE = Volley.newRequestQueue(context); - } - return REQUEST_QUEUE; - } - - private static class LogResponseListener implements Response.Listener { - - @Override - public void onResponse(T response) { - Timber.d(response.toString()); - } - } - - private static class LogResponseErrorListener implements Response.ErrorListener { - - @Override - public void onErrorResponse(VolleyError error) { - Timber.e(error.toString()); - } - } - - private static class QueryRequest extends JsonRequest { - - public QueryRequest(String url, - Response.Listener listener, - Response.ErrorListener errorListener) { - super(Request.Method.GET, url, null, listener, errorListener); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - String json = parseString(response); - QueryResponse queryResponse = GSON.fromJson(json, QueryResponse.class); - return Response.success(queryResponse, cacheEntry(response)); - } - - private Cache.Entry cacheEntry(NetworkResponse response) { - return HttpHeaderParser.parseCacheHeaders(response); - } - - private String parseString(NetworkResponse response) { - try { - return new String(response.data, HttpHeaderParser.parseCharset(response.headers)); - } catch (UnsupportedEncodingException e) { - return new String(response.data); - } - } - } - - public static class GpsCatExists { - private static boolean gpsCatExists; - - public static void setGpsCatExists(boolean gpsCat) { - gpsCatExists = gpsCat; - } - - public static boolean getGpsCatExists() { - return gpsCatExists; - } - } - - private static class QueryResponse { - private Query query = new Query(); - - private String printSet() { - if (categorySet == null || categorySet.isEmpty()) { - GpsCatExists.setGpsCatExists(false); - Timber.d("gpsCatExists=%b", GpsCatExists.getGpsCatExists()); - return "No collection of categories"; - } else { - GpsCatExists.setGpsCatExists(true); - Timber.d("gpsCatExists=%b", GpsCatExists.getGpsCatExists()); - return "CATEGORIES FOUND" + categorySet.toString(); - } - - } - - @Override - public String toString() { - if (query != null) { - return "query=" + query.toString() + "\n" + printSet(); - } else { - return "No pages found"; - } - } - } - - private static class Query { - private Page [] pages; - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("pages=" + "\n"); - if (pages != null) { - for (Page page : pages) { - builder.append(page.toString()); - builder.append("\n"); - } - builder.replace(builder.length() - 1, builder.length(), ""); - return builder.toString(); - } else { - return "No pages found"; - } - } - } - - public static class Page { - private int pageid; - private int ns; - private String title; - private Category[] categories; - private Category category; - - public Page() { - } - - @Override - public String toString() { - - StringBuilder builder = new StringBuilder("PAGEID=" + pageid + " ns=" + ns + " title=" + title + "\n" + " CATEGORIES= "); - - if (categories == null || categories.length == 0) { - builder.append("no categories exist\n"); - } else { - for (Category category : categories) { - builder.append(category.toString()); - builder.append("\n"); - if (category != null) { - String categoryString = category.toString().replace("Category:", ""); - categorySet.add(categoryString); - } - } - } - - categoryList = new ArrayList<>(categorySet); - builder.replace(builder.length() - 1, builder.length(), ""); - return builder.toString(); - } - } - - private static class Category { - private String title; - - @Override - public String toString() { - return title; - } - } -} - - - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index cfcec1da5d..e3188534cd 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -123,6 +123,8 @@ public class ShareActivity @Inject ModifierSequenceDao modifierSequenceDao; @Inject + CategoryApi apiCall; + @Inject @Named("default_preferences") SharedPreferences prefs; @@ -699,8 +701,6 @@ public void useImageCoords() { cacheController.setQtPoint(decLongitude, decLatitude); } - MwVolleyApi apiCall = new MwVolleyApi(this); - List displayCatList = cacheController.findCategory(); boolean catListEmpty = displayCatList.isEmpty(); @@ -712,7 +712,7 @@ public void useImageCoords() { } else { cacheFound = true; Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); - MwVolleyApi.setGpsCat(displayCatList); + CategoryApi.setGpsCat(displayCatList); } }else{ Timber.d("EXIF: no coords"); From 417fdc0bc8865b9bec16c16e7a76b2df19f39c45 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sun, 8 Apr 2018 15:55:58 -0500 Subject: [PATCH 12/94] Extracted a few networking related items into a new Dagger module and finished the process of mocking the main component for tests. --- .../di/CommonsApplicationComponent.java | 5 +- .../commons/di/CommonsApplicationModule.java | 42 ------------- .../free/nrw/commons/di/NetworkingModule.java | 59 +++++++++++++++++++ .../nrw/commons/TestCommonsApplication.kt | 20 +++++-- 4 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java index 91f6d4ccb8..5662bb8852 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java @@ -9,17 +9,16 @@ 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.Contribution; -import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.contributions.ContributionsSyncAdapter; import fr.free.nrw.commons.delete.DeleteTask; import fr.free.nrw.commons.modifications.ModificationsSyncAdapter; -import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.nearby.PlaceRenderer; +import fr.free.nrw.commons.settings.SettingsFragment; @Singleton @Component(modules = { CommonsApplicationModule.class, + NetworkingModule.class, AndroidInjectionModule.class, AndroidSupportInjectionModule.class, ActivityBuilderModule.class, diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 3d55169350..b0aa3e5e6c 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -4,29 +4,21 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; import android.support.v4.util.LruCache; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import javax.inject.Named; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; -import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.location.LocationServiceManager; -import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; import fr.free.nrw.commons.upload.UploadController; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; @@ -36,7 +28,6 @@ @SuppressWarnings({"WeakerAccess", "unused"}) public class CommonsApplicationModule { public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider"; - public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; private Context applicationContext; @@ -72,12 +63,6 @@ public ContentProviderClient provideModificationContentProviderClient(Context co return context.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY); } - @Provides - @Singleton - public OkHttpClient provideOkHttpClient() { - return new OkHttpClient.Builder().build(); - } - @Provides @Named("application_preferences") public SharedPreferences providesApplicationSharedPreferences(Context context) { @@ -126,39 +111,12 @@ public SessionManager providesSessionManager(Context context, return new SessionManager(context, mediaWikiApi, sharedPreferences); } - @Provides - @Singleton - public MediaWikiApi provideMediaWikiApi(Context context, - @Named("default_preferences") SharedPreferences defaultPreferences, - @Named("category_prefs") SharedPreferences categoryPrefs, - Gson gson) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); - } - - @Provides - @Named("commons_mediawiki_url") - @NonNull - @SuppressWarnings("ConstantConditions") - public HttpUrl provideMwUrl() { - return HttpUrl.parse("https://commons.wikimedia.org/"); - } - @Provides @Singleton public LocationServiceManager provideLocationServiceManager(Context context) { return new LocationServiceManager(context); } - /** - * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere. - * @return returns a singleton Gson instance - */ - @Provides - @Singleton - public Gson provideGson() { - return new GsonBuilder().create(); - } - @Provides @Singleton public CacheController provideCacheController() { diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java new file mode 100644 index 0000000000..8c0b523160 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java @@ -0,0 +1,59 @@ +package fr.free.nrw.commons.di; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; + +@Module +@SuppressWarnings({"WeakerAccess", "unused"}) +public class NetworkingModule { + public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; + + @Provides + @Singleton + public OkHttpClient provideOkHttpClient() { + return new OkHttpClient.Builder().build(); + } + + @Provides + @Singleton + public MediaWikiApi provideMediaWikiApi(Context context, + @Named("default_preferences") SharedPreferences defaultPreferences, + @Named("category_prefs") SharedPreferences categoryPrefs, + Gson gson) { + return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); + } + + @Provides + @Named("commons_mediawiki_url") + @NonNull + @SuppressWarnings("ConstantConditions") + public HttpUrl provideMwUrl() { + return HttpUrl.parse(BuildConfig.COMMONS_URL); + } + + /** + * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere. + * @return returns a singleton Gson instance + */ + @Provides + @Singleton + public Gson provideGson() { + return new GsonBuilder().create(); + } + +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index b1de291432..076b3e7451 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons +import android.content.ContentProviderClient import android.content.Context import android.content.SharedPreferences import android.support.v4.util.LruCache -import com.google.gson.Gson import com.nhaarman.mockito_kotlin.mock import com.squareup.leakcanary.RefWatcher import fr.free.nrw.commons.auth.AccountUtil @@ -33,21 +33,31 @@ class TestCommonsApplication : CommonsApplication() { override fun setupLeakCanary(): RefWatcher = RefWatcher.DISABLED } +@Suppress("MemberVisibilityCanBePrivate") class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModule(appContext) { val accountUtil: AccountUtil = mock() val appSharedPreferences: SharedPreferences = mock() val defaultSharedPreferences: SharedPreferences = mock() - val categorySharedPreferences: SharedPreferences = mock() val otherSharedPreferences: SharedPreferences = mock() val uploadController: UploadController = mock() val mockSessionManager: SessionManager = mock() - val mediaWikiApi: MediaWikiApi = mock() val locationServiceManager: LocationServiceManager = mock() val cacheController: CacheController = mock() val mockDbOpenHelper: DBOpenHelper = mock() val nearbyPlaces: NearbyPlaces = mock() val lruCache: LruCache = mock() - val gson: Gson = Gson() + val categoryClient: ContentProviderClient = mock() + val contributionClient: ContentProviderClient = mock() + val modificationClient: ContentProviderClient = mock() + val uploadPrefs: SharedPreferences = mock() + + override fun provideCategoryContentProviderClient(context: Context?): ContentProviderClient = categoryClient + + override fun provideContributionContentProviderClient(context: Context?): ContentProviderClient = contributionClient + + override fun provideModificationContentProviderClient(context: Context?): ContentProviderClient = modificationClient + + override fun providesDirectNearbyUploadPreferences(context: Context?): SharedPreferences = uploadPrefs override fun providesAccountUtil(context: Context): AccountUtil = accountUtil @@ -61,8 +71,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager - override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences, categorySharedPreferences: SharedPreferences, gson: Gson): MediaWikiApi = mediaWikiApi - override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager override fun provideCacheController(): CacheController = cacheController From 85b474bc8224496e67a16fd4b27fd0a397d541ae Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sun, 15 Apr 2018 07:32:14 -0500 Subject: [PATCH 13/94] Refactoring to extract GpsCategoryModel and ensure single-responsibility-principle is maintained in CategoryApi. --- app/proguard-rules.txt | 1 - .../nrw/commons/caching/CacheController.java | 19 +- .../category/CategorizationFragment.java | 8 +- .../commons/di/CommonsApplicationModule.java | 7 - .../free/nrw/commons/mwapi/CategoryApi.java | 101 ++++++++++ .../nrw/commons/mwapi/model/ApiResponse.java | 12 ++ .../fr/free/nrw/commons/mwapi/model/Page.java | 17 ++ .../nrw/commons/mwapi/model/PageCategory.java | 12 ++ .../free/nrw/commons/mwapi/model/Query.java | 10 + .../free/nrw/commons/upload/CategoryApi.java | 165 ---------------- .../nrw/commons/upload/GpsCategoryModel.java | 40 ++++ .../nrw/commons/upload/ShareActivity.java | 23 ++- .../nrw/commons/TestCommonsApplication.kt | 4 - .../free/nrw/commons/mwapi/CategoryApiTest.kt | 178 ++++++++++++++++++ .../commons/mwapi/model/ApiResponseTest.kt | 28 +++ .../commons/mwapi/model/PageCategoryTest.kt | 20 ++ .../free/nrw/commons/mwapi/model/PageTest.kt | 12 ++ .../commons/upload/GpsCategoryModelTest.kt | 77 ++++++++ 18 files changed, 542 insertions(+), 192 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index f3e00b3f58..39b6187189 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -1,5 +1,4 @@ -dontobfuscate -keep class org.apache.http.** { *; } -dontwarn org.apache.http.** --keep class fr.free.nrw.commons.upload.CategoryApi$Page {*;} -keep class android.support.v7.widget.ShareActionProvider { *; } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java index 1b48c3c50b..72de0db706 100644 --- a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java +++ b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java @@ -7,18 +7,25 @@ import java.util.Arrays; import java.util.List; -import fr.free.nrw.commons.upload.CategoryApi; +import javax.inject.Inject; +import javax.inject.Singleton; + +import fr.free.nrw.commons.upload.GpsCategoryModel; import timber.log.Timber; +@Singleton public class CacheController { + private final GpsCategoryModel gpsCategoryModel; + private final QuadTree> quadTree; private double x, y; - private QuadTree> quadTree; private double xMinus, xPlus, yMinus, yPlus; private static final int EARTH_RADIUS = 6378137; - public CacheController() { + @Inject + CacheController(GpsCategoryModel gpsCategoryModel) { + this.gpsCategoryModel = gpsCategoryModel; quadTree = new QuadTree<>(-180, -90, +180, +90); } @@ -31,8 +38,8 @@ public void setQtPoint(double decLongitude, double decLatitude) { public void cacheCategory() { List pointCatList = new ArrayList<>(); - if (CategoryApi.GpsCatExists.getGpsCatExists()) { - pointCatList.addAll(CategoryApi.getGpsCat()); + if (gpsCategoryModel.getGpsCatExists()) { + pointCatList.addAll(gpsCategoryModel.getCategoryList()); Timber.d("Categories being cached: %s", pointCatList); } else { Timber.d("No categories found, so no categories cached"); @@ -65,7 +72,7 @@ public List findCategory() { } //Based on algorithm at http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters - public void convertCoordRange() { + private void convertCoordRange() { //Position, decimal degrees double lat = y; double lon = x; diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java index 514b2fb5fd..93ddb60d5d 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java @@ -39,7 +39,7 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; -import fr.free.nrw.commons.upload.CategoryApi; +import fr.free.nrw.commons.upload.GpsCategoryModel; import fr.free.nrw.commons.utils.StringSortingUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; @@ -73,6 +73,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment { @Inject @Named("prefs") SharedPreferences prefsPrefs; @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; @Inject CategoryDao categoryDao; + @Inject GpsCategoryModel gpsCategoryModel; private RVRendererAdapter categoriesAdapter; private OnCategoriesSaveHandler onCategoriesSaveHandler; @@ -253,7 +254,6 @@ private List getStringList(List input) { } private Observable defaultCategories() { - Observable directCat = directCategories(); if (hasDirectCategories) { Timber.d("Image has direct Cat"); @@ -287,9 +287,7 @@ private Observable directCategories() { } private Observable gpsCategories() { - return Observable.fromIterable( - CategoryApi.GpsCatExists.getGpsCatExists() - ? CategoryApi.getGpsCat() : new ArrayList<>()) + return Observable.fromIterable(gpsCategoryModel.getCategoryList()) .map(name -> new CategoryItem(name, false)); } diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index b0aa3e5e6c..f4a77c4495 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -13,7 +13,6 @@ import dagger.Provides; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; -import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.mwapi.MediaWikiApi; @@ -117,12 +116,6 @@ public LocationServiceManager provideLocationServiceManager(Context context) { return new LocationServiceManager(context); } - @Provides - @Singleton - public CacheController provideCacheController() { - return new CacheController(); - } - @Provides @Singleton public DBOpenHelper provideDBOpenHelper(Context context) { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java new file mode 100644 index 0000000000..031796745f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java @@ -0,0 +1,101 @@ +package fr.free.nrw.commons.mwapi; + +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import fr.free.nrw.commons.mwapi.model.ApiResponse; +import fr.free.nrw.commons.mwapi.model.Page; +import fr.free.nrw.commons.mwapi.model.PageCategory; +import io.reactivex.Single; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import timber.log.Timber; + +/** + * Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates + * with nearby Commons categories. Parses the results using GSON to obtain a list of relevant + * categories. Note: that caller is responsible for executing the request() method on a background + * thread. + */ +public class CategoryApi { + + private final OkHttpClient okHttpClient; + private final HttpUrl mwUrl; + private final Gson gson; + + @Inject + public CategoryApi(OkHttpClient okHttpClient, Gson gson, + @Named("commons_mediawiki_url") HttpUrl mwUrl) { + this.okHttpClient = okHttpClient; + this.mwUrl = mwUrl; + this.gson = gson; + } + + public Single> request(String coords) { + return Single.fromCallable(() -> { + HttpUrl apiUrl = buildUrl(coords); + Timber.d("URL: %s", apiUrl.toString()); + + Request request = new Request.Builder().get().url(apiUrl).build(); + Response response = okHttpClient.newCall(request).execute(); + ResponseBody body = response.body(); + if (body == null) { + return Collections.emptyList(); + } + + ApiResponse apiResponse = gson.fromJson(body.charStream(), ApiResponse.class); + Set categories = new LinkedHashSet<>(); + if (apiResponse != null && apiResponse.hasPages()) { + for (Page page : apiResponse.query.pages) { + for (PageCategory category : page.getCategories()) { + categories.add(category.withoutPrefix()); + } + } + } + return new ArrayList<>(categories); + }); + } + + /** + * Builds URL with image coords for MediaWiki API calls + * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 + * + * @param coords Coordinates to build query with + * @return URL for API query + */ + private HttpUrl buildUrl(String coords) { + return mwUrl.newBuilder() + .addPathSegment("w") + .addPathSegment("api.php") + .addQueryParameter("action", "query") + .addQueryParameter("prop", "categories|coordinates|pageprops") + .addQueryParameter("format", "json") + .addQueryParameter("clshow", "!hidden") + .addQueryParameter("coprop", "type|name|dim|country|region|globe") + .addQueryParameter("codistancefrompoint", coords) + .addQueryParameter("generator", "geosearch") + .addQueryParameter("ggscoord", coords) + .addQueryParameter("ggsradius", "10000") + .addQueryParameter("ggslimit", "10") + .addQueryParameter("ggsnamespace", "6") + .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") + .addQueryParameter("ggsprimary", "all") + .addQueryParameter("formatversion", "2") + .build(); + } + +} + + + diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java new file mode 100644 index 0000000000..7feb902519 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.mwapi.model; + +public class ApiResponse { + public Query query; + + public ApiResponse() { + } + + public boolean hasPages() { + return query != null && query.pages != null; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java new file mode 100644 index 0000000000..d01ba658f5 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java @@ -0,0 +1,17 @@ +package fr.free.nrw.commons.mwapi.model; + +import android.support.annotation.NonNull; + +public class Page { + public String title; + public PageCategory[] categories; + public PageCategory category; + + public Page() { + } + + @NonNull + public PageCategory[] getCategories() { + return categories != null ? categories : new PageCategory[0]; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java new file mode 100644 index 0000000000..be4b9fd791 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.mwapi.model; + +public class PageCategory { + public String title; + + public PageCategory() { + } + + public String withoutPrefix() { + return title != null ? title.replace("Category:", "") : ""; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java new file mode 100644 index 0000000000..b87f97cc3d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java @@ -0,0 +1,10 @@ +package fr.free.nrw.commons.mwapi.model; + +public class Query { + public Page[] pages; + + public Query() { + pages = new Page[0]; + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java b/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java deleted file mode 100644 index 8a7dfcc6e8..0000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java +++ /dev/null @@ -1,165 +0,0 @@ -package fr.free.nrw.commons.upload; - -import android.support.annotation.NonNull; - -import com.google.gson.Gson; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.inject.Inject; -import javax.inject.Named; - -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.ResponseBody; -import timber.log.Timber; - -/** - * Uses the OkHttp library to implement asynchronous calls to the Commons MediaWiki API to match - * GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list - * of relevant categories. - */ -public class CategoryApi { - - private static Set categorySet; - private static List categoryList; - private final OkHttpClient okHttpClient; - private final HttpUrl mwUrl; - private final Gson gson; - - @Inject - public CategoryApi(OkHttpClient okHttpClient, @Named("commons_mediawiki_url") HttpUrl mwUrl, Gson gson) { - this.okHttpClient = okHttpClient; - this.mwUrl = mwUrl; - this.gson = gson; - categorySet = new HashSet<>(); - } - - public static List getGpsCat() { - return categoryList; - } - - public static void setGpsCat(List cachedList) { - categoryList = new ArrayList<>(); - categoryList.addAll(cachedList); - Timber.d("Setting GPS cats from cache: %s", categoryList); - } - - public void request(String coords) { - String apiUrl = buildUrl(coords); - Timber.d("URL: %s", apiUrl); - - Call call = okHttpClient.newCall(new Request.Builder().get().url(apiUrl).build()); - call.enqueue(new Callback() { - @Override - public void onFailure(@NonNull Call call, @NonNull IOException e) { - Timber.e(e); - GpsCatExists.setGpsCatExists(false); - } - - @Override - public void onResponse(@NonNull Call call, @NonNull okhttp3.Response response) { - categoryList = new ArrayList<>(); - categorySet = new HashSet<>(); - ResponseBody body = response.body(); - if (body == null) { - return; - } - QueryResponse queryResponse = gson.fromJson(body.charStream(), QueryResponse.class); - if (queryResponse != null && queryResponse.query != null && queryResponse.query.pages != null) { - for (Page page : queryResponse.query.pages) { - if (page.categories != null) { - for (Category category : page.categories) { - String categoryString = category.title.replace("Category:", ""); - categorySet.add(categoryString); - } - categoryList = new ArrayList<>(categorySet); - } - } - } - GpsCatExists.setGpsCatExists(!categorySet.isEmpty()); - } - }); - } - - /** - * Builds URL with image coords for MediaWiki API calls - * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 - * - * @param coords Coordinates to build query with - * @return URL for API query - */ - private String buildUrl(String coords) { - return mwUrl.newBuilder() - .addPathSegment("w") - .addPathSegment("api.php") - .addQueryParameter("action", "query") - .addQueryParameter("prop", "categories|coordinates|pageprops") - .addQueryParameter("format", "json") - .addQueryParameter("clshow", "!hidden") - .addQueryParameter("coprop", "type|name|dim|country|region|globe") - .addQueryParameter("codistancefrompoint", coords) - .addQueryParameter("generator", "geosearch") - .addQueryParameter("ggscoord", coords) - .addQueryParameter("ggsradius", "10000") - .addQueryParameter("ggslimit", "10") - .addQueryParameter("ggsnamespace", "6") - .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") - .addQueryParameter("ggsprimary", "all") - .addQueryParameter("formatversion", "2") - .build().toString(); - } - - public static class GpsCatExists { - private static boolean gpsCatExists; - - public static void setGpsCatExists(boolean gpsCat) { - gpsCatExists = gpsCat; - } - - public static boolean getGpsCatExists() { - return gpsCatExists; - } - } - - private static class QueryResponse { - public Query query; - - public QueryResponse() { - } - } - - private static class Query { - public Page[] pages; - - public Query() { - pages = new Page[0]; - } - } - - private static class Page { - public String title; - public Category[] categories; - public Category category; - - public Page() { - } - } - - private static class Category { - public String title; - - public Category() { - } - } -} - - - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java b/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java new file mode 100644 index 0000000000..841210453b --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java @@ -0,0 +1,40 @@ +package fr.free.nrw.commons.upload; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class GpsCategoryModel { + private Set categorySet; + + @Inject + public GpsCategoryModel() { + clear(); + } + + public void clear() { + categorySet = new HashSet<>(); + } + + public boolean getGpsCatExists() { + return !categorySet.isEmpty(); + } + + public List getCategoryList() { + return new ArrayList<>(categorySet); + } + + public void setCategoryList(List categoryList) { + clear(); + categorySet.addAll(categoryList != null ? categoryList : new ArrayList<>()); + } + + public void add(String categoryString) { + categorySet.add(categoryString); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index e3188534cd..98d11c20ee 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.upload; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -73,8 +74,10 @@ import fr.free.nrw.commons.modifications.ModifierSequence; import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; +import fr.free.nrw.commons.mwapi.CategoryApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import io.reactivex.schedulers.Schedulers; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; @@ -127,6 +130,8 @@ public class ShareActivity @Inject @Named("default_preferences") SharedPreferences prefs; + @Inject + GpsCategoryModel gpsCategoryModel; private String source; private String mimeType; @@ -687,8 +692,9 @@ public void onNegativeResponse() { /** * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. - * Then initiates the calls to MediaWiki API through an instance of MwVolleyApi. + * Then initiates the calls to MediaWiki API through an instance of CategpryApi. */ + @SuppressLint("CheckResult") public void useImageCoords() { if (decimalCoords != null) { Timber.d("Decimal coords of image: %s", decimalCoords); @@ -707,12 +713,21 @@ public void useImageCoords() { // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories if (catListEmpty) { cacheFound = false; - apiCall.request(decimalCoords); + apiCall.request(decimalCoords) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .subscribe( + gpsCategoryModel::setCategoryList, + throwable -> { + Timber.e(throwable); + gpsCategoryModel.clear(); + } + ); Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList); } else { cacheFound = true; - Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); - CategoryApi.setGpsCat(displayCatList); + Timber.d("Cache found, setting categoryList in model to %s", displayCatList); + gpsCategoryModel.setCategoryList(displayCatList); } }else{ Timber.d("EXIF: no coords"); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index 076b3e7451..090cf39b52 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -8,7 +8,6 @@ import com.nhaarman.mockito_kotlin.mock import com.squareup.leakcanary.RefWatcher import fr.free.nrw.commons.auth.AccountUtil import fr.free.nrw.commons.auth.SessionManager -import fr.free.nrw.commons.caching.CacheController import fr.free.nrw.commons.data.DBOpenHelper import fr.free.nrw.commons.di.CommonsApplicationComponent import fr.free.nrw.commons.di.CommonsApplicationModule @@ -42,7 +41,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu val uploadController: UploadController = mock() val mockSessionManager: SessionManager = mock() val locationServiceManager: LocationServiceManager = mock() - val cacheController: CacheController = mock() val mockDbOpenHelper: DBOpenHelper = mock() val nearbyPlaces: NearbyPlaces = mock() val lruCache: LruCache = mock() @@ -73,8 +71,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager - override fun provideCacheController(): CacheController = cacheController - override fun provideDBOpenHelper(context: Context): DBOpenHelper = mockDbOpenHelper override fun provideNearbyPlaces(): NearbyPlaces = nearbyPlaces diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt new file mode 100644 index 0000000000..76f34d55d2 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt @@ -0,0 +1,178 @@ +package fr.free.nrw.commons.mwapi + +import com.google.gson.Gson +import fr.free.nrw.commons.mwapi.model.Page +import fr.free.nrw.commons.mwapi.model.PageCategory +import okhttp3.HttpUrl +import okhttp3.OkHttpClient +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class CategoryApiTest { + private lateinit var server: MockWebServer + private lateinit var url: String + private lateinit var categoryApi: CategoryApi + + @Before + fun setUp() { + server = MockWebServer() + url = "http://${server.hostName}:${server.port}/" + categoryApi = CategoryApi(OkHttpClient.Builder().build(), Gson(), HttpUrl.parse(url)) + } + + @After + fun teardown() { + server.shutdown() + } + + @Test + fun apiReturnsEmptyListWhenError() { + server.enqueue(MockResponse().setResponseCode(400).setBody("")) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun apiReturnsEmptyWhenTheresNoQuery() { + server.success(emptyMap()) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun apiReturnsEmptyWhenQueryHasNoPages() { + server.success(mapOf("query" to emptyMap())) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun apiReturnsEmptyWhenQueryHasPagesButTheyreEmpty() { + server.success(mapOf("query" to + mapOf("pages" to emptyList()))) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun singlePageSingleCategory() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(1, response.size) + assertEquals("one", response[0]) + } + + @Test + fun multiplePagesSingleCategory() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one")), + page(listOf("two")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(2, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + } + + @Test + fun singlePageMultipleCategories() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one", "two")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(2, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + } + + @Test + fun multiplePagesMultipleCategories() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one", "two")), + page(listOf("three", "four")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(4, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + assertEquals("three", response[2]) + assertEquals("four", response[3]) + } + + @Test + fun multiplePagesMultipleCategories_duplicatesRemoved() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one", "two", "three")), + page(listOf("three", "four", "one")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(4, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + assertEquals("three", response[2]) + assertEquals("four", response[3]) + } + + @Test + fun requestSendsWhatWeExpect() { + server.success(mapOf("query" to mapOf("pages" to emptyList()))) + + val coords = "foo,bar" + categoryApi.request(coords).blockingGet() + + server.takeRequest().let { request -> + assertEquals("GET", request.method) + assertEquals("/w/api.php", request.requestUrl.encodedPath()) + request.requestUrl.let { url -> + assertEquals("query", url.queryParameter("action")) + assertEquals("categories|coordinates|pageprops", url.queryParameter("prop")) + assertEquals("json", url.queryParameter("format")) + assertEquals("!hidden", url.queryParameter("clshow")) + assertEquals("type|name|dim|country|region|globe", url.queryParameter("coprop")) + assertEquals(coords, url.queryParameter("codistancefrompoint")) + assertEquals("geosearch", url.queryParameter("generator")) + assertEquals(coords, url.queryParameter("ggscoord")) + assertEquals("10000", url.queryParameter("ggsradius")) + assertEquals("10", url.queryParameter("ggslimit")) + assertEquals("6", url.queryParameter("ggsnamespace")) + assertEquals("type|name|dim|country|region|globe", url.queryParameter("ggsprop")) + assertEquals("all", url.queryParameter("ggsprimary")) + assertEquals("2", url.queryParameter("formatversion")) + } + } + } + + private fun page(catList: List) = Page().apply { + categories = catList.map { + PageCategory().apply { + title = "Category:$it" + } + }.toTypedArray() + } +} + +fun MockWebServer.success(response: Map) { + enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(response))) +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt new file mode 100644 index 0000000000..41406a8949 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt @@ -0,0 +1,28 @@ +package fr.free.nrw.commons.mwapi.model + +import org.junit.Assert.* +import org.junit.Test + +class ApiResponseTest { + @Test + fun hasPages_whenQueryIsNull() { + val response = ApiResponse() + assertFalse(response.hasPages()) + } + + @Test + fun hasPages_whenPagesIsNull() { + val response = ApiResponse() + response.query = Query() + response.query.pages = null + assertFalse(response.hasPages()) + } + + @Test + fun hasPages_defaultsToSafeValue() { + val response = ApiResponse() + response.query = Query() + assertNotNull(response.query.pages) + assertTrue(response.hasPages()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt new file mode 100644 index 0000000000..fcc3d408fc --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt @@ -0,0 +1,20 @@ +package fr.free.nrw.commons.mwapi.model + +import org.junit.Assert.assertEquals +import org.junit.Test + +class PageCategoryTest { + @Test + fun stripPrefix_whenPresent() { + val testObject = PageCategory() + testObject.title = "Category:Foo" + assertEquals("Foo", testObject.withoutPrefix()) + } + + @Test + fun stripPrefix_prefixAbsent() { + val testObject = PageCategory() + testObject.title = "Foo_Bar" + assertEquals("Foo_Bar", testObject.withoutPrefix()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt new file mode 100644 index 0000000000..4179b4fb5b --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.mwapi.model + +import org.junit.Assert.assertNotNull +import org.junit.Test + +class PageTest { + @Test + fun categoriesDefaultToSafeValue() { + val page = Page() + assertNotNull(page.getCategories()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt new file mode 100644 index 0000000000..cd2d77ada0 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt @@ -0,0 +1,77 @@ +package fr.free.nrw.commons.upload + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +class GpsCategoryModelTest { + + private lateinit var testObject : GpsCategoryModel + + @Before + fun setUp() { + testObject = GpsCategoryModel() + } + + @Test + fun initiallyTheModelIsEmpty() { + assertFalse(testObject.gpsCatExists) + assertTrue(testObject.categoryList.isEmpty()) + } + + @Test + fun addingCategoriesToTheModel() { + testObject.add("one") + assertTrue(testObject.gpsCatExists) + assertFalse(testObject.categoryList.isEmpty()) + assertEquals(listOf("one"), testObject.categoryList) + } + + @Test + fun duplicatesAreIgnored() { + testObject.add("one") + testObject.add("one") + assertEquals(listOf("one"), testObject.categoryList) + } + + @Test + fun modelProtectsAgainstExternalModification() { + testObject.add("one") + + val list = testObject.categoryList + list.add("two") + + assertEquals(listOf("one"), testObject.categoryList) + } + + @Test + fun clearingTheModel() { + testObject.add("one") + + testObject.clear() + assertFalse(testObject.gpsCatExists) + assertTrue(testObject.categoryList.isEmpty()) + + testObject.add("two") + assertEquals(listOf("two"), testObject.categoryList) + } + + @Test + fun settingTheListHandlesNull() { + testObject.add("one") + + testObject.categoryList = null + + assertFalse(testObject.gpsCatExists) + assertTrue(testObject.categoryList.isEmpty()) + } + + @Test + fun setttingTheListOverwritesExistingValues() { + testObject.add("one") + + testObject.categoryList = listOf("two") + + assertEquals(listOf("two"), testObject.categoryList) + } +} \ No newline at end of file From e8f3849bb0208cf60a50f7bd5eb5cc094206915b Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 14:16:29 +0530 Subject: [PATCH 14/94] fixed featured image back bug --- .../nrw/commons/category/CategoryImagesListFragment.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 3b6734eddf..18eb5ec481 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -224,4 +224,10 @@ private void handleSuccess(List collection) { public ListAdapter getAdapter() { return gridView.getAdapter(); } + + @Override + public void onResume() { + gridView.setAdapter(gridAdapter); + super.onResume(); + } } From dfd4d2aee441dcd515ce820efad2e0945eba0c99 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 28 May 2018 10:51:54 +0200 Subject: [PATCH 15/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-hr/error.xml | 10 + app/src/main/res/values-hr/strings.xml | 275 +++++++++++++++++++++ app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 3 +- app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 11 +- 9 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/values-hr/error.xml create mode 100644 app/src/main/res/values-hr/strings.xml diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 14444cc4a4..3002482a55 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -57,6 +57,7 @@ Παρακαλώ παρέχετε ένα τίτλο για αυτό το αρχείο Περιγραφή Δεν είναι δυνατή η σύνδεση - αποτυχία του δικτύου + Αποτυχία σύνδεσης - παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό σας Πάρα πολλές ανεπιτυχείς προσπάθειες. Παρακαλώ δοκιμάστε ξανά σε λίγα λεπτά. Συγνώμη, αυτός ο χρήστης έχει αποκλειστεί από τα Commons Πρέπει να δώσετε τον κωδικό πιστοποίησης με δύο παράγοντες diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 804c8755d1..1be0ebd1aa 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -58,7 +58,7 @@ Proporciona un título para este archivo Descripción No se pudo iniciar sesión: falla de red - Incapaz de ingresar - por favor cheque su nombre de usario y contraseña + No se puede acceder. Revisa el nombre de usuario y la contraseña Demasiados intentos fallidos. Inténtalo de nuevo en unos minutos. Lo sentimos, este usuario ha sido bloqueado en Commons Debes proporcionar tu código de auntenticación de dos factores. diff --git a/app/src/main/res/values-hr/error.xml b/app/src/main/res/values-hr/error.xml new file mode 100644 index 0000000000..4bc77982c7 --- /dev/null +++ b/app/src/main/res/values-hr/error.xml @@ -0,0 +1,10 @@ + + + + Aplikacija Zajednički poslužitelj je prestala s radom + Nešto je krenulo po krivu! + Napišite nam što radite i podijelite s nama putem elektroničke pošte. Pomoći će nam da to popravimo! + Hvala Vam! + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..f675e2f779 --- /dev/null +++ b/app/src/main/res/values-hr/strings.xml @@ -0,0 +1,275 @@ + + + + Izgled + Opće + Povratna informacija + Lokacija + Zajednički poslužitelj + + Postavke + Suradničko ime + Zaporka + Prijavite se na Commons beta račun + Prijavi se + Zaboravljena zaporka? + Otvori račun + Prijava + Molimo pričekajte ... + Prijava uspješna! + Prijava neuspješna! + Datoteka nije pronađena. Molimo probajte drugu. + Autentifikacija neuspješna! + Postavljanje započeto! + %1$s postavljeno! + Dodirnite da biste vidjeli datoteku + Počinje postavljanje %1$s + Postavljanje %1$s + Završeno postavljanje %1$s + Postavljanje %1$s neuspješno + Dodirnite da biste vidjeli + + Postavlja se %1$d datoteka + Postavljaju se %1$d datoteke + + Moja nedavja postavljanja + U redu čekanja + Neuspješno + %1$d%% postavljeno + Postavljanje + Iz galerije + Napravi sliku + U blizini + Moja postavljanja + Podijeli + Pogledaj u pregledniku + Naziv + Molimo imenujte ovu datoteku + Opis + Prijava nije moguća - mrežna pogrješka + Prijava nije moguća - molimo provjerite suradničko ime i zaportku + Previše neuspješnih pokušaja, molimo probajte opet za par minuta. + Ispričavamo se, ovaj je suradnik blokiran na Zajendičkom poslužitelju + Morate upisati autetifikacijski kôd od dva faktora + Prijava neuspješna + Postavljanje + Imenujte ovaj set + Promjene + Postavljanje + Pretraži kategorije + Spremi + Osvježi + Popis + GPS je onemogućen na Vašem uređaju. Želite li ga omogućiti? + Omogući GPS + Nemate još postavljenih datoteka + + \@string/contributions_subtitle_zero + %1$d postavljena datoteka + %1$d postavljene datoteke + + + Započeto %1$d postavljanje + Započeta %1$d postavljanja + + + %1$d postavljanje + %1$d postavljanja + + Nema kategorija koje odgovoraju upitu %1$s + Dodajte slikama kategorije kako bi se lakše pronašle. Da biste ih dodali, započnite s upisivanjem. + Kategorije + Postavke + Otvori račun + Izabrane slike + O + Aplikacija The Wikimedia Commons je aplikacija otvorenog kôda koju razvijaju i održavaju volonteri Wikimedijine zajednice. Zaklada Wikimedija nije uključena u stvaranje, razvoj ili održavanje ove aplikacije. + Da biste prijavili poteškoću ili dali prijedlog, stvorite <a href=\"https://github.com/commons-app/apps-android-commons/issues\">novi zahtjev na GitHubu</a>. + <u>Politika privatnosti</u> + <u>Zasluge</u> + O + Pošaljite povratnu informaciju (putem elektroničke pošte) + Klijent za elektroničku poštu nije instaliran + Nedavno rabljene kategorije + Pričekajte za prvu sinkronizaciju... + Nemate još postavljenih slika. + Pokušaj ponovo + Odustani + Ova će slika biti licencirana pod %1$s + Slanjem ove slike izjavljujem da je ona moje djelo i ne sadrži materijale zaštićene autorskim pravom ili selfije, te da je u skladu sa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">smjernicama Zajedničkog poslužitelja</a>. + Preuzmi + Podrazumijevana licencija + Rabite prethodni naziv/opis + Automatski pribavi trenutačnu lokaciju + Ako slika nema oznaku lokacije, pribavi trenutačnu lokaciju kako bi se mogle ponuditi kategorije + Noćni način + Rabi tamnu temu + Imenovanje-Dijeli pod istim uvjetima 4.0 + Imenovanje 4.0 + Imenovanje-Dijeli pod istim uvjetima 3.0 + Imenovanje 3.0 + CC0 + CC BY-SA 3.0 + CC BY-SA 3.0 (Austrija) + CC BY-SA 3.0 (Njemačka) + CC BY-SA 3.0 (Estonija) + CC BY-SA 3.0 (Španjolska) + CC BY-SA 3.0 (Hrvatska) + CC BY-SA 3.0 (Luksemburg) + CC BY-SA 3.0 (Nizozemska) + CC BY-SA 3.0 (Norveška) + CC BY-SA 3.0 (Poljska) + CC BY-SA 3.0 (Rumunjska) + CC BY 3.0 + CC BY-SA 4.0 + CC BY 4.0 + CC Zero + Na Zajedničkom poslužitelju se nalazi većina slika rabljena na Wikipediji. + Vaše slike pomažu u edukaciji ljudi diljem svijeta! + Molimo postavite slike koje su u cijelosti Vaše djelo: + Objekti iz prirode (cvijeće, životinje, planine)\n• Korisni objekti (bicikla, željezničke postaje)\n• Poznate osobe (Vaš gradonačelnik, olimpijski sportaš kojeg ste sreli) + Objekti iz prirode (cvijeće, životinje, planine) + Korisni objekti (bicikla, željezničke postaje) + Poznate osobe (Vaš gradonačelnik, olimpijski sportaš kojeg ste sreli) + Molimo NE postavljajte: + - selfije ili slike Vaših prijatelja\n- slike koje ste preuzeli s interneta\n- snimke ekrana zaštićenih aplikacija + Selfije ili slike Vaših prijatelja + Slike koje ste preuzeli s interneta + Snimke ekrana zaštićenih aplikacija + Primjer postavljanja: + - Naziv: Sydneyska opera\n- Opis: Sydneyska opera viđena iz zaljeva\n- Kategorije: Sydney Opera House from the west, Sydney Opera House remote views + Naziv: Sydneyska opera + Opis: Sydneyska opera viđena iz zaljeva + Kategorije: Sydney Opera House from the west, Sydney Opera House remote views + Dijelite Vaše slike. Pomozite da članci na Wikipediji zažive! + Slike na wikipediji su sa Zajedničkog poslužitelja. + Vaše slike pomažu u edukaciji ljudi diljem svijeta. + Izbjegavajte materijale s autorskim pravima koje ste pronašli na internetu (slike plakata, naslovnice knjiga, i slično). + Jeste li razumjeli? + Da! + Kategorije + Učitavanje... + Ništa nije odabrano + Nema opisa + Nepoznata licencija + Osvježi + Potrebno dopuštenje čitanja vanjske pohrane. Bez toga aplikacija ne može pristupiti Vašoj galeriji. + Potrebno dopuštenje spremanja na vanjsku pohranu. Bez toga aplikacija ne može pristupiti Vašoj kameri. + Potrebno dopuštenje za određivanje trenutačne lokacije za prijedloge kategorija (nije obvezno) + U redu + Mjesta u blizini + Nisu pronađena mjesta u blizini + Upozorenje + Ova datoteka već postoji na Zajedničkom poslužitelju. Jeste li sigurni da želite nastaviti? + Da + Ne + Naslov + Naslov medija + Opis + Ovdje ide opis datoteke. Mogao bi biti poprilično dug i trebat će se prelomiti u nekoliko redova. Nadamo se da će lijepo izgledati. + Autor + Ovdje ide suradničko ime autora izabrane slike. + Datum postavljanja + Licencija + Koordinate + Ništa nije navedeno + Postani beta tester + Prijavite se na naš beta-kanal na Google Playu i dobijte raniji pristup novim mogućnostima i ispravkama pogrješaka + Kôd za provjeru u 2 koraka + Moje ograničenje nedavnih postavljanja + Najviše moguće + Nije moguće prikazati više od 500 + Postavi ograničenje nedavnih postavljanja + Kôd za provjeru u 2 koraka nije podržan. + Zaista se želite odjaviti? + Logotip Zajedničkog poslužitelja + Mrežno mjesto Zajedničkog poslužitelja + Stranica Zajedničkog poslužitelja na Facebooku + Izvorni kôd Zajedničkog poslužitelja na Githubu + Pozadinska slika + Slika nije uspjela + Slika nije pronađena + Postavi sliku + Planina Zao + Ljame + Dugin most + Tulipan + Bez selfija + Vlasnička slika + Welcome (Wikipedija) + Dobro došli (autorska prava) + Sidnejska opera + Odustani + Otvori + Zatvori + Početna stranica + Postavljanje + U blizini + O + Postavke + Povratna informacija + Odjava + Upute + Obavijesti + Izabrano + Mjesta u blizini ne mogu biti prikazana bez dopuštenja određivanja lokacije + nema opisa + Stranica datoteke na Zajedničkom poslužitelju + Stavka na Wikidati + Članak na Wikipediji + Pogrješka predmemoriranja slika + Jedinstveni naziv datoteke koji će služiti kao njeno ime. Možete koristiti uobičajeni jezik s razmacima. Ne uključuje datotečni nastavak. + Opišite medij što je više moguće: gdje je napravljen, što prikazuje,... Opišite objekte ili osobe. Napišite informacije koje ne mogu biti lako okrivene, npr. doba dana ako je u pitanju pejzaž. Ako medij prikazuje nešto neobično, molimo objasnite što je neobično. + Slika je pretamna, želite ili je ipak ostaviti? Zajednički poslužitelj je namijenjen slikama od enciklopedijske vrijednosti. + Slika je mutna, želite ili je ipak ostaviti? Zajednički poslužitelj je namijenjen slikama od enciklopedijske vrijednosti. + Daj dopuštenje + Rabi vanjsku pohranu + Spremite slike načinjene kamerom Vašeg uređaja + Prijavite se na Vaš račun + Pošalji zapisnik + Pošalji zapisnik elektroničkom poštom razvijateljima + Preglednik nije pronađen + Pogrješka! URL nije pronađen + Predloži za brisanje + Slika je predložena za brisanje + Pogledaj u pregledniku + Lokacija nepromijenjena. + Lokacija nedostupna. + Potrebno je dopuštenje za popis mjesta u blizini + PRIBAVI UPUTE + PROČITAJ ČLANAK + %1$s, dobro došli na Zajednički poslužitelj! Drago nam je da ste tu. + %1$s Vam je ostavio poruku na Vašoj razgovornoj stranici + Hvala Vam na uređivanju + %1$s Vas je spomenuo na %2$s. + Prebaci prikaz + UPUTE + WIKIDATA + WIKIPEDIJA + ZAJEDNIČKI POSLUŽITELJ + <u>Ocijenite nas</u> + <u>ČPP</u> + Preskoči upute + Internet nije dostupan + Internet je dostupan + Pogrješka dohvaćanja obavijesti + Nema obavijesti + <u>Prevedi</u> + Jezici + Odaberite jezik na koji bi željeli prevoditi + Nastavi + Odustani + Pokušaj ponovo + U redu! + Ovo su mjesta u blizini koja trebaju slike za ilustriranje članaka o njima na Wikipediji + Dodirnite da biste dobili popis ovih mjesta + Možete postaviti sliku s bilo kojeg mjesta u Vašoj galeriji ili kameri + Slike nisu pronađene! + Pogrješka prilikom učitavanja slika. + Postavio: %1$s + Aplikacija za dijeljenje + Prilikom označavanja slike koordinate nisu navedene + Pogrješka prilikom dohvaćanja mjesta u blizini. + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 49763dda8f..8005ce63cc 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -58,6 +58,7 @@ 이 파일의 제목을 지정해 주십시오 설명 로그인할 수 없습니다 - 네트워크 오류입니다 + 로그인할 수 없습니다 - 사용자 이름과 비밀번호를 확인해 주십시오 실패한 시도가 너무 많습니다. 몇 분 후에 다시 시도하세요. 죄송합니다, 이 사용자는 공용에서 차단되었습니다 2요소 인증 코드를 제공해야 합니다. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 98e7212df8..be40b9f8df 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -60,6 +60,7 @@ Tytuł Opis Nie można zalogować - błąd sieci + Nie można się zalogować - sprawdź swoją nazwę użytkownika i hasło Zbyt wiele nieudanych prób zalogowania. Spróbuj ponownie za kilka minut. Przepraszamy, ten użytkownik został zablokowany na Commons Wprowadź swój kod dla dwuetapowej autoryzacji. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 888a453b9f..b995ae2205 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -58,6 +58,7 @@ Forneça um título para este ficheiro, por favor Descrição Não foi possível iniciar sessão - falha de rede + Não foi possível iniciar sessão - verifique o seu nome de utilizador e a palavra-passe Demasiadas tentativas malsucedidas. Por favor, tente de novo dentro de alguns minutos. Desculpe, este utilizador foi bloqueado no Commons Precisa fornecer o seu código de ativação de dois fatores. @@ -220,7 +221,7 @@ Configurações Comentários Sair - Tutorial + Explicação Notificações Destacadas Os sítios aqui perto não podem ser apresentados sem permissões de localização diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 54c1f86c50..cf6ed0896f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -58,6 +58,7 @@ Будь ласка, вкажіть назву цього файлу Опис Неможливо увійти — збій у мережі + Неможливо увійти — будь ласка перевірте ім\'я користувача та пароль Надто багато невдалих спроб. Будь ласка, спробуйте знову через кілька хвилин. Вибачте, цього користувача було заблоковано на Вікісховищі Ви повинні надати код двофакторної автентифікації. diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8b794d1c5f..c57406c801 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -4,6 +4,7 @@ * Kly * LNDDYL * Liuxinyu970226 +* S099001 * Simon Shek * StephDC * Wwycheuk @@ -28,7 +29,7 @@ 請稍候… 登入成功! 登入失敗! - 找不到檔案。請嘗試其它檔案看看。 + 找不到檔案。請試試看其它檔案。 未能核對身分! 開始上傳! 已上傳%1$s! @@ -42,7 +43,7 @@ 正在上載 %1$d 個檔案 正在上載 %1$d 個檔案 - 我的最近上傳 + 我最近的上傳 已佇列 失敗 %1$d%%完成 @@ -109,7 +110,7 @@ 透過提交此圖片,我宣佈這是我個人創作的成品,且不包含受版權保護或自拍內容,並除此之外遵守<a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">維基媒體共享資源方針</a>。 下載 預設授權條款 - 使用先前標題/說明 + 使用先前標題、說明 自動獲取目前位置 若圖片未有地理標記,就以目前位置來作為分類建議。 夜間模式 @@ -154,9 +155,10 @@ 貢獻您的圖片,使維基百科的文章更加生動! 維基百科的圖片,來自維基共享資源。 您的圖片可以幫助教育世界各地的人。 - 避免使用受版權保護的材料,例如從互聯網找來的圖片、海報、書籍封面等 + 避免使用受版權保護的材料,例如從網際網路找來的圖片、海報、書籍封面等 明白了嗎? 是! + 此提示為空,可能無效。請見錯誤報告: https://github.com/commons-app/apps-android-commons/issues/1333 。 分類 載入中… 未選擇 @@ -242,6 +244,7 @@ 錯誤!查無 URL 提名刪除 此圖片已被提名刪除。 + 此提示為空,可能無效。請見錯誤報告: https://github.com/commons-app/apps-android-commons/issues/1333 。 於瀏覽器檢視 位置無法更改。 位置無效。 From 4b7e1e25d9ea4e76a1369b0177fb688a64abaedb Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 15:14:52 +0530 Subject: [PATCH 16/94] Javadocs added --- .../free/nrw/commons/category/CategoryImagesListFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 18eb5ec481..0c0b95bb28 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -225,6 +225,10 @@ public ListAdapter getAdapter() { return gridView.getAdapter(); } + /** + * This method will be called on back pressed of CategoryImagesActivity. + * It initializes the grid view by setting adapter. +\ */ @Override public void onResume() { gridView.setAdapter(gridAdapter); From 5643352566115341bda4230d5fe6b399bc45aa6b Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Wed, 30 May 2018 15:41:36 +0530 Subject: [PATCH 17/94] Add option to set image as wallpaper (#1535) * Add option to set image as wallpaper * Added java docs * Toast message on setting the wallpaper successfully --- app/src/main/AndroidManifest.xml | 1 + .../media/MediaDetailPagerFragment.java | 24 +++++++ .../fr/free/nrw/commons/utils/ImageUtils.java | 72 ++++++++++++++++++- .../main/res/menu/fragment_image_detail.xml | 4 ++ app/src/main/res/values/strings.xml | 2 + 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 53e3a78f9c..9b36266cbd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 62d1261cfe..2c58e8dbb1 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -2,9 +2,11 @@ import android.annotation.SuppressLint; import android.app.DownloadManager; +import android.app.WallpaperManager; import android.content.Intent; import android.content.SharedPreferences; import android.database.DataSetObserver; +import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -26,6 +28,8 @@ import android.view.ViewGroup; import android.widget.Toast; +import java.io.IOException; + import butterknife.BindView; import butterknife.ButterKnife; import javax.inject.Inject; @@ -38,12 +42,15 @@ import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.ImageUtils; +import timber.log.Timber; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.content.Context.DOWNLOAD_SERVICE; import static android.content.Intent.ACTION_VIEW; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.widget.Toast.LENGTH_SHORT; +import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment implements ViewPager.OnPageChangeListener { @@ -140,6 +147,10 @@ public boolean onOptionsItemSelected(MenuItem item) { // Download downloadMedia(m); return true; + case R.id.menu_set_as_wallpaper: + // Set wallpaper + setWallpaper(m); + return true; case R.id.menu_retry_current_image: // Retry ((ContributionsActivity) getActivity()).retryUpload(pager.getCurrentItem()); @@ -155,6 +166,19 @@ public boolean onOptionsItemSelected(MenuItem item) { } } + /** + * Set the media as the device's wallpaper if the imageUrl is not null + * Fails silently if setting the wallpaper fails + * @param media + */ + private void setWallpaper(Media media) { + if(media.getImageUrl() == null || media.getImageUrl().isEmpty()) { + Timber.d("Media URL not present"); + return; + } + ImageUtils.setWallpaperFromImageUrl(getActivity(), Uri.parse(media.getImageUrl())); + } + /** * Start the media file downloading to the local SD card/storage. * The file can then be opened in Gallery or other apps. diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java index 4f6a6d456f..a091d77585 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java @@ -1,12 +1,34 @@ package fr.free.nrw.commons.utils; +import android.app.WallpaperManager; +import android.content.Context; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Color; import android.graphics.Rect; - +import android.net.Uri; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; + +import com.facebook.common.executors.CallerThreadExecutor; +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.imagepipeline.core.ImagePipeline; +import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; + +import java.io.IOException; + +import fr.free.nrw.commons.R; import timber.log.Timber; +import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; + /** * Created by bluesir9 on 3/10/17. */ @@ -132,4 +154,52 @@ private static boolean checkIfImageIsDark(Bitmap bitmap) { return isImageDark; } + + /** + * Downloads the image from the URL and sets it as the phone's wallpaper + * Fails silently if download or setting wallpaper fails. + * @param context + * @param imageUrl + */ + public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) { + Timber.d("Trying to set wallpaper from url %s", imageUrl.toString()); + ImageRequest imageRequest = ImageRequestBuilder + .newBuilderWithSource(imageUrl) + .setAutoRotateEnabled(true) + .build(); + + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + final DataSource> + dataSource = imagePipeline.fetchDecodedImage(imageRequest, context); + + dataSource.subscribe(new BaseBitmapDataSubscriber() { + + @Override + public void onNewResultImpl(@Nullable Bitmap bitmap) { + if (dataSource.isFinished() && bitmap != null){ + Timber.d("Bitmap loaded from url %s", imageUrl.toString()); + setWallpaper(context, Bitmap.createBitmap(bitmap)); + dataSource.close(); + } + } + + @Override + public void onFailureImpl(DataSource dataSource) { + Timber.d("Error getting bitmap from image url %s", imageUrl.toString()); + if (dataSource != null) { + dataSource.close(); + } + } + }, CallerThreadExecutor.getInstance()); + } + + private static void setWallpaper(Context context, Bitmap bitmap) { + WallpaperManager wallpaperManager = WallpaperManager.getInstance(context); + try { + wallpaperManager.setBitmap(bitmap); + ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully)); + } catch (IOException e) { + Timber.e(e,"Error setting wallpaper"); + } + } } diff --git a/app/src/main/res/menu/fragment_image_detail.xml b/app/src/main/res/menu/fragment_image_detail.xml index e864dddb25..70a35951a8 100644 --- a/app/src/main/res/menu/fragment_image_detail.xml +++ b/app/src/main/res/menu/fragment_image_detail.xml @@ -15,6 +15,10 @@ android:id="@+id/menu_download_current_image" android:title="@string/menu_download" app:showAsAction="never" /> + No Images matching %1$s found Search + Set wallpaper + Wallpaper set successfully! From ba5d0ac0a137a135f985cfaebd28cbbe9f1451e8 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 31 May 2018 08:21:26 +0200 Subject: [PATCH 18/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-de/strings.xml | 2 + app/src/main/res/values-el/strings.xml | 2 + app/src/main/res/values-hu/strings.xml | 4 ++ app/src/main/res/values-iw/strings.xml | 2 + app/src/main/res/values-lb/strings.xml | 2 + app/src/main/res/values-mk/strings.xml | 2 + app/src/main/res/values-ne/strings.xml | 2 + app/src/main/res/values-pt-rBR/strings.xml | 2 + app/src/main/res/values-pt/strings.xml | 2 + app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values-sv/strings.xml | 2 + app/src/main/res/values-tr/strings.xml | 4 ++ app/src/main/res/values-uk/strings.xml | 2 + app/src/main/res/values-vi/strings.xml | 67 ++++++++++++++++++++-- app/src/main/res/values-zh-rTW/strings.xml | 2 + 15 files changed, 95 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6fbd7d517e..f0eab6cb27 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -278,4 +278,6 @@ App teilen Während der Bildauswahl wurden keine Koordinaten angegeben Fehler beim Abrufen der Orte in der Nähe. + Hintergrundbild festlegen + Hintergrundbild erfolgreich festgelegt! diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 3002482a55..876b769248 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -282,4 +282,6 @@ Κοινοποίηση εφαρμογής Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας Σφάλμα κατά την εύρεση κοντινών μερών. + Ρύθμιση ταπετσαρίας + Η ταπετσαρία ρυθμίστηκε επιτυχώς! diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index fe91781d9c..d259e4895e 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -59,6 +59,7 @@ Kérlek, adj címet a fájlnak Leírás Nem lehet bejelentkezni - hálózati hiba + Nem sikerült bejelentkezni – kérlek, ellenőrizd a felhasználónevedet és a jelszavadat Túl sok sikertelen próbálkozás. Próbálkozz újra pár perc múlva. Sajnáljuk, ezt a felhasználót blokkolták a Commonson Meg kell adnia a kétlépcsős hitelesítő kódját. @@ -253,15 +254,18 @@ Internet nem elérhető Internet elérhető Nincs értesítés + <u>Fordítás</u> Nyelvek Folytatás Mégse Újra + Értettem! Ezek a helyek vannak a közeledben, amikről van Wikipédia szócikk és nincs bennük kép. A gombra koppintva bejön egy lista, ami ezeket a helyeket mutatja. Bármelyik helyhez feltölthetsz képet a galériádból vagy készíthetsz újat a kamerával. Nem található kép! Képbetöltés közben hiba történt + Feltöltötte: %1$s Alkalmazás megosztása A koordináták nem lettek megadva a kép kiválasztásakor. Hiba a közeli helyek elérésekor. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 03a76a3fc2..e13626c295 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -282,4 +282,6 @@ שיתוף היישום לא צוינו קואורדינטות בעת בחירת התמונה שגיאה באחזור המקומות בסביבתך. + הגדרת רקע + הרקע הוגדר בהצלחה! diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index fa333fa235..e39c064989 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -242,4 +242,6 @@ Keng Biller fonnt! Feeler beim Eropluede vu Biller. Eropgeluede vum: %1$s + Hannergrondbild festleeën + Hannergrondbild festgeluecht diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index ee3d94a83f..66a48e0a0e 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -272,4 +272,6 @@ Сподели прилог Не беа укажани координати при изборот на сликата Грешка при добивањето на околните места. + Задај позадина + Позадината е успешно зададена! diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index 512a655eb3..6cd9596474 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -95,4 +95,6 @@ अज्ञान अनुमतिपत्र ताजागर्ने टगल दृश्य + भित्तेपत्र चयन गर्नुहोस् + भित्तेपत्र सफलतापूर्वक चयन भयो! diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e7b70a35b4..f7fa873bf7 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -284,4 +284,6 @@ Compartilhar o aplicativo Não foram especificadas coordenadas durante a seleção da imagem Erro ao buscar lugares próximos. + Definir imagem de fundo + Imagem de fundo definida! diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index b995ae2205..22776341f7 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -283,4 +283,6 @@ Partilhar aplicação Não foram especificadas coordenadas durante a seleção da imagem Erro ao localizar locais próximos. + Definir imagem de fundo + Imagem de fundo definida! diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 747f368d5f..754fd228cd 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -290,4 +290,6 @@ Поделиться приложением Во время выбора изображения не были указаны координаты Ошибка получения мест поблизости + Сделать фоновой заставкой + Фоновая заставка успешно установлена! diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8c9e8a4787..e57c98b5bd 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -280,4 +280,6 @@ Dela app Koordinater specificerades inte vid bildvalet Fel uppstod när platser i närheten hämtades. + Ange som bakgrundsbild + Bakgrundsbilden ändrades! diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 784458cfa0..c81a11055d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -57,6 +57,7 @@ Lütfen bu dosya için bir başlık ekleyin Açıklama Oturum açılamıyor - ağ hatası + Giriş yapılamıyor - lütfen kullanıcı adınızı ve şifrenizi kontrol edin Çok sayıda başarısız girişimde bulundunuz. Birkaç dakika sonra tekrar deneyin. Üzgünüz, bu kullanıcı Commons\'ta engellendi İki faktörlü kimlik doğrulama kodunu sağlamalısınız. @@ -156,6 +157,7 @@ Telif hakkı olan ve internette bulunan film afişi, kitap kapağı gibi malzemelerin kullanımından kaçının. Bunu anladınız mı? Evet! + * Kategoriler Yükleniyor... Hiçbir şey seçilmedi @@ -279,4 +281,6 @@ Uygulamayı Paylaş Koordinatlar görüntü seçimi sırasında belirlenmedi Yakındaki yerler alınırken hata oluştu. + Duvar kağıdı ayarla + Duvar kağıdı başarıyla ayarlandı! diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index cf6ed0896f..4b02866050 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -289,4 +289,6 @@ Поділитися програмою Під час вибору зображення не були вказані координати Помилка отримання місць поблизу. + Поставити шпалерами екрану + Шпалери екрану виставлено успішно! diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 5b7fd430ce..6e32bfddeb 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7,6 +7,8 @@ * Tuankiet65 --> + Giao diện + Tổng quát Phản hồi Vị trí Commons @@ -51,6 +53,7 @@ Xin hãy đặt tiêu đề cho tập tin này Miêu tả Không thể đăng nhập – có lỗi mạng + Không thể đăng nhập – vui lòng kiểm tra tên người dùng và mật khẩu của bạn Đã đăng nhập thất bại quá nhiều lần. Xin vui lòng thử lại trong vòng vài phút. Rất tiếc, người dùng này đã bị cấm tại Commons Bạn phải cung cấp mã xác thực dùng hai nhân tố. @@ -127,11 +130,20 @@ Wikimedia Commons là nơi lưu giữ phần nhiều hình ảnh xuất hiện trong Wikipedia. Các hình ảnh của bạn giúp giáo dục người dân trên khắp thế giới! Xin hãy tải lên các hình ảnh do chính bạn chụp hoặc vẽ: - - Thiên nhiên (bông hoa, thú vật, cảnh núi)\n- Vật nhân tạo (xe đạp, nhà ga, món ăn)\n- Nhân vật nổi tiếng (thị trưởng của bạn, cầu thủ đội tuyển mà bạn gặp) + Thiên nhiên (bông hoa, thú vật, cảnh núi)\n* Vật nhân tạo (xe đạp, nhà ga, món ăn)\n* Nhân vật nổi tiếng (thị trưởng của bạn, cầu thủ đội tuyển mà bạn gặp) + Thiên nhiên (bông hoa, thú vật, cảnh núi) + Vật nhân tạo (xe đạp, nhà ga, món ăn) + Nhân vật nổi tiếng (thị trưởng của bạn, cầu thủ đội tuyển mà bạn gặp) Xin DỪNG tải lên: - Hình tự sướng hoặc hình bạn bè\n- Hình ảnh tải về từ Internet\n- Ảnh chụp màn hình của ứng dụng thương mại + Ảnh tự chụp hoặc hình bạn bè + Hình ảnh trên Internet tải về + Ảnh chụp màn hình của ứng dụng có bản quyền Tập tin tải lên ví dụ: - - Tiêu đề: Nhà hát Opera Sydney\n- Miêu tả: Nhà hát Opera Sydney nhìn qua cảng\n- Thể loại: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views + - Tiêu đề: Nhà hát Opera Sydney\n- Miêu tả: Nhà hát Opera Sydney nhìn qua cảng\n- Thể loại: Nhà hát Opera Sydney nhìn từ phía tây, Nhà hát Opera Sydney nhìn từ xa + Tiêu đề: Nhà hát Opera Sydney + Miêu tả: Nhà hát Opera Sydney nhìn qua cảng + Thể loại: Nhà hát Opera Sydney nhìn từ phía tây, Nhà hát Opera Sydney nhìn từ xa Đóng góp hình ảnh của bạn. Làm sinh động các bài viết Wikipedia! Các hình ảnh trên Wikipedia được cung cấp bởi Wikimedia Commons. Các hình ảnh của bạn giúp giáo dục người dân trên khắp thế giới. @@ -144,8 +156,8 @@ Không miêu tả Giấy phép không rõ Làm tươi - Yêu cầu cấp phép: Đọc thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép đọc thiết bị lưu trữ bên ngoài để hoạt động. - Yêu cầu cấp phép: Ghi vào thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép ghi vào thiết bị lưu trữ bên ngoài để hoạt động. + Yêu cầu cấp phép: Đọc thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép đọc thiết bị lưu trữ bên ngoài để truy cập kho ảnh của bạn. + Yêu cầu cấp phép: Ghi vào thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép ghi vào thiết bị lưu trữ bên ngoài để truy cập máy chụp hình của bạn. Tùy chọn cấp phép: Định vị hiện tại để nhận gợi ý thể loại OK Nơi Lân cận @@ -158,6 +170,7 @@ Tiêu đề phương tiện Mô tả Mô tả phương tiện xuất hiện tại đây. Nó có thể khá dài và sẽ cần phải ngắt thành nhiều dòng. Nhưng chúng tôi hy vọng sẽ trông ưa nhìn. + Tác giả Ngày tải lên Giấy phép Tọa độ @@ -172,6 +185,9 @@ Hiện chưa hỗ trợ xác thực dùng hai nhân tố. Bạn có chắc chắn muốn đăng xuất? Biểu trưng Commons + Trang Web Commons + Trang Commons tại Facebook + Mã nguồn Commons tại GitHub Hình nền Hình ảnh bị Thất bại Không tìm thấy Hình ảnh @@ -196,15 +212,58 @@ Phản hồi Đăng xuất Hướng dẫn + Thông báo + Chọn lọc Cần có quyền truy cập vị trí của bạn để hiển thị các địa điểm lân cận không tìm thấy miêu tả Trang tập tin Commons Khoản mục Wikidata + Bài Wikipedia Xuất hiện lỗi khi đưa hình ảnh vào vùng nhớ đệm Tên ngắn và duy nhất cho tập tin sẽ được dùng làm tên tập tin. Có thể dùng thuật ngữ bình thường với khoảng cách. Đừng bao gồm phần mở rộng tập tin. Xin vui lòng miêu tả phương tiện càng đầy đủ càng tốt: Chụp ở đâu? Trong hình có gì? Bối cảnh làm sao? Xin vui lòng miêu tả các đối tượng và người trong hình. Cho biết những thông tin khó đoán ra, chẳng hạn giờ trong ngày nếu là phong cảnh. Nếu phương tiện có gì kỳ lạ, xin vui lòng giải thích tại sao nó kỳ lạ. + Cho phép Sử dụng thiết bị lưu trữ bên ngoài Lưu các hình ảnh được chụp bằng máy chụp hình trong ứng dụng vào thiết bị của bạn + Đăng nhập vào tài khoản của bạn + Gửi tập tin nhật trình + Gửi tập tin nhật trình cho nhà phát triển qua thư điện tử + Không tìm thấy trình duyệt để mở URL + Lỗi! Không tìm thấy URL Bầu chọn để xóa + Có đề nghị xóa hình này. Hiện lên ở trang xem browse + Vị trí không thay đổi. + Vị trí không có sẵn. + Bạn cần cho phép để hiển thị danh sách nơi lân cận + HƯỚNG DẪN + ĐỌC BÀI + Hoan nghênh %1$s đã đến Wikimedia Commons! Chúng tôi rất mừng bạn đến đây. + %1$s đã nhắn tin tại trang thảo luận của bạn + Cảm ơn vì bạn đã thực hiện sửa đổi + %1$s đã nhắc đến bạn tại %2$s. + HƯỚNG DẪN + WIKIDATA + WIKIPEDIA + COMMONS + <u>Đánh giá chúng tôi</u> + <u>Câu thường hỏi</u> + Bỏ qua Hướng dẫn + Internet không có sẵn + Internet có sẵn + Lỗi khi lấy thông báo + Không tìm thấy thông báo + <u>Biên dịch</u> + Ngôn ngữ + Chọn ngôn ngữ để gửi bản dịch + Tiến hành + Hủy bỏ + Thử lại + Được rồi! + Không tìm thấy hình ảnh! + Đã xuất hiện lỗi khi tải hình ảnh. + Tải lên bởi: %1$s + Chia sẻ Ứng dụng + Tọa độ không được chỉ định khi chọn hình ảnh + Lỗi khi lấy các nơi lân cận. diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index c57406c801..bd1242e12b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -283,4 +283,6 @@ 分享應用程式 當選擇圖片時未指定座標 索取附近地點時出錯。 + 設定桌布 + 桌布設定成功! From deb8b5350a43cb13a7818b2a688815abd6025f6e Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 4 Jun 2018 12:25:00 +0530 Subject: [PATCH 19/94] SearchHistory Fragment added --- app/build.gradle | 8 +- .../category/CategoryImagesActivity.java | 6 +- .../nrw/commons/explore/SearchActivity.java | 48 +- .../history/SearchHistoryFragment.java | 53 ++ .../res/layout/activity_category_images.xml | 2 +- app/src/main/res/layout/activity_search.xml | 19 +- .../res/layout/fragment_search_history.xml | 13 + app/src/main/res/values/strings.xml | 569 +++++++++--------- 8 files changed, 407 insertions(+), 311 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java create mode 100644 app/src/main/res/layout/fragment_search_history.xml diff --git a/app/build.gradle b/app/build.gradle index fad3eccd8c..e845cdfb63 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ dependencies { implementation 'info.debatty:java-string-similarity:0.24' implementation 'com.borjabravo:readmoretextview:2.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' - implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){ - transitive=true + implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar') { + transitive = true } implementation "com.github.deano2390:MaterialShowcaseView:1.2.0" @@ -80,6 +80,7 @@ dependencies { debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" testImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" + implementation 'com.android.support:support-v4:27.1.1' } android { @@ -119,7 +120,8 @@ android { buildTypes { release { - minifyEnabled false // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient. + minifyEnabled false + // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt' } debug { diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 2cb15534c8..d292fbd98a 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -61,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { supportFragmentManager.addOnBackStackChangedListener(this); if (savedInstanceState != null) { mediaDetails = (MediaDetailPagerFragment) supportFragmentManager - .findFragmentById(R.id.fragmentContainer); + .findFragmentById(R.id.resultsContainer); } requestAuthToken(); @@ -81,7 +81,7 @@ private void setCategoryImagesFragment() { categoryImagesListFragment.setArguments(arguments); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); transaction - .add(R.id.fragmentContainer, categoryImagesListFragment) + .add(R.id.resultsContainer, categoryImagesListFragment) .commit(); } } @@ -107,7 +107,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.fragmentContainer, mediaDetails) + .replace(R.id.resultsContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index f650223ccc..505aa188ef 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -8,6 +8,7 @@ import android.text.TextUtils; import android.view.View; import android.widget.EditText; +import android.widget.FrameLayout; import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxTextView; @@ -18,6 +19,7 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.explore.history.SearchHistoryFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; @@ -32,8 +34,10 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai @BindView(R.id.toolbar_search) Toolbar toolbar; @BindView(R.id.searchBox) EditText etSearchKeyword; - + @BindView(R.id.resultsContainer) FrameLayout resultsContainer; + @BindView(R.id.searchHistoryContainer) FrameLayout searchHistoryContainer; private SearchImageFragment searchImageFragment; + private SearchHistoryFragment searchHistoryFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; private String query; @@ -48,27 +52,39 @@ protected void onCreate(Bundle savedInstanceState) { toolbar.setNavigationOnClickListener(v->onBackPressed()); supportFragmentManager = getSupportFragmentManager(); setBrowseImagesFragment(); + setSearchHistoryFragment(); RxTextView.textChanges(etSearchKeyword) - .takeUntil(RxView.detaches(etSearchKeyword)) - .debounce(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( query -> { - //update image list - if (!TextUtils.isEmpty(query)) { - this.query = query.toString(); - searchImageFragment.updateImageList(query.toString()); - }else { - // open search history fragment - } - } - ); + .takeUntil(RxView.detaches(etSearchKeyword)) + .debounce(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( query -> { + //update image list + if (!TextUtils.isEmpty(query)) { + resultsContainer.setVisibility(View.VISIBLE); + searchHistoryContainer.setVisibility(View.GONE); + this.query = query.toString(); + searchImageFragment.updateImageList(query.toString()); + }else { + resultsContainer.setVisibility(View.GONE); + searchHistoryContainer.setVisibility(View.VISIBLE); + // open search history fragment + } + } + ); + } + + private void setSearchHistoryFragment() { + searchHistoryFragment = new SearchHistoryFragment(); + FragmentTransaction transaction = supportFragmentManager.beginTransaction(); + transaction.add(R.id.searchHistoryContainer, searchHistoryFragment).commit(); } private void setBrowseImagesFragment() { searchImageFragment = new SearchImageFragment(); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction.add(R.id.fragmentContainer, searchImageFragment).commit(); + transaction.add(R.id.resultsContainer, searchImageFragment).commit(); + } @Override @@ -110,7 +126,7 @@ public void onSearchImageClicked(int index) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.fragmentContainer, mediaDetails) + .replace(R.id.resultsContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java new file mode 100644 index 0000000000..6b4b16d006 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java @@ -0,0 +1,53 @@ +package fr.free.nrw.commons.explore.history; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +import butterknife.BindView; +import butterknife.ButterKnife; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; + + +public class SearchHistoryFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; +// @BindView(R.id.imagesListBox) +// RecyclerView imagesRecyclerView; + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); + ButterKnife.bind(this, rootView); +// imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); +// ArrayList items = new ArrayList<>(); +// imagesAdapter = adapterFactory.create(items); +// imagesRecyclerView.setAdapter(imagesAdapter); + return rootView; + } +} diff --git a/app/src/main/res/layout/activity_category_images.xml b/app/src/main/res/layout/activity_category_images.xml index c329e44585..c1fb5ed1f6 100644 --- a/app/src/main/res/layout/activity_category_images.xml +++ b/app/src/main/res/layout/activity_category_images.xml @@ -17,7 +17,7 @@ diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index e6aedc5e6e..e72c206c6e 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -40,15 +40,24 @@ /> - - - + /> + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed9016fd11..ed7bc96dba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,292 +1,295 @@ - Appearance - General - Feedback - Location - Commons - - Settings - Username - Password - Log in to your Commons Beta account - Log in - Forgot Password? - Sign up - Logging in - Please wait… - Login success! - Login failed! - File not found. Please try another file. - Authentication failed! - Upload started! - %1$s uploaded! - Tap to view your upload - Starting %1$s upload - %1$s uploading - Finishing uploading %1$s - Uploading %1$s failed - Tap to view - - %1$d file uploading - %1$d files uploading - - My Recent Uploads - Queued - Failed - %1$d%% complete - Uploading - From Gallery - Take photo - Nearby - My uploads - Share - View in Browser - Title - Please provide a title for this file - Description - Unable to login - network failure - Unable to login - please check your username and password - Too many unsuccessful attempts. Please try again in a few minutes. - Sorry, this user has been blocked on Commons - You must provide your two factor authentication code. - Login failed - Upload - Name this set - Modifications - Upload - Search categories - Save - Refresh - List - GPS is disabled in your device. Would you like to enable it? - Enable GPS - No uploads yet - - - @string/contributions_subtitle_zero - %1$d upload - %1$d uploads - - - Starting %1$d upload - Starting %1$d uploads - - - %1$d upload - %1$d uploads - - No categories matching %1$s found - Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories. - Categories - Settings - Sign Up - Explore - About - The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. - Wikimedia Commons - Create a new <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub issue</a> for bug reports and suggestions. - Privacy policy]]> - Credits]]> - About - Send Feedback (via Email) - No email client installed - Recently used categories - Waiting for first sync… - You have not yet uploaded any photos. - Retry - Cancel - This image will be licensed under %1$s - By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. - Download - Default License - Use previous title/description - Automatically get current location - Retrieve current location to offer category suggestions if image is not geotagged - Night mode - Use dark theme - Attribution-ShareAlike 4.0 - Attribution 4.0 - Attribution-ShareAlike 3.0 - Attribution 3.0 - CC0 - CC BY-SA 3.0 - CC BY-SA 3.0 (Austria) - CC BY-SA 3.0 (Germany) - CC BY-SA 3.0 (Estonia) - CC BY-SA 3.0 (Spain) - CC BY-SA 3.0 (Croatia) - CC BY-SA 3.0 (Luxembourg) - CC BY-SA 3.0 (Netherlands) - CC BY-SA 3.0 (Norway) - CC BY-SA 3.0 (Poland) - CC BY-SA 3.0 (Romania) - CC BY 3.0 - CC BY-SA 4.0 - CC BY 4.0 - CC Zero - Wikimedia Commons hosts most of the images that are used in Wikipedia. - Your images help educate people around the world! - Please upload pictures that are taken or created entirely by yourself: - Natural objects (flowers, animals, mountains)\n• Useful objects (bicycles, train stations)\n• Famous people (your mayor, Olympic athletes you met) - Natural objects (flowers, animals, mountains) - Useful objects (bicycles, train stations) - Famous people (your mayor, Olympic athletes you met) - Please do NOT upload: - - Selfies or pictures of your friends\n- Pictures you downloaded from the Internet\n- Screenshots of proprietary apps - Selfies or pictures of your friends - Pictures you downloaded from the Internet - Screenshots of proprietary apps - Example upload: - - Title: Sydney Opera House\n- Description: Sydney Opera House as viewed from across the bay\n- Categories: Sydney Opera House from the west, Sydney Opera House remote views - Title: Sydney Opera House - Description: Sydney Opera House as viewed from across the bay - Categories: Sydney Opera House from the west, Sydney Opera House remote views - Contribute your images. Help Wikipedia articles come to life! - Images on Wikipedia come from Wikimedia Commons. - Your images help educate people around the world. - Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc. - You think you got it? - Yes! - More Information - Categories - Loading… - None selected - No description - Unknown license - Refresh - Required permission: Read external storage. App cannot access your gallery without this. - Required permission: Write external storage. App cannot access your camera without this. - Optional permission: Get current location for category suggestions - OK - Nearby Places - No nearby places found - Warning - This file already exists on Commons. Are you sure you want to proceed? - Yes - No - Title - Title of the media - Description - Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though. - Author - Featured image author user name goes here. - Uploaded date - License - Coordinates - None provided - Become a Beta Tester - Opt-in to our beta channel on Google Play and get early access to new features and bug fixes - https://play.google.com/apps/testing/fr.free.nrw.commons - mapbox://styles/mapbox/traffic-day-v2 - mapbox://styles/mapbox/traffic-night-v2 - pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg - 2FA Code - My Recent Upload Limit - Maximum Limit - Unable to display more than 500 - Set Recent Upload Limit - Two factor authentication is currently not supported. - Do you really want to logout? - Commons Logo - Commons Website - Commons Facebook Page - Commons Github Source Code - Background Image - Media Image Failed - No Image Found - Upload Image - Mount Zao - Llamas - Rainbow Bridge - Tulip - No Selfies - Proprietary Image - Welcome Wikipedia - Welcome Copyright - Sydney Opera House - Cancel - Open - Close - Home - Upload - Nearby - About - Settings - Feedback - Logout - Tutorial - Notifications - Explore - Nearby places cannot be displayed without location permissions - no description found - Commons file page - Wikidata item - Wikipedia article - Error while caching pictures - A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension - Please describe the media as much as possible: Where was it taken? What does it show? What is the context? Please describe the objects or persons. Reveal information that can not be easily guessed, for instance the time of day if it is a landscape. If the media shows something unusual, please explain what makes it unusual. - This picture is too dark, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. - This picture is blurry, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. - Give permission - Use external storage - Save pictures taken with the in-app camera on your device - Login to your account - Send log file - Send log file to developers via email - No web browser found to open URL - Error! URL not found - Nominate for Deletion - This image has been nominated for deletion. - See webpage for details - View in Browser + Appearance + General + Feedback + Location + Commons + + Settings + Username + Password + Log in to your Commons Beta account + Log in + Forgot Password? + Sign up + Logging in + Please wait… + Login success! + Login failed! + File not found. Please try another file. + Authentication failed! + Upload started! + %1$s uploaded! + Tap to view your upload + Starting %1$s upload + %1$s uploading + Finishing uploading %1$s + Uploading %1$s failed + Tap to view + + %1$d file uploading + %1$d files uploading + + My Recent Uploads + Queued + Failed + %1$d%% complete + Uploading + From Gallery + Take photo + Nearby + My uploads + Share + View in Browser + Title + Please provide a title for this file + Description + Unable to login - network failure + Unable to login - please check your username and password + Too many unsuccessful attempts. Please try again in a few minutes. + Sorry, this user has been blocked on Commons + You must provide your two factor authentication code. + Login failed + Upload + Name this set + Modifications + Upload + Search categories + Save + Refresh + List + GPS is disabled in your device. Would you like to enable it? + Enable GPS + No uploads yet + + + @string/contributions_subtitle_zero + %1$d upload + %1$d uploads + + + Starting %1$d upload + Starting %1$d uploads + + + %1$d upload + %1$d uploads + + No categories matching %1$s found + Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories. + Categories + Settings + Sign Up + Explore + About + The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. + Wikimedia Commons + Create a new <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub issue</a> for bug reports and suggestions. + Privacy policy]]> + Credits]]> + About + Send Feedback (via Email) + No email client installed + Recently used categories + Waiting for first sync… + You have not yet uploaded any photos. + Retry + Cancel + This image will be licensed under %1$s + By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. + Download + Default License + Use previous title/description + Automatically get current location + Retrieve current location to offer category suggestions if image is not geotagged + Night mode + Use dark theme + Attribution-ShareAlike 4.0 + Attribution 4.0 + Attribution-ShareAlike 3.0 + Attribution 3.0 + CC0 + CC BY-SA 3.0 + CC BY-SA 3.0 (Austria) + CC BY-SA 3.0 (Germany) + CC BY-SA 3.0 (Estonia) + CC BY-SA 3.0 (Spain) + CC BY-SA 3.0 (Croatia) + CC BY-SA 3.0 (Luxembourg) + CC BY-SA 3.0 (Netherlands) + CC BY-SA 3.0 (Norway) + CC BY-SA 3.0 (Poland) + CC BY-SA 3.0 (Romania) + CC BY 3.0 + CC BY-SA 4.0 + CC BY 4.0 + CC Zero + Wikimedia Commons hosts most of the images that are used in Wikipedia. + Your images help educate people around the world! + Please upload pictures that are taken or created entirely by yourself: + Natural objects (flowers, animals, mountains)\n• Useful objects (bicycles, train stations)\n• Famous people (your mayor, Olympic athletes you met) + Natural objects (flowers, animals, mountains) + Useful objects (bicycles, train stations) + Famous people (your mayor, Olympic athletes you met) + Please do NOT upload: + - Selfies or pictures of your friends\n- Pictures you downloaded from the Internet\n- Screenshots of proprietary apps + Selfies or pictures of your friends + Pictures you downloaded from the Internet + Screenshots of proprietary apps + Example upload: + - Title: Sydney Opera House\n- Description: Sydney Opera House as viewed from across the bay\n- Categories: Sydney Opera House from the west, Sydney Opera House remote views + Title: Sydney Opera House + Description: Sydney Opera House as viewed from across the bay + Categories: Sydney Opera House from the west, Sydney Opera House remote views + Contribute your images. Help Wikipedia articles come to life! + Images on Wikipedia come from Wikimedia Commons. + Your images help educate people around the world. + Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc. + You think you got it? + Yes! + More Information + Categories + Loading… + None selected + No description + Unknown license + Refresh + Required permission: Read external storage. App cannot access your gallery without this. + Required permission: Write external storage. App cannot access your camera without this. + Optional permission: Get current location for category suggestions + OK + Nearby Places + No nearby places found + Warning + This file already exists on Commons. Are you sure you want to proceed? + Yes + No + Title + Title of the media + Description + Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though. + Author + Featured image author user name goes here. + Uploaded date + License + Coordinates + None provided + Become a Beta Tester + Opt-in to our beta channel on Google Play and get early access to new features and bug fixes + https://play.google.com/apps/testing/fr.free.nrw.commons + mapbox://styles/mapbox/traffic-day-v2 + mapbox://styles/mapbox/traffic-night-v2 + pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg + 2FA Code + My Recent Upload Limit + Maximum Limit + Unable to display more than 500 + Set Recent Upload Limit + Two factor authentication is currently not supported. + Do you really want to logout? + Commons Logo + Commons Website + Commons Facebook Page + Commons Github Source Code + Background Image + Media Image Failed + No Image Found + Upload Image + Mount Zao + Llamas + Rainbow Bridge + Tulip + No Selfies + Proprietary Image + Welcome Wikipedia + Welcome Copyright + Sydney Opera House + Cancel + Open + Close + Home + Upload + Nearby + About + Settings + Feedback + Logout + Tutorial + Notifications + Explore + Nearby places cannot be displayed without location permissions + no description found + Commons file page + Wikidata item + Wikipedia article + Error while caching pictures + A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension + Please describe the media as much as possible: Where was it taken? What does it show? What is the context? Please describe the objects or persons. Reveal information that can not be easily guessed, for instance the time of day if it is a landscape. If the media shows something unusual, please explain what makes it unusual. + This picture is too dark, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. + This picture is blurry, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. + Give permission + Use external storage + Save pictures taken with the in-app camera on your device + Login to your account + Send log file + Send log file to developers via email + No web browser found to open URL + Error! URL not found + Nominate for Deletion + This image has been nominated for deletion. + See webpage for details + View in Browser - Location has not changed. - Location not available. - Permission required to display a list of nearby places - GET DIRECTIONS - READ ARTICLE + Location has not changed. + Location not available. + Permission required to display a list of nearby places + GET DIRECTIONS + READ ARTICLE - Welcome to Wikimedia Commons, %1$s! We\'re glad you\'re here. - %1$s left a message on your talk page - Thank you for making an edit - %1$s mentioned you on %2$s. - Toggle view - DIRECTIONS - WIKIDATA - WIKIPEDIA - COMMONS - Rate us]]> - FAQ]]> - Skip Tutorial - Internet unavailable - Internet available - Error fetching notifications - No notifications found - Translate]]> - Languages - Select the language that you would like to submit translations for - Proceed - Cancel - Retry + Welcome to Wikimedia Commons, %1$s! We\'re glad you\'re here. + %1$s left a message on your talk page + Thank you for making an edit + %1$s mentioned you on %2$s. + Toggle view + DIRECTIONS + WIKIDATA + WIKIPEDIA + COMMONS + Rate us]]> + FAQ]]> + Skip Tutorial + Internet unavailable + Internet available + Error fetching notifications + No notifications found + Translate]]> + Languages + Select the language that you would like to submit translations for + Proceed + Cancel + Retry - Got it! - These are the places near you that need pictures to illustrate their Wikipedia articles - Tapping this button brings up a list of these places - You can upload a picture for any place from your gallery or camera + Got it! + These are the places near you that need pictures to illustrate their Wikipedia articles + Tapping this button brings up a list of these places + You can upload a picture for any place from your gallery or camera - No images found! - Error occurred while loading images. - Uploaded by: %1$s + No images found! + Error occurred while loading images. + Uploaded by: %1$s - Share App - Coordinates were not specified during image selection - Error fetching nearby places. - Search - Search Commons - No Images matching %1$s found - Search + Share App + Coordinates were not specified during image selection + Error fetching nearby places. + Search + Search Commons + No Images matching %1$s found + Search - Set wallpaper - Wallpaper set successfully! + Set wallpaper + Wallpaper set successfully! + + + Hello blank fragment From 1978f0a47862a7581b834c77302fed5a751a3376 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 4 Jun 2018 12:57:00 +0530 Subject: [PATCH 20/94] Search History Item added --- .../nrw/commons/explore/SearchActivity.java | 8 +- .../history/SearchHistoryFragment.java | 53 ------ .../RecentSearchesContentProvider.java | 168 ++++++++++++++++++ .../recent_searches/RecentSearchesDao.java | 156 ++++++++++++++++ .../RecentSearchesFragment.java | 24 +++ .../drawable-hdpi/ic_delete_grey_700_24dp.png | Bin 0 -> 195 bytes .../drawable-mdpi/ic_delete_grey_700_24dp.png | Bin 0 -> 132 bytes .../ic_delete_grey_700_24dp.png | Bin 0 -> 194 bytes .../ic_delete_grey_700_24dp.png | Bin 0 -> 276 bytes .../ic_delete_grey_700_24dp.png | Bin 0 -> 355 bytes .../res/layout/fragment_search_history.xml | 50 +++++- .../main/res/layout/item_recent_searches.xml | 15 ++ app/src/main/res/values/strings.xml | 4 +- 13 files changed, 414 insertions(+), 64 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java create mode 100644 app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/layout/item_recent_searches.xml diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 505aa188ef..89f6e28e40 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -19,7 +19,7 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.explore.history.SearchHistoryFragment; +import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; @@ -37,7 +37,7 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai @BindView(R.id.resultsContainer) FrameLayout resultsContainer; @BindView(R.id.searchHistoryContainer) FrameLayout searchHistoryContainer; private SearchImageFragment searchImageFragment; - private SearchHistoryFragment searchHistoryFragment; + private RecentSearchesFragment recentSearchesFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; private String query; @@ -74,9 +74,9 @@ protected void onCreate(Bundle savedInstanceState) { } private void setSearchHistoryFragment() { - searchHistoryFragment = new SearchHistoryFragment(); + recentSearchesFragment = new RecentSearchesFragment(); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction.add(R.id.searchHistoryContainer, searchHistoryFragment).commit(); + transaction.add(R.id.searchHistoryContainer, recentSearchesFragment).commit(); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java deleted file mode 100644 index 6b4b16d006..0000000000 --- a/app/src/main/java/fr/free/nrw/commons/explore/history/SearchHistoryFragment.java +++ /dev/null @@ -1,53 +0,0 @@ -package fr.free.nrw.commons.explore.history; - -import android.content.Context; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; - -import butterknife.BindView; -import butterknife.ButterKnife; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; - - -public class SearchHistoryFragment extends Fragment { - // TODO: Rename parameter arguments, choose names that match - // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER - private static final String ARG_PARAM1 = "param1"; - private static final String ARG_PARAM2 = "param2"; -// @BindView(R.id.imagesListBox) -// RecyclerView imagesRecyclerView; - // TODO: Rename and change types of parameters - private String mParam1; - private String mParam2; - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - mParam1 = getArguments().getString(ARG_PARAM1); - mParam2 = getArguments().getString(ARG_PARAM2); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); - ButterKnife.bind(this, rootView); -// imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); -// ArrayList items = new ArrayList<>(); -// imagesAdapter = adapterFactory.create(items); -// imagesRecyclerView.setAdapter(imagesAdapter); - return rootView; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java new file mode 100644 index 0000000000..8b95b57e26 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java @@ -0,0 +1,168 @@ +package fr.free.nrw.commons.explore.recent_searches; + +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.text.TextUtils; + +import javax.inject.Inject; + +import fr.free.nrw.commons.data.DBOpenHelper; +import fr.free.nrw.commons.di.CommonsDaggerContentProvider; +import timber.log.Timber; + +import static android.content.UriMatcher.NO_MATCH; +import static fr.free.nrw.commons.category.CategoryDao.Table.ALL_FIELDS; +import static fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_ID; +import static fr.free.nrw.commons.category.CategoryDao.Table.TABLE_NAME; + +public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { + + public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider"; + // For URI matcher + private static final int CATEGORIES = 1; + private static final int CATEGORIES_ID = 2; + private static final String BASE_PATH = "categories"; + + public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); + + private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH); + + static { + uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES); + uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID); + } + + public static Uri uriForId(int id) { + return Uri.parse(BASE_URI.toString() + "/" + id); + } + + @Inject DBOpenHelper dbOpenHelper; + + @SuppressWarnings("ConstantConditions") + @Override + public Cursor query(@NonNull Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); + queryBuilder.setTables(TABLE_NAME); + + int uriType = uriMatcher.match(uri); + + SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); + Cursor cursor; + + switch (uriType) { + case CATEGORIES: + cursor = queryBuilder.query(db, projection, selection, selectionArgs, + null, null, sortOrder); + break; + case CATEGORIES_ID: + cursor = queryBuilder.query(db, + ALL_FIELDS, + "_id = ?", + new String[]{uri.getLastPathSegment()}, + null, + null, + sortOrder + ); + break; + default: + throw new IllegalArgumentException("Unknown URI" + uri); + } + + cursor.setNotificationUri(getContext().getContentResolver(), uri); + + return cursor; + } + + @Override + public String getType(@NonNull Uri uri) { + return null; + } + + @SuppressWarnings("ConstantConditions") + @Override + public Uri insert(@NonNull Uri uri, ContentValues contentValues) { + int uriType = uriMatcher.match(uri); + SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); + long id; + switch (uriType) { + case CATEGORIES: + id = sqlDB.insert(TABLE_NAME, null, contentValues); + break; + default: + throw new IllegalArgumentException("Unknown URI: " + uri); + } + getContext().getContentResolver().notifyChange(uri, null); + return Uri.parse(BASE_URI + "/" + id); + } + + @Override + public int delete(@NonNull Uri uri, String s, String[] strings) { + return 0; + } + + @SuppressWarnings("ConstantConditions") + @Override + public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { + Timber.d("Hello, bulk insert! (CategoryContentProvider)"); + int uriType = uriMatcher.match(uri); + SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); + sqlDB.beginTransaction(); + switch (uriType) { + case CATEGORIES: + for (ContentValues value : values) { + Timber.d("Inserting! %s", value); + sqlDB.insert(TABLE_NAME, null, value); + } + break; + default: + throw new IllegalArgumentException("Unknown URI: " + uri); + } + sqlDB.setTransactionSuccessful(); + sqlDB.endTransaction(); + getContext().getContentResolver().notifyChange(uri, null); + return values.length; + } + + @SuppressWarnings("ConstantConditions") + @Override + public int update(@NonNull Uri uri, ContentValues contentValues, String selection, + String[] selectionArgs) { + /* + SQL Injection warnings: First, note that we're not exposing this to the + outside world (exported="false"). Even then, we should make sure to sanitize + all user input appropriately. Input that passes through ContentValues + should be fine. So only issues are those that pass in via concating. + + In here, the only concat created argument is for id. It is cast to an int, + and will error out otherwise. + */ + int uriType = uriMatcher.match(uri); + SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); + int rowsUpdated; + switch (uriType) { + case CATEGORIES_ID: + if (TextUtils.isEmpty(selection)) { + int id = Integer.valueOf(uri.getLastPathSegment()); + rowsUpdated = sqlDB.update(TABLE_NAME, + contentValues, + COLUMN_ID + " = ?", + new String[]{String.valueOf(id)}); + } else { + throw new IllegalArgumentException( + "Parameter `selection` should be empty when updating an ID"); + } + break; + default: + throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType); + } + getContext().getContentResolver().notifyChange(uri, null); + return rowsUpdated; + } +} + diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java new file mode 100644 index 0000000000..44ae056aba --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java @@ -0,0 +1,156 @@ +package fr.free.nrw.commons.explore.recent_searches; + +import android.content.ContentProviderClient; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.RemoteException; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; + +import fr.free.nrw.commons.category.Category; +import fr.free.nrw.commons.category.CategoryContentProvider; + +public class RecentSearchesDao { + + private final Provider clientProvider; + + @Inject + public RecentSearchesDao(@Named("category") Provider clientProvider) { + this.clientProvider = clientProvider; + } + + public void save(Category category) { + ContentProviderClient db = clientProvider.get(); + try { + if (category.getContentUri() == null) { + category.setContentUri(db.insert(CategoryContentProvider.BASE_URI, toContentValues(category))); + } else { + db.update(category.getContentUri(), toContentValues(category), null, null); + } + } catch (RemoteException e) { + throw new RuntimeException(e); + } finally { + db.release(); + } + } + + /** + * Retrieve recently-used categories, ordered by descending date. + * + * @return a list containing recent categories + */ + @NonNull + List recentCategories(int limit) { + List items = new ArrayList<>(); + Cursor cursor = null; + ContentProviderClient db = clientProvider.get(); + try { + cursor = db.query( + CategoryContentProvider.BASE_URI, + Table.ALL_FIELDS, + null, + new String[]{}, + Table.COLUMN_LAST_USED + " DESC"); + // fixme add a limit on the original query instead of falling out of the loop? + while (cursor != null && cursor.moveToNext() + && cursor.getPosition() < limit) { + items.add(fromCursor(cursor).getName()); + } + } catch (RemoteException e) { + throw new RuntimeException(e); + } finally { + if (cursor != null) { + cursor.close(); + } + db.release(); + } + return items; + } + + @NonNull + Category fromCursor(Cursor cursor) { + // Hardcoding column positions! + return new Category( + CategoryContentProvider.uriForId(cursor.getInt(cursor.getColumnIndex(Table.COLUMN_ID))), + cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)), + new Date(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_LAST_USED))), + cursor.getInt(cursor.getColumnIndex(Table.COLUMN_TIMES_USED)) + ); + } + + private ContentValues toContentValues(Category category) { + ContentValues cv = new ContentValues(); + cv.put(RecentSearchesDao.Table.COLUMN_NAME, category.getName()); + cv.put(RecentSearchesDao.Table.COLUMN_LAST_USED, category.getLastUsed().getTime()); + cv.put(RecentSearchesDao.Table.COLUMN_TIMES_USED, category.getTimesUsed()); + return cv; + } + + public static class Table { + public static final String TABLE_NAME = "categories"; + + public static final String COLUMN_ID = "_id"; + static final String COLUMN_NAME = "name"; + static final String COLUMN_LAST_USED = "last_used"; + static final String COLUMN_TIMES_USED = "times_used"; + + // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES. + public static final String[] ALL_FIELDS = { + COLUMN_ID, + COLUMN_NAME, + COLUMN_LAST_USED, + COLUMN_TIMES_USED + }; + + static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME; + + static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " (" + + COLUMN_ID + " INTEGER PRIMARY KEY," + + COLUMN_NAME + " STRING," + + COLUMN_LAST_USED + " INTEGER," + + COLUMN_TIMES_USED + " INTEGER" + + ");"; + + public static void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_TABLE_STATEMENT); + } + + public static void onDelete(SQLiteDatabase db) { + db.execSQL(DROP_TABLE_STATEMENT); + onCreate(db); + } + + public static void onUpdate(SQLiteDatabase db, int from, int to) { + if (from == to) { + return; + } + if (from < 4) { + // doesn't exist yet + from++; + onUpdate(db, from, to); + return; + } + if (from == 4) { + // table added in version 5 + onCreate(db); + from++; + onUpdate(db, from, to); + return; + } + if (from == 5) { + from++; + onUpdate(db, from, to); + return; + } + } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java new file mode 100644 index 0000000000..9f27cd1683 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java @@ -0,0 +1,24 @@ +package fr.free.nrw.commons.explore.recent_searches; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import butterknife.ButterKnife; +import fr.free.nrw.commons.R; + + +public class RecentSearchesFragment extends Fragment { + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); + ButterKnife.bind(this, rootView); + return rootView; + } + +} diff --git a/app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..de4a147fe909f8f2b918dd92253ebe46058c58a8 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBx;$MRLn>~)yG;} zdZ*rF&M4Xc$(0>13@&DU-u;oX{c3QbvSwk})7<5mG<0V^4ck{>STbc}CWWt+;x#paYN?#LqOA^;&2F0dV7QORIyP*cgca7}!M>+6g9 o*Zri8^}ZGQoxacCteBbP0l+XkK@GmNc literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fa573b1e39709f32584e76c9d6b7dfdf34b2eca2 GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUtPEQxdkcwMxZye-oFyL{x=%>7H zs_(AAiw9jURQE0@*Y@rC!2kc6vz)HTBo~#xJ6V8Q7|J78^Pj%RpnJI7b&JfpbBqiE zya((BSOp%v1j(X-0`@m;hx6qcZVCk0Ff86C#j`)7`Fh?L7oY1>zgL(&J^LYNIU@tZ ahgx@1PLn>~)y=};K$Uvmwq2E`& zT@_np9KOs66|i;mSIjeYa*JJ~vi_;`Khq2SUnVNP4Dy=0kDXCK!J&bHk%?u6-|ss! zX4mEDWUmp*(DM^svh-ny`>fpuwoCxYatJ7>XEJ7SY?-x_8OVlcAvr0`a{Uzh?wUlp^xtKFtJP(TpBw39^E_GjC+zS3{l|-$ d;a1eXVU2L*ys4jJ;SKaAgQu&X%Q~loCIE+MSReoZ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..10e27028790db44124e7d84e221e573500772758 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>VAS$-aSW-r_4dwb-X;eI*TAwH zJR8-l7P6ZL^={dNRbf7=GNpxU%~5 z?TFoP3d5gOsl_<;zctox^*@_wU9kH`^$we>XLmBNFf=eQ2td60>Lv5F + xmlns:app="http://schemas.android.com/apk/res-auto" + tools:context="fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment"> - - + android:background="?android:windowBackground" + android:orientation="vertical" + android:id="@+id/recent_searches"> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_recent_searches.xml b/app/src/main/res/layout/item_recent_searches.xml new file mode 100644 index 0000000000..abad996a66 --- /dev/null +++ b/app/src/main/res/layout/item_recent_searches.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed7bc96dba..5abdd00cc2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -289,7 +289,5 @@ Set wallpaper Wallpaper set successfully! - - - Hello blank fragment + Recent searches: From b84105eb9b9ad280dbee5afa88cfbf1ccd843fc2 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 4 Jun 2018 21:02:36 +0530 Subject: [PATCH 21/94] Content Provider, RecentSearchesDao added --- app/src/main/AndroidManifest.xml | 7 ++ .../nrw/commons/di/FragmentBuilderModule.java | 4 + .../nrw/commons/explore/SearchActivity.java | 7 ++ .../explore/images/SearchImageFragment.java | 21 ++++- .../explore/recent_searches/RecentSearch.java | 77 ++++++++++++++++++ .../RecentSearchesContentProvider.java | 32 ++++---- .../recent_searches/RecentSearchesDao.java | 80 ++++++++++++------- .../RecentSearchesFragment.java | 27 ++++++- .../main/res/layout/item_recent_searches.xml | 18 ++--- app/src/main/res/values/strings.xml | 1 + 10 files changed, 210 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9b36266cbd..e5c6e69fd3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -171,6 +171,13 @@ android:label="@string/provider_categories" android:syncable="false" /> + + diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index 323093691d..de85a8da78 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -6,6 +6,7 @@ import fr.free.nrw.commons.contributions.ContributionsListFragment; import fr.free.nrw.commons.category.CategoryImagesListFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; +import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; import fr.free.nrw.commons.media.MediaDetailFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.nearby.NearbyListFragment; @@ -55,4 +56,7 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract SearchImageFragment bindBrowseImagesListFragment(); + @ContributesAndroidInjector + abstract RecentSearchesFragment bindRecentSearchesFragment(); + } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 89f6e28e40..6ebab402a2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -13,12 +13,14 @@ import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxTextView; +import java.util.Date; import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.category.Category; import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; @@ -117,6 +119,7 @@ public void unregisterDataSetObserver(DataSetObserver observer) { * @param index item index that should be opened */ public void onSearchImageClicked(int index) { + // search query ViewUtil.hideKeyboard(this.findViewById(R.id.searchBox)); toolbar.setVisibility(View.GONE); setNavigationBaseToolbarVisibility(true); @@ -134,6 +137,7 @@ public void onSearchImageClicked(int index) { mediaDetails.showImage(index); } + @Override public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount() == 1){ @@ -150,4 +154,7 @@ public void onBackPressed() { super.onBackPressed(); } + public void updateText(String query) { + etSearchKeyword.setText(query); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 8e5d486e74..fc4bc1c605 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -12,6 +12,7 @@ import android.widget.TextView; import com.pedrogomez.renderers.RVRendererAdapter; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -20,8 +21,10 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.category.Category; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; +import fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -49,8 +52,8 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { TextView imagesNotFoundView; String query; - @Inject - MediaWikiApi mwApi; + @Inject RecentSearchesDao recentSearchesDao; + @Inject MediaWikiApi mwApi; @Inject @Named("default_preferences") SharedPreferences prefs; private RVRendererAdapter imagesAdapter; @@ -60,8 +63,22 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { int index = queryList.indexOf(item); ((SearchActivity)getContext()).onSearchImageClicked(index); //TODO : Add images to recently searched images db table + saveQuery(query); }); + private void saveQuery(String query) { + Category category = recentSearchesDao.find(query); + + // Newly used category... + if (category == null) { + category = new Category(null, query, new Date(), 0); + } + + category.incTimesUsed(); + recentSearchesDao.save(category); + } + + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java new file mode 100644 index 0000000000..1d5ee3064d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java @@ -0,0 +1,77 @@ +package fr.free.nrw.commons.explore.recent_searches; + +import android.net.Uri; + +import java.util.Date; + +/** + * Represents a category + */ +public class RecentSearch { + private Uri contentUri; + private String name; + private Date lastUsed; + + public RecentSearch() { + } + + public RecentSearch(Uri contentUri, String name, Date lastUsed) { + this.contentUri = contentUri; + this.name = name; + this.lastUsed = lastUsed; + } + + /** + * Gets name + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Modifies name + * + * @param name Category name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets last used date + * + * @return Last used date + */ + public Date getLastUsed() { + // warning: Date objects are mutable. + return (Date)lastUsed.clone(); + } + + /** + * Generates new last used date + */ + private void touch() { + lastUsed = new Date(); + } + + /** + * Gets the content URI for this category + * + * @return content URI + */ + public Uri getContentUri() { + return contentUri; + } + + /** + * Modifies the content URI - marking this category as already saved in the database + * + * @param contentUri the content URI + */ + public void setContentUri(Uri contentUri) { + this.contentUri = contentUri; + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java index 8b95b57e26..a3a28b77e7 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java @@ -16,25 +16,23 @@ import timber.log.Timber; import static android.content.UriMatcher.NO_MATCH; -import static fr.free.nrw.commons.category.CategoryDao.Table.ALL_FIELDS; -import static fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_ID; -import static fr.free.nrw.commons.category.CategoryDao.Table.TABLE_NAME; +import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao.Table.ALL_FIELDS; +import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao.Table.COLUMN_ID; +import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao.Table.TABLE_NAME; public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { - public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider"; + public static final String AUTHORITY = "fr.free.nrw.commons.explore.recent_searches.contentprovider"; // For URI matcher - private static final int CATEGORIES = 1; - private static final int CATEGORIES_ID = 2; - private static final String BASE_PATH = "categories"; - + private static final int RECENT_SEARCHES = 1; + private static final int RECENT_SEARCHES_ID = 2; + private static final String BASE_PATH = "recent_searches"; public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); - private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH); static { - uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES); - uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID); + uriMatcher.addURI(AUTHORITY, BASE_PATH, RECENT_SEARCHES); + uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", RECENT_SEARCHES_ID); } public static Uri uriForId(int id) { @@ -56,11 +54,11 @@ public Cursor query(@NonNull Uri uri, String[] projection, String selection, Cursor cursor; switch (uriType) { - case CATEGORIES: + case RECENT_SEARCHES: cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); break; - case CATEGORIES_ID: + case RECENT_SEARCHES_ID: cursor = queryBuilder.query(db, ALL_FIELDS, "_id = ?", @@ -91,7 +89,7 @@ public Uri insert(@NonNull Uri uri, ContentValues contentValues) { SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); long id; switch (uriType) { - case CATEGORIES: + case RECENT_SEARCHES: id = sqlDB.insert(TABLE_NAME, null, contentValues); break; default: @@ -109,12 +107,12 @@ public int delete(@NonNull Uri uri, String s, String[] strings) { @SuppressWarnings("ConstantConditions") @Override public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { - Timber.d("Hello, bulk insert! (CategoryContentProvider)"); + Timber.d("Hello, bulk insert! (RecentSearchesContentProvider)"); int uriType = uriMatcher.match(uri); SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); sqlDB.beginTransaction(); switch (uriType) { - case CATEGORIES: + case RECENT_SEARCHES: for (ContentValues value : values) { Timber.d("Inserting! %s", value); sqlDB.insert(TABLE_NAME, null, value); @@ -146,7 +144,7 @@ outside world (exported="false"). Even then, we should make sure to sanitize SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); int rowsUpdated; switch (uriType) { - case CATEGORIES_ID: + case RECENT_SEARCHES_ID: if (TextUtils.isEmpty(selection)) { int id = Integer.valueOf(uri.getLastPathSegment()); rowsUpdated = sqlDB.update(TABLE_NAME, diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java index 44ae056aba..43b71b8d2f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java @@ -16,8 +16,6 @@ import javax.inject.Named; import javax.inject.Provider; -import fr.free.nrw.commons.category.Category; -import fr.free.nrw.commons.category.CategoryContentProvider; public class RecentSearchesDao { @@ -28,13 +26,13 @@ public RecentSearchesDao(@Named("category") Provider clie this.clientProvider = clientProvider; } - public void save(Category category) { + public void save(RecentSearch recentSearch) { ContentProviderClient db = clientProvider.get(); try { - if (category.getContentUri() == null) { - category.setContentUri(db.insert(CategoryContentProvider.BASE_URI, toContentValues(category))); + if (recentSearch.getContentUri() == null) { + recentSearch.setContentUri(db.insert(RecentSearchesContentProvider.BASE_URI, toContentValues(recentSearch))); } else { - db.update(category.getContentUri(), toContentValues(category), null, null); + db.update(recentSearch.getContentUri(), toContentValues(recentSearch), null, null); } } catch (RemoteException e) { throw new RuntimeException(e); @@ -44,25 +42,52 @@ public void save(Category category) { } /** - * Retrieve recently-used categories, ordered by descending date. + * Find persisted search query in database, based on its name. * - * @return a list containing recent categories + * @param name Search query name + * @return recently searched query from database, or null if not found */ - @NonNull - List recentCategories(int limit) { - List items = new ArrayList<>(); + @Nullable + public RecentSearch find(String name) { Cursor cursor = null; ContentProviderClient db = clientProvider.get(); try { cursor = db.query( - CategoryContentProvider.BASE_URI, + RecentSearchesContentProvider.BASE_URI, Table.ALL_FIELDS, - null, - new String[]{}, - Table.COLUMN_LAST_USED + " DESC"); + Table.COLUMN_NAME + "=?", + new String[]{name}, + null); + if (cursor != null && cursor.moveToFirst()) { + return fromCursor(cursor); + } + } catch (RemoteException e) { + // This feels lazy, but to hell with checked exceptions. :) + throw new RuntimeException(e); + } finally { + if (cursor != null) { + cursor.close(); + } + db.release(); + } + return null; + } + + /** + * Retrieve recently-searched queries, ordered by descending date. + * + * @return a list containing recent searches + */ + @NonNull + public List recentSearches(int limit) { + List items = new ArrayList<>(); + Cursor cursor = null; + ContentProviderClient db = clientProvider.get(); + try { + cursor = db.query( RecentSearchesContentProvider.BASE_URI, Table.ALL_FIELDS, + null, new String[]{}, Table.COLUMN_LAST_USED + " DESC"); // fixme add a limit on the original query instead of falling out of the loop? - while (cursor != null && cursor.moveToNext() - && cursor.getPosition() < limit) { + while (cursor != null && cursor.moveToNext() && cursor.getPosition() < limit) { items.add(fromCursor(cursor).getName()); } } catch (RemoteException e) { @@ -77,38 +102,34 @@ List recentCategories(int limit) { } @NonNull - Category fromCursor(Cursor cursor) { + RecentSearch fromCursor(Cursor cursor) { // Hardcoding column positions! - return new Category( - CategoryContentProvider.uriForId(cursor.getInt(cursor.getColumnIndex(Table.COLUMN_ID))), + return new RecentSearch( + RecentSearchesContentProvider.uriForId(cursor.getInt(cursor.getColumnIndex(Table.COLUMN_ID))), cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)), - new Date(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_LAST_USED))), - cursor.getInt(cursor.getColumnIndex(Table.COLUMN_TIMES_USED)) + new Date(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_LAST_USED))) ); } - private ContentValues toContentValues(Category category) { + private ContentValues toContentValues(RecentSearch recentSearch) { ContentValues cv = new ContentValues(); - cv.put(RecentSearchesDao.Table.COLUMN_NAME, category.getName()); - cv.put(RecentSearchesDao.Table.COLUMN_LAST_USED, category.getLastUsed().getTime()); - cv.put(RecentSearchesDao.Table.COLUMN_TIMES_USED, category.getTimesUsed()); + cv.put(RecentSearchesDao.Table.COLUMN_NAME, recentSearch.getName()); + cv.put(RecentSearchesDao.Table.COLUMN_LAST_USED, recentSearch.getLastUsed().getTime()); return cv; } public static class Table { - public static final String TABLE_NAME = "categories"; + public static final String TABLE_NAME = "recent_searches"; public static final String COLUMN_ID = "_id"; static final String COLUMN_NAME = "name"; static final String COLUMN_LAST_USED = "last_used"; - static final String COLUMN_TIMES_USED = "times_used"; // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES. public static final String[] ALL_FIELDS = { COLUMN_ID, COLUMN_NAME, COLUMN_LAST_USED, - COLUMN_TIMES_USED }; static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME; @@ -117,7 +138,6 @@ public static class Table { + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_NAME + " STRING," + COLUMN_LAST_USED + " INTEGER," - + COLUMN_TIMES_USED + " INTEGER" + ");"; public static void onCreate(SQLiteDatabase db) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java index 9f27cd1683..c9fe49e7ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java @@ -1,24 +1,45 @@ package fr.free.nrw.commons.explore.recent_searches; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Toast; +import java.util.List; + +import javax.inject.Inject; + +import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.explore.SearchActivity; -public class RecentSearchesFragment extends Fragment { - +public class RecentSearchesFragment extends CommonsDaggerSupportFragment { + @Inject RecentSearchesDao recentSearchesDao; + @BindView(R.id.recent_searches_list) ListView recentSearchesList; + List recentSearches; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); ButterKnife.bind(this, rootView); + + recentSearches = recentSearchesDao.recentSearches(10); + ArrayAdapter adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches,recentSearchesDao.recentSearches(10)); + recentSearchesList.setAdapter(adapter); + recentSearchesList.setOnItemClickListener((parent, view, position, id) -> { + ((SearchActivity)getContext()).updateText(recentSearches.get(position)); + Toast.makeText(getContext(),recentSearches.get(position),Toast.LENGTH_SHORT).show(); + }); + adapter.notifyDataSetChanged(); return rootView; } + } diff --git a/app/src/main/res/layout/item_recent_searches.xml b/app/src/main/res/layout/item_recent_searches.xml index abad996a66..6b0b0b68e7 100644 --- a/app/src/main/res/layout/item_recent_searches.xml +++ b/app/src/main/res/layout/item_recent_searches.xml @@ -1,15 +1,9 @@ - - - + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:padding="@dimen/standard_gap" + android:text="@string/search_commons" + xmlns:android="http://schemas.android.com/apk/res/android" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5abdd00cc2..759fe808e9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -290,4 +290,5 @@ Set wallpaper Wallpaper set successfully! Recent searches: + Recently searched queries From ae0944584770d2f1bd9de0dfe43f07db207fbe57 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 5 Jun 2018 10:30:48 +0530 Subject: [PATCH 22/94] Database version changed to 7 and added methods for find, save ,.. --- .../contributions/ContributionDao.java | 2 +- .../free/nrw/commons/data/DBOpenHelper.java | 5 ++- .../free/nrw/commons/delete/DeleteTask.java | 4 +-- .../commons/di/CommonsApplicationModule.java | 8 +++++ .../di/ContentProviderBuilderModule.java | 4 +++ .../explore/images/SearchImageFragment.java | 13 ++++--- .../explore/recent_searches/RecentSearch.java | 26 ++------------ .../RecentSearchesContentProvider.java | 8 ++--- .../recent_searches/RecentSearchesDao.java | 35 +++++++++++++++---- .../RecentSearchesFragment.java | 34 +++++++++++++----- .../commons/media/MediaDetailFragment.java | 2 +- app/src/main/res/values/strings.xml | 2 ++ 12 files changed, 89 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java index 079cf64778..6ee89d783c 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java @@ -75,7 +75,7 @@ public void delete(Contribution contribution) { try { if (contribution.getContentUri() == null) { // noooo - throw new RuntimeException("tried to delete item with no content URI"); + throw new RuntimeException("tried to deleteAll item with no content URI"); } else { db.delete(contribution.getContentUri(), null, null); } diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java index 35305c5ba9..b0f0f78921 100644 --- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java @@ -6,12 +6,13 @@ import fr.free.nrw.commons.category.CategoryDao; import fr.free.nrw.commons.contributions.ContributionDao; +import fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao; import fr.free.nrw.commons.modifications.ModifierSequenceDao; public class DBOpenHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "commons.db"; - private static final int DATABASE_VERSION = 6; + private static final int DATABASE_VERSION = 7; /** * Do not use directly - @Inject an instance where it's needed and let @@ -26,6 +27,7 @@ public void onCreate(SQLiteDatabase sqLiteDatabase) { ContributionDao.Table.onCreate(sqLiteDatabase); ModifierSequenceDao.Table.onCreate(sqLiteDatabase); CategoryDao.Table.onCreate(sqLiteDatabase); + RecentSearchesDao.Table.onCreate(sqLiteDatabase); } @Override @@ -33,5 +35,6 @@ public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) { ContributionDao.Table.onUpdate(sqLiteDatabase, from, to); ModifierSequenceDao.Table.onUpdate(sqLiteDatabase, from, to); CategoryDao.Table.onUpdate(sqLiteDatabase, from, to); + RecentSearchesDao.Table.onUpdate(sqLiteDatabase, from, to); } } diff --git a/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java b/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java index 37b9a7a828..1716fd0b3f 100644 --- a/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java +++ b/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java @@ -70,7 +70,7 @@ protected Boolean doInBackground(Void ...voids) { mwApi.setAuthCookie(authCookie); Calendar calendar = Calendar.getInstance(); - String fileDeleteString = "{{delete|reason=" + reason + + String fileDeleteString = "{{deleteAll|reason=" + reason + "|subpage=" +media.getFilename() + "|day=" + calendar.get(Calendar.DAY_OF_MONTH) + "|month=" + calendar.getDisplayName(Calendar.MONTH,Calendar.LONG, Locale.getDefault()) + @@ -129,7 +129,7 @@ protected void onProgressUpdate (Integer... values){ message = "Getting token"; break; case 1: - message = "Adding delete message to file"; + message = "Adding deleteAll message to file"; break; case 2: message = "Creating Delete requests sub-page"; diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index f4a77c4495..d38090b011 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -21,6 +21,7 @@ import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; +import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesContentProvider.RECENT_SEARCH_AUTHORITY; import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MODIFICATIONS_AUTHORITY; @Module @@ -50,6 +51,13 @@ public ContentProviderClient provideCategoryContentProviderClient(Context contex return context.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY); } + @Provides + @Named("recent_search") + public ContentProviderClient provideRecentSearchContentProviderClient(Context context) { + return context.getContentResolver().acquireContentProviderClient(RECENT_SEARCH_AUTHORITY); + } + + @Provides @Named("contribution") public ContentProviderClient provideContributionContentProviderClient(Context context) { diff --git a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java index f18c331c51..95c9a81ac9 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java @@ -4,6 +4,7 @@ import dagger.android.ContributesAndroidInjector; import fr.free.nrw.commons.category.CategoryContentProvider; import fr.free.nrw.commons.contributions.ContributionsContentProvider; +import fr.free.nrw.commons.explore.recent_searches.RecentSearchesContentProvider; import fr.free.nrw.commons.modifications.ModificationsContentProvider; @Module @@ -19,4 +20,7 @@ public abstract class ContentProviderBuilderModule { @ContributesAndroidInjector abstract CategoryContentProvider bindCategoryContentProvider(); + @ContributesAndroidInjector + abstract RecentSearchesContentProvider bindRecentSearchesContentProvider(); + } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index fc4bc1c605..24fa53a065 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -21,9 +21,9 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.category.Category; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; +import fr.free.nrw.commons.explore.recent_searches.RecentSearch; import fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.NetworkUtils; @@ -67,15 +67,14 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { }); private void saveQuery(String query) { - Category category = recentSearchesDao.find(query); + RecentSearch recentSearch = recentSearchesDao.find(query); - // Newly used category... - if (category == null) { - category = new Category(null, query, new Date(), 0); + // Newly searched query... + if (recentSearch == null) { + recentSearch = new RecentSearch(null, query, new Date()); } + recentSearchesDao.save(recentSearch); - category.incTimesUsed(); - recentSearchesDao.save(category); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java index 1d5ee3064d..8092317116 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java @@ -1,20 +1,16 @@ package fr.free.nrw.commons.explore.recent_searches; import android.net.Uri; - import java.util.Date; /** - * Represents a category + * Represents a recently searched query */ public class RecentSearch { private Uri contentUri; private String name; private Date lastUsed; - public RecentSearch() { - } - public RecentSearch(Uri contentUri, String name, Date lastUsed) { this.contentUri = contentUri; this.name = name; @@ -30,15 +26,6 @@ public String getName() { return name; } - /** - * Modifies name - * - * @param name Category name - */ - public void setName(String name) { - this.name = name; - } - /** * Gets last used date * @@ -50,14 +37,7 @@ public Date getLastUsed() { } /** - * Generates new last used date - */ - private void touch() { - lastUsed = new Date(); - } - - /** - * Gets the content URI for this category + * Gets the content URI for this query * * @return content URI */ @@ -66,7 +46,7 @@ public Uri getContentUri() { } /** - * Modifies the content URI - marking this category as already saved in the database + * Modifies the content URI - marking this query as already saved in the database * * @param contentUri the content URI */ diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java index a3a28b77e7..4d1fa7032e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java @@ -22,17 +22,17 @@ public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { - public static final String AUTHORITY = "fr.free.nrw.commons.explore.recent_searches.contentprovider"; + public static final String RECENT_SEARCH_AUTHORITY = "fr.free.nrw.commons.explore.recent_searches.contentprovider"; // For URI matcher private static final int RECENT_SEARCHES = 1; private static final int RECENT_SEARCHES_ID = 2; private static final String BASE_PATH = "recent_searches"; - public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); + public static final Uri BASE_URI = Uri.parse("content://" + RECENT_SEARCH_AUTHORITY + "/" + BASE_PATH); private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH); static { - uriMatcher.addURI(AUTHORITY, BASE_PATH, RECENT_SEARCHES); - uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", RECENT_SEARCHES_ID); + uriMatcher.addURI(RECENT_SEARCH_AUTHORITY, BASE_PATH, RECENT_SEARCHES); + uriMatcher.addURI(RECENT_SEARCH_AUTHORITY, BASE_PATH + "/#", RECENT_SEARCHES_ID); } public static Uri uriForId(int id) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java index 43b71b8d2f..19d0f6a65c 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java @@ -7,6 +7,7 @@ import android.os.RemoteException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; import java.util.ArrayList; import java.util.Date; @@ -22,7 +23,7 @@ public class RecentSearchesDao { private final Provider clientProvider; @Inject - public RecentSearchesDao(@Named("category") Provider clientProvider) { + public RecentSearchesDao(@Named("recent_search") Provider clientProvider) { this.clientProvider = clientProvider; } @@ -41,6 +42,27 @@ public void save(RecentSearch recentSearch) { } } + public void deleteAll(List recentSearchesStringList) { + ContentProviderClient db = clientProvider.get(); + Log.d("qwertyui",recentSearchesStringList.size()+"Size"); + for (String recentSearchName : recentSearchesStringList) { + Log.d("qweName",recentSearchName+"Name"); + try { + RecentSearch recentSearch = find(recentSearchName); + if (recentSearch.getContentUri() == null) { + throw new RuntimeException("tried to delete item with no content URI"); + } else { + db.delete(recentSearch.getContentUri(), null, null); + } + } catch (RemoteException e) { + throw new RuntimeException(e); + } finally { + db.release(); + } + } + } + + /** * Find persisted search query in database, based on its name. * @@ -120,7 +142,6 @@ private ContentValues toContentValues(RecentSearch recentSearch) { public static class Table { public static final String TABLE_NAME = "recent_searches"; - public static final String COLUMN_ID = "_id"; static final String COLUMN_NAME = "name"; static final String COLUMN_LAST_USED = "last_used"; @@ -137,7 +158,7 @@ public static class Table { static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " (" + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_NAME + " STRING," - + COLUMN_LAST_USED + " INTEGER," + + COLUMN_LAST_USED + " INTEGER" + ");"; public static void onCreate(SQLiteDatabase db) { @@ -153,20 +174,20 @@ public static void onUpdate(SQLiteDatabase db, int from, int to) { if (from == to) { return; } - if (from < 4) { + if (from < 6) { // doesn't exist yet from++; onUpdate(db, from, to); return; } - if (from == 4) { - // table added in version 5 + if (from == 6) { + // table added in version 7 onCreate(db); from++; onUpdate(db, from, to); return; } - if (from == 5) { + if (from == 7) { from++; onUpdate(db, from, to); return; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java index c9fe49e7ae..e1ee50f9ac 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java @@ -1,10 +1,12 @@ package fr.free.nrw.commons.explore.recent_searches; import android.os.Bundle; +import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.ImageView; import android.widget.ListView; import android.widget.Toast; @@ -23,23 +25,39 @@ public class RecentSearchesFragment extends CommonsDaggerSupportFragment { @Inject RecentSearchesDao recentSearchesDao; @BindView(R.id.recent_searches_list) ListView recentSearchesList; List recentSearches; - + ArrayAdapter adapter; + @BindView(R.id.recent_searches_delete_button) ImageView recent_searches_delete_button; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); ButterKnife.bind(this, rootView); - recentSearches = recentSearchesDao.recentSearches(10); - ArrayAdapter adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches,recentSearchesDao.recentSearches(10)); - recentSearchesList.setAdapter(adapter); - recentSearchesList.setOnItemClickListener((parent, view, position, id) -> { - ((SearchActivity)getContext()).updateText(recentSearches.get(position)); - Toast.makeText(getContext(),recentSearches.get(position),Toast.LENGTH_SHORT).show(); + recent_searches_delete_button.setOnClickListener(v -> { + new AlertDialog.Builder(getContext()) + .setMessage(getString(R.string.delete_recent_searches_dialog)) + .setPositiveButton("YES", (dialog, which) -> { + recentSearchesDao.deleteAll(recentSearches); + Toast.makeText(getContext(),getString(R.string.search_history_deleted),Toast.LENGTH_SHORT).show(); + dialog.dismiss(); + }) + .setNegativeButton("NO", null) + .create() + .show(); + }); + adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches,recentSearches); + recentSearchesList.setAdapter(adapter); + recentSearchesList.setOnItemClickListener((parent, view, position, id) -> ( + (SearchActivity)getContext()).updateText(recentSearches.get(position))); adapter.notifyDataSetChanged(); return rootView; } - + @Override + public void onResume() { + recentSearches = recentSearchesDao.recentSearches(10); + adapter.notifyDataSetChanged(); + super.onResume(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index fcfb1f4d95..da19d413b8 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -347,7 +347,7 @@ public void onMediaDetailCoordinatesClicked(){ @OnClick(R.id.nominateDeletion) public void onDeleteButtonClicked(){ //Reviewer correct me if i have misunderstood something over here - //But how does this if (delete.getVisibility() == View.VISIBLE) { + //But how does this if (deleteAll.getVisibility() == View.VISIBLE) { // enableDeleteButton(true); makes sense ? AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); alert.setMessage("Why should this file be deleted?"); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 759fe808e9..5348b73fed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -291,4 +291,6 @@ Wallpaper set successfully! Recent searches: Recently searched queries + Are you sure you want to clear your search history? + Search history deleted From 948ce059b76efece86928fd003fc97199573ecde Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 5 Jun 2018 10:32:48 +0530 Subject: [PATCH 23/94] Delete icon deleted --- .../recent_searches/RecentSearchesDao.java | 21 ------------------ .../RecentSearchesFragment.java | 14 ------------ .../drawable-hdpi/ic_delete_grey_700_24dp.png | Bin 195 -> 0 bytes .../drawable-mdpi/ic_delete_grey_700_24dp.png | Bin 132 -> 0 bytes .../ic_delete_grey_700_24dp.png | Bin 194 -> 0 bytes .../ic_delete_grey_700_24dp.png | Bin 276 -> 0 bytes .../ic_delete_grey_700_24dp.png | Bin 355 -> 0 bytes .../res/layout/fragment_search_history.xml | 11 --------- 8 files changed, 46 deletions(-) delete mode 100644 app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_delete_grey_700_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_delete_grey_700_24dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java index 19d0f6a65c..89c6ef8fb1 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java @@ -42,27 +42,6 @@ public void save(RecentSearch recentSearch) { } } - public void deleteAll(List recentSearchesStringList) { - ContentProviderClient db = clientProvider.get(); - Log.d("qwertyui",recentSearchesStringList.size()+"Size"); - for (String recentSearchName : recentSearchesStringList) { - Log.d("qweName",recentSearchName+"Name"); - try { - RecentSearch recentSearch = find(recentSearchName); - if (recentSearch.getContentUri() == null) { - throw new RuntimeException("tried to delete item with no content URI"); - } else { - db.delete(recentSearch.getContentUri(), null, null); - } - } catch (RemoteException e) { - throw new RuntimeException(e); - } finally { - db.release(); - } - } - } - - /** * Find persisted search query in database, based on its name. * diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java index e1ee50f9ac..3bce256eee 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java @@ -26,26 +26,12 @@ public class RecentSearchesFragment extends CommonsDaggerSupportFragment { @BindView(R.id.recent_searches_list) ListView recentSearchesList; List recentSearches; ArrayAdapter adapter; - @BindView(R.id.recent_searches_delete_button) ImageView recent_searches_delete_button; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); ButterKnife.bind(this, rootView); recentSearches = recentSearchesDao.recentSearches(10); - recent_searches_delete_button.setOnClickListener(v -> { - new AlertDialog.Builder(getContext()) - .setMessage(getString(R.string.delete_recent_searches_dialog)) - .setPositiveButton("YES", (dialog, which) -> { - recentSearchesDao.deleteAll(recentSearches); - Toast.makeText(getContext(),getString(R.string.search_history_deleted),Toast.LENGTH_SHORT).show(); - dialog.dismiss(); - }) - .setNegativeButton("NO", null) - .create() - .show(); - - }); adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches,recentSearches); recentSearchesList.setAdapter(adapter); recentSearchesList.setOnItemClickListener((parent, view, position, id) -> ( diff --git a/app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png deleted file mode 100644 index de4a147fe909f8f2b918dd92253ebe46058c58a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBx;$MRLn>~)yG;} zdZ*rF&M4Xc$(0>13@&DU-u;oX{c3QbvSwk})7<5mG<0V^4ck{>STbc}CWWt+;x#paYN?#LqOA^;&2F0dV7QORIyP*cgca7}!M>+6g9 o*Zri8^}ZGQoxacCteBbP0l+XkK@GmNc diff --git a/app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png deleted file mode 100644 index fa573b1e39709f32584e76c9d6b7dfdf34b2eca2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUtPEQxdkcwMxZye-oFyL{x=%>7H zs_(AAiw9jURQE0@*Y@rC!2kc6vz)HTBo~#xJ6V8Q7|J78^Pj%RpnJI7b&JfpbBqiE zya((BSOp%v1j(X-0`@m;hx6qcZVCk0Ff86C#j`)7`Fh?L7oY1>zgL(&J^LYNIU@tZ ahgx@1PLn>~)y=};K$Uvmwq2E`& zT@_np9KOs66|i;mSIjeYa*JJ~vi_;`Khq2SUnVNP4Dy=0kDXCK!J&bHk%?u6-|ss! zX4mEDWUmp*(DM^svh-ny`>fpuwoCxYatJ7>XEJ7SY?-x_8OVlcAvr0`a{Uzh?wUlp^xtKFtJP(TpBw39^E_GjC+zS3{l|-$ d;a1eXVU2L*ys4jJ;SKaAgQu&X%Q~loCIE+MSReoZ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png deleted file mode 100644 index 10e27028790db44124e7d84e221e573500772758..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>VAS$-aSW-r_4dwb-X;eI*TAwH zJR8-l7P6ZL^={dNRbf7=GNpxU%~5 z?TFoP3d5gOsl_<;zctox^*@_wU9kH`^$we>XLmBNFf=eQ2td60>Lv5F - - Date: Tue, 5 Jun 2018 10:43:58 +0530 Subject: [PATCH 24/94] Reverted changes in gradle files --- app/build.gradle | 8 +++----- .../free/nrw/commons/contributions/ContributionDao.java | 2 +- .../main/java/fr/free/nrw/commons/delete/DeleteTask.java | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e845cdfb63..9fd7f2f627 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ dependencies { implementation 'info.debatty:java-string-similarity:0.24' implementation 'com.borjabravo:readmoretextview:2.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' - implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar') { - transitive = true + implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar') { + transitive=true } implementation "com.github.deano2390:MaterialShowcaseView:1.2.0" @@ -80,7 +80,6 @@ dependencies { debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" testImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" - implementation 'com.android.support:support-v4:27.1.1' } android { @@ -120,8 +119,7 @@ android { buildTypes { release { - minifyEnabled false - // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient. + minifyEnabled false // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt' } debug { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java index 6ee89d783c..079cf64778 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java @@ -75,7 +75,7 @@ public void delete(Contribution contribution) { try { if (contribution.getContentUri() == null) { // noooo - throw new RuntimeException("tried to deleteAll item with no content URI"); + throw new RuntimeException("tried to delete item with no content URI"); } else { db.delete(contribution.getContentUri(), null, null); } diff --git a/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java b/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java index 1716fd0b3f..37b9a7a828 100644 --- a/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java +++ b/app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java @@ -70,7 +70,7 @@ protected Boolean doInBackground(Void ...voids) { mwApi.setAuthCookie(authCookie); Calendar calendar = Calendar.getInstance(); - String fileDeleteString = "{{deleteAll|reason=" + reason + + String fileDeleteString = "{{delete|reason=" + reason + "|subpage=" +media.getFilename() + "|day=" + calendar.get(Calendar.DAY_OF_MONTH) + "|month=" + calendar.getDisplayName(Calendar.MONTH,Calendar.LONG, Locale.getDefault()) + @@ -129,7 +129,7 @@ protected void onProgressUpdate (Integer... values){ message = "Getting token"; break; case 1: - message = "Adding deleteAll message to file"; + message = "Adding delete message to file"; break; case 2: message = "Creating Delete requests sub-page"; From 177f8a8d69a9eea6f0955838c4b48355b5f58fa5 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 5 Jun 2018 10:45:26 +0530 Subject: [PATCH 25/94] Reverted changes in gradle files 2 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 9fd7f2f627..fad3eccd8c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,7 @@ dependencies { implementation 'info.debatty:java-string-similarity:0.24' implementation 'com.borjabravo:readmoretextview:2.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' - implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar') { + implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){ transitive=true } From 8c68944a705dc8c559f29695edfba5a3393a722d Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 5 Jun 2018 10:48:36 +0530 Subject: [PATCH 26/94] Optimized Improts --- .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 4 +--- .../commons/explore/recent_searches/RecentSearchesDao.java | 1 - .../explore/recent_searches/RecentSearchesFragment.java | 3 --- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 6ebab402a2..08652fb8ad 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -13,16 +13,14 @@ import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxTextView; -import java.util.Date; import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.category.Category; -import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; +import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.ViewUtil; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java index 89c6ef8fb1..231e04d318 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java @@ -7,7 +7,6 @@ import android.os.RemoteException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.util.Log; import java.util.ArrayList; import java.util.Date; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java index 3bce256eee..a54f85f6f6 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java @@ -1,14 +1,11 @@ package fr.free.nrw.commons.explore.recent_searches; import android.os.Bundle; -import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.ImageView; import android.widget.ListView; -import android.widget.Toast; import java.util.List; From 19378e31046277286f57b8c022ea21f46a9ea0d8 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 5 Jun 2018 10:53:23 +0530 Subject: [PATCH 27/94] reverted refractor for container name --- .../free/nrw/commons/category/CategoryImagesActivity.java | 6 +++--- .../java/fr/free/nrw/commons/explore/SearchActivity.java | 7 +++---- .../fr/free/nrw/commons/media/MediaDetailFragment.java | 2 +- app/src/main/res/layout/activity_category_images.xml | 2 +- app/src/main/res/layout/activity_search.xml | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index d292fbd98a..2cb15534c8 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -61,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { supportFragmentManager.addOnBackStackChangedListener(this); if (savedInstanceState != null) { mediaDetails = (MediaDetailPagerFragment) supportFragmentManager - .findFragmentById(R.id.resultsContainer); + .findFragmentById(R.id.fragmentContainer); } requestAuthToken(); @@ -81,7 +81,7 @@ private void setCategoryImagesFragment() { categoryImagesListFragment.setArguments(arguments); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); transaction - .add(R.id.resultsContainer, categoryImagesListFragment) + .add(R.id.fragmentContainer, categoryImagesListFragment) .commit(); } } @@ -107,7 +107,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.resultsContainer, mediaDetails) + .replace(R.id.fragmentContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 08652fb8ad..7431b409da 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -34,7 +34,7 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai @BindView(R.id.toolbar_search) Toolbar toolbar; @BindView(R.id.searchBox) EditText etSearchKeyword; - @BindView(R.id.resultsContainer) FrameLayout resultsContainer; + @BindView(R.id.fragmentContainer) FrameLayout resultsContainer; @BindView(R.id.searchHistoryContainer) FrameLayout searchHistoryContainer; private SearchImageFragment searchImageFragment; private RecentSearchesFragment recentSearchesFragment; @@ -83,7 +83,7 @@ private void setSearchHistoryFragment() { private void setBrowseImagesFragment() { searchImageFragment = new SearchImageFragment(); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction.add(R.id.resultsContainer, searchImageFragment).commit(); + transaction.add(R.id.fragmentContainer, searchImageFragment).commit(); } @@ -117,7 +117,6 @@ public void unregisterDataSetObserver(DataSetObserver observer) { * @param index item index that should be opened */ public void onSearchImageClicked(int index) { - // search query ViewUtil.hideKeyboard(this.findViewById(R.id.searchBox)); toolbar.setVisibility(View.GONE); setNavigationBaseToolbarVisibility(true); @@ -127,7 +126,7 @@ public void onSearchImageClicked(int index) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.resultsContainer, mediaDetails) + .replace(R.id.fragmentContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index da19d413b8..fcfb1f4d95 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -347,7 +347,7 @@ public void onMediaDetailCoordinatesClicked(){ @OnClick(R.id.nominateDeletion) public void onDeleteButtonClicked(){ //Reviewer correct me if i have misunderstood something over here - //But how does this if (deleteAll.getVisibility() == View.VISIBLE) { + //But how does this if (delete.getVisibility() == View.VISIBLE) { // enableDeleteButton(true); makes sense ? AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); alert.setMessage("Why should this file be deleted?"); diff --git a/app/src/main/res/layout/activity_category_images.xml b/app/src/main/res/layout/activity_category_images.xml index c1fb5ed1f6..c329e44585 100644 --- a/app/src/main/res/layout/activity_category_images.xml +++ b/app/src/main/res/layout/activity_category_images.xml @@ -17,7 +17,7 @@ diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index e72c206c6e..9ecd5cb4ab 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -45,7 +45,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:id="@+id/resultsContainer" + android:id="@+id/fragmentContainer" android:orientation="horizontal" android:layout_below="@id/toolbar_layout" /> From c48b725e1732054207cb8390911bbb209649f46c Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 10:08:59 +0530 Subject: [PATCH 28/94] Refactored packagename, changed name to query in POJO class. --- app/src/main/AndroidManifest.xml | 4 ++-- .../java/fr/free/nrw/commons/data/DBOpenHelper.java | 2 +- .../nrw/commons/di/CommonsApplicationModule.java | 2 +- .../commons/di/ContentProviderBuilderModule.java | 2 +- .../free/nrw/commons/di/FragmentBuilderModule.java | 2 +- .../fr/free/nrw/commons/explore/SearchActivity.java | 2 +- .../commons/explore/images/SearchImageFragment.java | 4 ++-- .../RecentSearch.java | 13 +++++++------ .../RecentSearchesContentProvider.java | 8 ++++---- .../RecentSearchesDao.java | 6 +++--- .../RecentSearchesFragment.java | 2 +- app/src/main/res/layout/fragment_search_history.xml | 2 +- app/src/main/res/values/strings.xml | 2 -- 13 files changed, 25 insertions(+), 26 deletions(-) rename app/src/main/java/fr/free/nrw/commons/explore/{recent_searches => recentsearches}/RecentSearch.java (78%) rename app/src/main/java/fr/free/nrw/commons/explore/{recent_searches => recentsearches}/RecentSearchesContentProvider.java (94%) rename app/src/main/java/fr/free/nrw/commons/explore/{recent_searches => recentsearches}/RecentSearchesDao.java (97%) rename app/src/main/java/fr/free/nrw/commons/explore/{recent_searches => recentsearches}/RecentSearchesFragment.java (96%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e5c6e69fd3..6272b25852 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -172,8 +172,8 @@ android:syncable="false" /> diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java index b0f0f78921..b019a63031 100644 --- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java @@ -6,7 +6,7 @@ import fr.free.nrw.commons.category.CategoryDao; import fr.free.nrw.commons.contributions.ContributionDao; -import fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao; +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; import fr.free.nrw.commons.modifications.ModifierSequenceDao; public class DBOpenHelper extends SQLiteOpenHelper { diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index d38090b011..02a9f09b46 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -21,7 +21,7 @@ import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; -import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesContentProvider.RECENT_SEARCH_AUTHORITY; +import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.RECENT_SEARCH_AUTHORITY; import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MODIFICATIONS_AUTHORITY; @Module diff --git a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java index 95c9a81ac9..0db0ff7fb7 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java @@ -4,7 +4,7 @@ import dagger.android.ContributesAndroidInjector; import fr.free.nrw.commons.category.CategoryContentProvider; import fr.free.nrw.commons.contributions.ContributionsContentProvider; -import fr.free.nrw.commons.explore.recent_searches.RecentSearchesContentProvider; +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider; import fr.free.nrw.commons.modifications.ModificationsContentProvider; @Module diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index de85a8da78..6df2ea72a2 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -6,7 +6,7 @@ import fr.free.nrw.commons.contributions.ContributionsListFragment; import fr.free.nrw.commons.category.CategoryImagesListFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; -import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment; import fr.free.nrw.commons.media.MediaDetailFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.nearby.NearbyListFragment; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 7431b409da..731d652150 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -20,7 +20,7 @@ import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.explore.images.SearchImageFragment; -import fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment; +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.ViewUtil; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 24fa53a065..03c31c7030 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -23,8 +23,8 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; -import fr.free.nrw.commons.explore.recent_searches.RecentSearch; -import fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao; +import fr.free.nrw.commons.explore.recentsearches.RecentSearch; +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java similarity index 78% rename from app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java rename to app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java index 8092317116..ee3d06f600 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearch.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java @@ -1,19 +1,20 @@ -package fr.free.nrw.commons.explore.recent_searches; +package fr.free.nrw.commons.explore.recentsearches; import android.net.Uri; import java.util.Date; /** * Represents a recently searched query + * query - butterfly */ public class RecentSearch { private Uri contentUri; - private String name; + private String query; private Date lastUsed; - public RecentSearch(Uri contentUri, String name, Date lastUsed) { + public RecentSearch(Uri contentUri, String query, Date lastUsed) { this.contentUri = contentUri; - this.name = name; + this.query = query; this.lastUsed = lastUsed; } @@ -22,8 +23,8 @@ public RecentSearch(Uri contentUri, String name, Date lastUsed) { * * @return name */ - public String getName() { - return name; + public String getQuery() { + return query; } /** diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java similarity index 94% rename from app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java rename to app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java index 4d1fa7032e..3516560181 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java @@ -1,4 +1,4 @@ -package fr.free.nrw.commons.explore.recent_searches; +package fr.free.nrw.commons.explore.recentsearches; import android.content.ContentValues; import android.content.UriMatcher; @@ -16,9 +16,9 @@ import timber.log.Timber; import static android.content.UriMatcher.NO_MATCH; -import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao.Table.ALL_FIELDS; -import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao.Table.COLUMN_ID; -import static fr.free.nrw.commons.explore.recent_searches.RecentSearchesDao.Table.TABLE_NAME; +import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS; +import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID; +import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME; public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java similarity index 97% rename from app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java rename to app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index 231e04d318..21b5272a01 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -1,4 +1,4 @@ -package fr.free.nrw.commons.explore.recent_searches; +package fr.free.nrw.commons.explore.recentsearches; import android.content.ContentProviderClient; import android.content.ContentValues; @@ -88,7 +88,7 @@ public List recentSearches(int limit) { null, new String[]{}, Table.COLUMN_LAST_USED + " DESC"); // fixme add a limit on the original query instead of falling out of the loop? while (cursor != null && cursor.moveToNext() && cursor.getPosition() < limit) { - items.add(fromCursor(cursor).getName()); + items.add(fromCursor(cursor).getQuery()); } } catch (RemoteException e) { throw new RuntimeException(e); @@ -113,7 +113,7 @@ RecentSearch fromCursor(Cursor cursor) { private ContentValues toContentValues(RecentSearch recentSearch) { ContentValues cv = new ContentValues(); - cv.put(RecentSearchesDao.Table.COLUMN_NAME, recentSearch.getName()); + cv.put(RecentSearchesDao.Table.COLUMN_NAME, recentSearch.getQuery()); cv.put(RecentSearchesDao.Table.COLUMN_LAST_USED, recentSearch.getLastUsed().getTime()); return cv; } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java similarity index 96% rename from app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java rename to app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java index a54f85f6f6..795278426c 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recent_searches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java @@ -1,4 +1,4 @@ -package fr.free.nrw.commons.explore.recent_searches; +package fr.free.nrw.commons.explore.recentsearches; import android.os.Bundle; import android.view.LayoutInflater; diff --git a/app/src/main/res/layout/fragment_search_history.xml b/app/src/main/res/layout/fragment_search_history.xml index cf9b8f8cea..55369f95d7 100644 --- a/app/src/main/res/layout/fragment_search_history.xml +++ b/app/src/main/res/layout/fragment_search_history.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - tools:context="fr.free.nrw.commons.explore.recent_searches.RecentSearchesFragment"> + tools:context="fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment"> Wallpaper set successfully! Recent searches: Recently searched queries - Are you sure you want to clear your search history? - Search history deleted From dc4b13a02fa661c9412d09a95a495d729d5d8667 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 10:22:44 +0530 Subject: [PATCH 29/94] Updated lastUsed to lastSearched --- .../commons/di/CommonsApplicationModule.java | 2 +- .../explore/images/SearchImageFragment.java | 1 - .../explore/recentsearches/RecentSearch.java | 21 +++++++++---------- .../RecentSearchesContentProvider.java | 2 +- .../recentsearches/RecentSearchesDao.java | 6 +++--- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 02a9f09b46..84d95a6be2 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -52,7 +52,7 @@ public ContentProviderClient provideCategoryContentProviderClient(Context contex } @Provides - @Named("recent_search") + @Named("recentsearch") public ContentProviderClient provideRecentSearchContentProviderClient(Context context) { return context.getContentResolver().acquireContentProviderClient(RECENT_SEARCH_AUTHORITY); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 03c31c7030..158d3e025e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -62,7 +62,6 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { int index = queryList.indexOf(item); ((SearchActivity)getContext()).onSearchImageClicked(index); - //TODO : Add images to recently searched images db table saveQuery(query); }); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java index ee3d06f600..28833b3c06 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java @@ -5,41 +5,40 @@ /** * Represents a recently searched query - * query - butterfly + * Example - query = "butterfly" */ public class RecentSearch { private Uri contentUri; private String query; - private Date lastUsed; + private Date lastSearched; - public RecentSearch(Uri contentUri, String query, Date lastUsed) { + public RecentSearch(Uri contentUri, String query, Date lastSearched) { this.contentUri = contentUri; this.query = query; - this.lastUsed = lastUsed; + this.lastSearched = lastSearched; } /** - * Gets name + * Gets query name * - * @return name + * @return query name */ public String getQuery() { return query; } /** - * Gets last used date + * Gets last searched date * - * @return Last used date + * @return Last searched date */ - public Date getLastUsed() { + public Date getLastSearched() { // warning: Date objects are mutable. - return (Date)lastUsed.clone(); + return (Date)lastSearched.clone(); } /** * Gets the content URI for this query - * * @return content URI */ public Uri getContentUri() { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java index 3516560181..a0b0669f5a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java @@ -22,7 +22,7 @@ public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { - public static final String RECENT_SEARCH_AUTHORITY = "fr.free.nrw.commons.explore.recent_searches.contentprovider"; + public static final String RECENT_SEARCH_AUTHORITY = "fr.free.nrw.commons.explore.recentsearches.contentprovider"; // For URI matcher private static final int RECENT_SEARCHES = 1; private static final int RECENT_SEARCHES_ID = 2; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index 21b5272a01..fecd474270 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -22,7 +22,7 @@ public class RecentSearchesDao { private final Provider clientProvider; @Inject - public RecentSearchesDao(@Named("recent_search") Provider clientProvider) { + public RecentSearchesDao(@Named("recentsearch") Provider clientProvider) { this.clientProvider = clientProvider; } @@ -44,7 +44,7 @@ public void save(RecentSearch recentSearch) { /** * Find persisted search query in database, based on its name. * - * @param name Search query name + * @param name Search query Ex- "butterfly" * @return recently searched query from database, or null if not found */ @Nullable @@ -114,7 +114,7 @@ RecentSearch fromCursor(Cursor cursor) { private ContentValues toContentValues(RecentSearch recentSearch) { ContentValues cv = new ContentValues(); cv.put(RecentSearchesDao.Table.COLUMN_NAME, recentSearch.getQuery()); - cv.put(RecentSearchesDao.Table.COLUMN_LAST_USED, recentSearch.getLastUsed().getTime()); + cv.put(RecentSearchesDao.Table.COLUMN_LAST_USED, recentSearch.getLastSearched().getTime()); return cv; } From 103395fbb60281935a66173879d2fd71ecb707e3 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 10:30:58 +0530 Subject: [PATCH 30/94] Javadocs updated --- .../nrw/commons/explore/images/SearchImageFragment.java | 1 + .../commons/explore/recentsearches/RecentSearchesDao.java | 6 ++++-- .../explore/recentsearches/RecentSearchesFragment.java | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 158d3e025e..45ebbabb08 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -123,6 +123,7 @@ private void handleSuccess(List mediaList) { progressBar.setVisibility(View.GONE); imagesAdapter.addAll(mediaList); imagesAdapter.notifyDataSetChanged(); + saveQuery(query); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index fecd474270..e9234a5ab8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -43,7 +43,6 @@ public void save(RecentSearch recentSearch) { /** * Find persisted search query in database, based on its name. - * * @param name Search query Ex- "butterfly" * @return recently searched query from database, or null if not found */ @@ -75,7 +74,6 @@ public RecentSearch find(String name) { /** * Retrieve recently-searched queries, ordered by descending date. - * * @return a list containing recent searches */ @NonNull @@ -118,6 +116,10 @@ private ContentValues toContentValues(RecentSearch recentSearch) { return cv; } + /** + * This class contains the database table architechture for recent searches, + * It also contains queries and logic necessary to the create, update, delete this table. + */ public static class Table { public static final String TABLE_NAME = "recent_searches"; public static final String COLUMN_ID = "_id"; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java index 795278426c..ad5bf7ebe2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java @@ -18,11 +18,15 @@ import fr.free.nrw.commons.explore.SearchActivity; +/** + * Displays the recent searches screen. + */ public class RecentSearchesFragment extends CommonsDaggerSupportFragment { @Inject RecentSearchesDao recentSearchesDao; @BindView(R.id.recent_searches_list) ListView recentSearchesList; List recentSearches; ArrayAdapter adapter; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -37,6 +41,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, return rootView; } + /** + * This method is called on back press of activity + * so we are updating the list from database to refresh the recent searches list. + */ @Override public void onResume() { recentSearches = recentSearchesDao.recentSearches(10); From 5fb664c4cbe8dcbc405f393e63527074a5d052e0 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 10:54:47 +0530 Subject: [PATCH 31/94] Check if user has been in search page for 5 seconds if yes then save it to history --- .../commons/explore/images/SearchImageFragment.java | 9 ++++++++- .../commons/explore/recentsearches/RecentSearch.java | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 45ebbabb08..6412eac04a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -3,6 +3,7 @@ import android.content.SharedPreferences; import android.os.Bundle; +import android.os.Handler; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -71,7 +72,10 @@ private void saveQuery(String query) { // Newly searched query... if (recentSearch == null) { recentSearch = new RecentSearch(null, query, new Date()); + }else { + recentSearch.setLastSearched(new Date()); } + recentSearchesDao.save(recentSearch); } @@ -123,7 +127,10 @@ private void handleSuccess(List mediaList) { progressBar.setVisibility(View.GONE); imagesAdapter.addAll(mediaList); imagesAdapter.notifyDataSetChanged(); - saveQuery(query); + + // check if user is waiting for 5 seconds if yes then save search query to history. + Handler handler = new Handler(); + handler.postDelayed(() -> saveQuery(query), 5000); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java index 28833b3c06..6cc30e21a8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java @@ -20,7 +20,6 @@ public RecentSearch(Uri contentUri, String query, Date lastSearched) { /** * Gets query name - * * @return query name */ public String getQuery() { @@ -29,7 +28,6 @@ public String getQuery() { /** * Gets last searched date - * * @return Last searched date */ public Date getLastSearched() { @@ -37,6 +35,15 @@ public Date getLastSearched() { return (Date)lastSearched.clone(); } + + /** + * Updates the last searched date + * @param lastSearched Last searched date + */ + public void setLastSearched(Date lastSearched) { + this.lastSearched = lastSearched; + } + /** * Gets the content URI for this query * @return content URI From 9202f9960bd0b56fe6e72b8810d2912247f6d921 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 12:48:08 +0530 Subject: [PATCH 32/94] If else indentation updated --- .../commons/explore/images/SearchImageFragment.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 6412eac04a..ad7fecaa61 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -72,7 +72,8 @@ private void saveQuery(String query) { // Newly searched query... if (recentSearch == null) { recentSearch = new RecentSearch(null, query, new Date()); - }else { + } + else { recentSearch.setLastSearched(new Date()); } @@ -122,7 +123,8 @@ private void handleSuccess(List mediaList) { queryList = mediaList; if(mediaList == null || mediaList.isEmpty()) { initErrorView(); - }else { + } + else { progressBar.setVisibility(View.GONE); imagesAdapter.addAll(mediaList); @@ -167,7 +169,8 @@ private void handleNoInternet() { public int getTotalImagesCount(){ if (imagesAdapter == null) { return 0; - }else { + } + else { return imagesAdapter.getItemCount(); } } @@ -180,7 +183,8 @@ public Media getImageAtPosition(int i) { if (imagesAdapter.getItem(i).getFilename() == null) { // not yet ready to return data return null; - } else { + } + else { return new Media(imagesAdapter.getItem(i).getFilename()); } } From 903d71569ea7db48cde24f789156aeb3d8fc8bea Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 13:22:03 +0530 Subject: [PATCH 33/94] added import in test --- .../test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index a883a5a45b..edb9fc3746 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -4,6 +4,7 @@ import android.content.ContentProviderClient import android.content.Context import android.content.SharedPreferences import android.support.v4.util.LruCache +import com.google.gson.Gson import com.nhaarman.mockito_kotlin.mock import com.squareup.leakcanary.RefWatcher import fr.free.nrw.commons.auth.AccountUtil From 33c1af42396f8ebd61d5cfbdbf8a380a035d56fe Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 4 Jun 2018 11:54:12 +0530 Subject: [PATCH 34/94] edittext replaced with Searchview --- .../nrw/commons/explore/SearchActivity.java | 60 +++++++++++-------- app/src/main/res/layout/activity_search.xml | 25 ++++---- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index f650223ccc..80b240851b 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -4,15 +4,10 @@ import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.View; -import android.widget.EditText; - -import com.jakewharton.rxbinding2.view.RxView; -import com.jakewharton.rxbinding2.widget.RxTextView; - -import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.ButterKnife; @@ -22,7 +17,6 @@ import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.ViewUtil; -import io.reactivex.android.schedulers.AndroidSchedulers; /** * Represents search screen of this app @@ -31,12 +25,13 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetailPagerFragment.MediaDetailProvider{ @BindView(R.id.toolbar_search) Toolbar toolbar; - @BindView(R.id.searchBox) EditText etSearchKeyword; + @BindView(R.id.searchBox) + SearchView searchView; private SearchImageFragment searchImageFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; - private String query; + private String str_query; @Override protected void onCreate(Bundle savedInstanceState) { @@ -48,20 +43,35 @@ protected void onCreate(Bundle savedInstanceState) { toolbar.setNavigationOnClickListener(v->onBackPressed()); supportFragmentManager = getSupportFragmentManager(); setBrowseImagesFragment(); - RxTextView.textChanges(etSearchKeyword) - .takeUntil(RxView.detaches(etSearchKeyword)) - .debounce(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( query -> { - //update image list - if (!TextUtils.isEmpty(query)) { - this.query = query.toString(); - searchImageFragment.updateImageList(query.toString()); - }else { - // open search history fragment - } - } - ); + searchView.setQueryHint(getString(R.string.search_commons)); + searchView.onActionViewExpanded(); + searchView.clearFocus(); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + //update image list + if (!TextUtils.isEmpty(query)) { + str_query = query; + searchImageFragment.updateImageList(query); + }else { + + // open search history fragment + } + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + //update image list + if (!TextUtils.isEmpty(newText)) { + str_query = newText; + searchImageFragment.updateImageList(newText); + }else { + // open search history fragment + } + return false; + } + }); } @@ -124,8 +134,8 @@ public void onBackPressed() { // back to search so show search toolbar and hide navigation toolbar toolbar.setVisibility(View.VISIBLE); setNavigationBaseToolbarVisibility(false); - if (!TextUtils.isEmpty(query)) { - searchImageFragment.updateImageList(query); + if (!TextUtils.isEmpty(str_query)) { + searchImageFragment.updateImageList(str_query); } }else { toolbar.setVisibility(View.GONE); diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index e6aedc5e6e..a4af8ac045 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -26,18 +26,19 @@ app:navigationIcon="@drawable/ic_arrow_back_primary_24dp" android:background="@color/item_white_background" android:layout_height="wrap_content"> - + + Date: Thu, 7 Jun 2018 12:42:10 +0530 Subject: [PATCH 35/94] RxSearchview added --- .../nrw/commons/explore/SearchActivity.java | 49 +++++++++---------- app/src/main/res/layout/activity_search.xml | 2 +- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 80b240851b..02dfdbe6b2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -4,10 +4,15 @@ import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.View; +import android.widget.SearchView; + +import com.jakewharton.rxbinding2.view.RxView; +import com.jakewharton.rxbinding2.widget.RxSearchView; + +import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.ButterKnife; @@ -17,6 +22,7 @@ import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.ViewUtil; +import io.reactivex.android.schedulers.AndroidSchedulers; /** * Represents search screen of this app @@ -46,32 +52,21 @@ protected void onCreate(Bundle savedInstanceState) { searchView.setQueryHint(getString(R.string.search_commons)); searchView.onActionViewExpanded(); searchView.clearFocus(); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - //update image list - if (!TextUtils.isEmpty(query)) { - str_query = query; - searchImageFragment.updateImageList(query); - }else { - - // open search history fragment - } - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - //update image list - if (!TextUtils.isEmpty(newText)) { - str_query = newText; - searchImageFragment.updateImageList(newText); - }else { - // open search history fragment - } - return false; - } - }); + RxSearchView.queryTextChanges(searchView) + .takeUntil(RxView.detaches(searchView)) + .debounce(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( query -> { + //update image list + if (!TextUtils.isEmpty(query)) { + str_query = query.toString(); + searchImageFragment.updateImageList(query.toString()); + }else { + + // open search history fragment + } + } + ); } diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index a4af8ac045..a85791b444 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -26,7 +26,7 @@ app:navigationIcon="@drawable/ic_arrow_back_primary_24dp" android:background="@color/item_white_background" android:layout_height="wrap_content"> - Date: Thu, 7 Jun 2018 13:29:32 +0530 Subject: [PATCH 36/94] Added support for API 21+ --- app/src/main/res/layout/activity_search.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index a85791b444..c8b4fa6fa3 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -31,6 +31,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="true" + android:queryBackground="@android:color/transparent" + android:searchIcon="@null" app:queryBackground="@android:color/transparent" app:searchIcon="@null" android:imeOptions="actionDone" From d06e3ae9b0b52044656647f196e9d3ebd638d1d6 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 13:06:34 +0530 Subject: [PATCH 37/94] Snackbar removed on success --- .../free/nrw/commons/explore/images/SearchImageFragment.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 8e5d486e74..6002e4878f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -101,10 +101,7 @@ public void updateImageList(String query) { private void handleSuccess(List mediaList) { imagesNotFoundView.setVisibility(GONE); queryList = mediaList; - if(mediaList == null || mediaList.isEmpty()) { - initErrorView(); - }else { - + if (mediaList != null && !mediaList.isEmpty()) { progressBar.setVisibility(View.GONE); imagesAdapter.addAll(mediaList); imagesAdapter.notifyDataSetChanged(); From 8edfae56c33a117b4c5af60e43a1775deccd21cb Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 13:09:33 +0530 Subject: [PATCH 38/94] Improved code --- .../nrw/commons/explore/images/SearchImageFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 6002e4878f..10ad9f1028 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -101,7 +101,10 @@ public void updateImageList(String query) { private void handleSuccess(List mediaList) { imagesNotFoundView.setVisibility(GONE); queryList = mediaList; - if (mediaList != null && !mediaList.isEmpty()) { + if(mediaList == null || mediaList.isEmpty()) { + initErrorView(); + }else { + progressBar.setVisibility(View.GONE); imagesAdapter.addAll(mediaList); imagesAdapter.notifyDataSetChanged(); @@ -115,13 +118,13 @@ private void handleSuccess(List mediaList) { private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading queried images"); initErrorView(); + ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); } /** * Handles the UI updates for a error scenario */ private void initErrorView() { - ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); progressBar.setVisibility(GONE); imagesNotFoundView.setVisibility(VISIBLE); imagesNotFoundView.setText(getString(R.string.images_not_found, query)); From c847dbf446b3fad443d97f16541dac4f7cb2fb83 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 7 Jun 2018 17:34:11 +0530 Subject: [PATCH 39/94] Pagination added --- .../explore/images/SearchImageFragment.java | 37 ++++++++++++++++++- .../mwapi/ApacheHttpClientMediaWikiApi.java | 3 +- .../free/nrw/commons/mwapi/MediaWikiApi.java | 2 +- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 10ad9f1028..1f9c13b938 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -70,6 +70,17 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav ArrayList items = new ArrayList<>(); imagesAdapter = adapterFactory.create(items); imagesRecyclerView.setAdapter(imagesAdapter); + imagesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + + if (!recyclerView.canScrollVertically(1)) { +// Toast.makeText(ChatDetailsActivity.this,"LAst",Toast.LENGTH_LONG).show(); + addImagesToList(query); + } + } + }); return rootView; } @@ -86,13 +97,37 @@ public void updateImageList(String query) { progressBar.setVisibility(View.VISIBLE); queryList.clear(); imagesAdapter.clear(); - Observable.fromCallable(() -> mwApi.searchImages(query)) + Observable.fromCallable(() -> mwApi.searchImages(query,queryList.size())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) .subscribe(this::handleSuccess, this::handleError); } + public void addImagesToList(String query) { + this.query = query; + progressBar.setVisibility(View.VISIBLE); + Observable.fromCallable(() -> mwApi.searchImages(query,queryList.size())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handlePaginationSuccess, this::handleError); + } + + /** + * Handles the success scenario + * it initializes the recycler view by adding items to the adapter + * @param mediaList + */ + private void handlePaginationSuccess(List mediaList) { + queryList.addAll(mediaList); + progressBar.setVisibility(View.GONE); + imagesAdapter.addAll(mediaList); + imagesAdapter.notifyDataSetChanged(); + } + + + /** * Handles the success scenario * it initializes the recycler view by adding items to the adapter diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 9f2ee638ba..cff78779f2 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -628,7 +628,7 @@ public List getCategoryImages(String categoryName) { */ @Override @NonNull - public List searchImages(String query) { + public List searchImages(String query, int offset) { List imageNodes = null; try { imageNodes = api.action("query") @@ -637,6 +637,7 @@ public List searchImages(String query) { .param("srwhat", "text") .param("srnamespace", "6") .param("srlimit", "25") + .param("sroffset",offset) .param("srsearch", query) .get() .getNodes("/api/query/search/p/@title"); diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index fb6d9c37cf..a9d334d083 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -42,7 +42,7 @@ public interface MediaWikiApi { List getCategoryImages(String categoryName); @NonNull - List searchImages(String title); + List searchImages(String title, int offset); @NonNull UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener) throws IOException; From c5e6b2309eab6fd620e5875747bef748f2c2b593 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Sat, 9 Jun 2018 09:37:15 +0530 Subject: [PATCH 40/94] Removed unnecessary toast --- .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 2 +- .../fr/free/nrw/commons/explore/images/SearchImageFragment.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 02dfdbe6b2..c8ddef5522 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -65,7 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { // open search history fragment } - } + } ); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 1f9c13b938..5217335cc5 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -76,7 +76,6 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (!recyclerView.canScrollVertically(1)) { -// Toast.makeText(ChatDetailsActivity.this,"LAst",Toast.LENGTH_LONG).show(); addImagesToList(query); } } From 8f385cb0e91902e22d419e0f970ff9dc641c7937 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Sat, 9 Jun 2018 09:59:33 +0530 Subject: [PATCH 41/94] Comment added in method --- .../nrw/commons/explore/images/SearchImageFragment.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index ded8d6d9e3..b9e74e209d 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -94,7 +94,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); - + // check if end of recycler view is reached, if yes then add more results to existing results if (!recyclerView.canScrollVertically(1)) { addImagesToList(query); } @@ -123,6 +123,10 @@ public void updateImageList(String query) { .subscribe(this::handleSuccess, this::handleError); } + + /** + * Adds more results to existing search results + */ public void addImagesToList(String query) { this.query = query; progressBar.setVisibility(View.VISIBLE); From 350da737a768d26263c1c3534929b51683886607 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 12 Jun 2018 13:59:38 +0530 Subject: [PATCH 42/94] Support for landscape mode added --- .../nrw/commons/explore/images/SearchImageFragment.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index b9e74e209d..fcc6ce950d 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -2,8 +2,10 @@ import android.content.SharedPreferences; +import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; +import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -86,7 +88,12 @@ private void saveQuery(String query) { public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); ButterKnife.bind(this, rootView); - imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ + imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + } + else{ + imagesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); + } ArrayList items = new ArrayList<>(); imagesAdapter = adapterFactory.create(items); imagesRecyclerView.setAdapter(imagesAdapter); From 046533050e16e447b2d8bc25c816e1fd53dc9933 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 12 Jun 2018 17:18:57 +0530 Subject: [PATCH 43/94] Fixed screen rotation issue on Explore and Search activity --- .../commons/category/CategoryImagesActivity.java | 15 ++++++++++----- .../free/nrw/commons/explore/SearchActivity.java | 11 +++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 2cb15534c8..c716017343 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -59,11 +59,6 @@ protected void onCreate(Bundle savedInstanceState) { supportFragmentManager = getSupportFragmentManager(); setCategoryImagesFragment(); supportFragmentManager.addOnBackStackChangedListener(this); - if (savedInstanceState != null) { - mediaDetails = (MediaDetailPagerFragment) supportFragmentManager - .findFragmentById(R.id.fragmentContainer); - - } requestAuthToken(); initDrawer(); setPageTitle(); @@ -115,6 +110,16 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { mediaDetails.showImage(i); } + @Override + protected void onResume() { + if (supportFragmentManager.getBackStackEntryCount()==1){ + //FIXME: Temporary fix for screen rotation inside media details. If we don't call onBackPressed then fragment stack is increasing every time. + //FIXME: Similar issue like this https://github.com/commons-app/apps-android-commons/issues/894 + onBackPressed(); + } + super.onResume(); + } + /** * Consumers should be simply using this method to use this activity. * @param context diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 51663fdc26..108417e23e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -137,6 +137,17 @@ public void onSearchImageClicked(int index) { mediaDetails.showImage(index); } + @Override + protected void onResume() { + if (supportFragmentManager.getBackStackEntryCount()==1){ + //FIXME: Temporary fix for screen rotation inside media details. If we don't call onBackPressed then fragment stack is increasing every time. + //FIXME: Similar issue like this https://github.com/commons-app/apps-android-commons/issues/894 + // This is called on screen rotation when user is inside media details. Ideally it should show Media Details but since we are not saving the state now. We are throwing the user to search screen otherwise the app was crashing. + // + onBackPressed(); + } + super.onResume(); + } @Override public void onBackPressed() { From cdb11a0b0e17e50250556ada4c0470bbd0e7c024 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 12 Jun 2018 17:27:52 +0530 Subject: [PATCH 44/94] Clear focus added --- .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 3 +++ app/src/main/res/layout/activity_search.xml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 51663fdc26..cdd4c7ba33 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -156,5 +156,8 @@ public void onBackPressed() { public void updateText(String query) { searchView.setQuery(query, true);; + // Clear focus of searchView now. searchView.clearFocus(); does not seem to work Check the below link for more details. + // https://stackoverflow.com/questions/6117967/how-to-remove-focus-without-setting-focus-to-another-control/15481511 + resultsContainer.requestFocus(); } } diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 3f561b973d..f2f9d50c0a 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -51,6 +51,8 @@ android:id="@+id/fragmentContainer" android:orientation="horizontal" android:layout_below="@id/toolbar_layout" + android:focusable="true" + android:focusableInTouchMode="true" /> Date: Tue, 12 Jun 2018 18:09:40 +0530 Subject: [PATCH 45/94] Delete all function added in Content Provider and called from fragment --- .../RecentSearchesContentProvider.java | 18 +++++++++++++- .../recentsearches/RecentSearchesDao.java | 22 ++++++++++++++++++ .../RecentSearchesFragment.java | 19 ++++++++++++++- .../drawable-hdpi/ic_delete_grey_700_24dp.png | Bin 0 -> 195 bytes .../drawable-mdpi/ic_delete_grey_700_24dp.png | Bin 0 -> 132 bytes .../ic_delete_grey_700_24dp.png | Bin 0 -> 194 bytes .../ic_delete_grey_700_24dp.png | Bin 0 -> 276 bytes .../ic_delete_grey_700_24dp.png | Bin 0 -> 355 bytes .../res/layout/fragment_search_history.xml | 11 +++++++++ app/src/main/res/values/strings.xml | 3 +++ 10 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_delete_grey_700_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java index a0b0669f5a..810a781d40 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java @@ -11,6 +11,7 @@ import javax.inject.Inject; +import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.di.CommonsDaggerContentProvider; import timber.log.Timber; @@ -101,7 +102,22 @@ public Uri insert(@NonNull Uri uri, ContentValues contentValues) { @Override public int delete(@NonNull Uri uri, String s, String[] strings) { - return 0; + int rows; + int uriType = uriMatcher.match(uri); + SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); + switch (uriType) { + case RECENT_SEARCHES_ID: + Timber.d("Deleting recent searches id %s", uri.getLastPathSegment()); + rows = db.delete(RecentSearchesDao.Table.TABLE_NAME, + "_id = ?", + new String[]{uri.getLastPathSegment()} + ); + break; + default: + throw new IllegalArgumentException("Unknown URI" + uri); + } + getContext().getContentResolver().notifyChange(uri, null); + return rows; } @SuppressWarnings("ConstantConditions") diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index e9234a5ab8..cc9ebc0441 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -7,6 +7,7 @@ import android.os.RemoteException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; import java.util.ArrayList; import java.util.Date; @@ -41,6 +42,27 @@ public void save(RecentSearch recentSearch) { } } + public void deleteAll(List recentSearchesStringList) { + ContentProviderClient db = clientProvider.get(); + for (String recentSearchName : recentSearchesStringList) { + try { + RecentSearch recentSearch = find(recentSearchName); + if (recentSearch.getContentUri() == null) { + throw new RuntimeException("tried to delete item with no content URI"); + } else { + Log.d("QUERY_NAME",recentSearch.getContentUri()+"- delete tried"); + db.delete(recentSearch.getContentUri(), null, null); + Log.d("QUERY_NAME",recentSearch.getQuery()+"- query deleted"); + } + } catch (RemoteException e) { + Log.d("Exception",e+"- query deleted"); + throw new RuntimeException(e); + } finally { + db.release(); + } + } + } + /** * Find persisted search query in database, based on its name. * @param name Search query Ex- "butterfly" diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java index ad5bf7ebe2..8b3cd87104 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java @@ -1,11 +1,14 @@ package fr.free.nrw.commons.explore.recentsearches; import android.os.Bundle; +import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.ImageView; import android.widget.ListView; +import android.widget.Toast; import java.util.List; @@ -26,6 +29,8 @@ public class RecentSearchesFragment extends CommonsDaggerSupportFragment { @BindView(R.id.recent_searches_list) ListView recentSearchesList; List recentSearches; ArrayAdapter adapter; + @BindView(R.id.recent_searches_delete_button) + ImageView recent_searches_delete_button; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -33,7 +38,19 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View rootView = inflater.inflate(R.layout.fragment_search_history, container, false); ButterKnife.bind(this, rootView); recentSearches = recentSearchesDao.recentSearches(10); - adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches,recentSearches); + recent_searches_delete_button.setOnClickListener(v -> { + new AlertDialog.Builder(getContext()) + .setMessage(getString(R.string.delete_recent_searches_dialog)) + .setPositiveButton("YES", (dialog, which) -> { + recentSearchesDao.deleteAll(recentSearches); + Toast.makeText(getContext(),getString(R.string.search_history_deleted),Toast.LENGTH_SHORT).show(); + dialog.dismiss(); + }) + .setNegativeButton("NO", null) + .create() + .show(); + }); + adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches, recentSearches); recentSearchesList.setAdapter(adapter); recentSearchesList.setOnItemClickListener((parent, view, position, id) -> ( (SearchActivity)getContext()).updateText(recentSearches.get(position))); diff --git a/app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-hdpi/ic_delete_grey_700_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..de4a147fe909f8f2b918dd92253ebe46058c58a8 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBx;$MRLn>~)yG;} zdZ*rF&M4Xc$(0>13@&DU-u;oX{c3QbvSwk})7<5mG<0V^4ck{>STbc}CWWt+;x#paYN?#LqOA^;&2F0dV7QORIyP*cgca7}!M>+6g9 o*Zri8^}ZGQoxacCteBbP0l+XkK@GmNc literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-xhdpi/ic_delete_grey_700_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fa573b1e39709f32584e76c9d6b7dfdf34b2eca2 GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUtPEQxdkcwMxZye-oFyL{x=%>7H zs_(AAiw9jURQE0@*Y@rC!2kc6vz)HTBo~#xJ6V8Q7|J78^Pj%RpnJI7b&JfpbBqiE zya((BSOp%v1j(X-0`@m;hx6qcZVCk0Ff86C#j`)7`Fh?L7oY1>zgL(&J^LYNIU@tZ ahgx@1PLn>~)y=};K$Uvmwq2E`& zT@_np9KOs66|i;mSIjeYa*JJ~vi_;`Khq2SUnVNP4Dy=0kDXCK!J&bHk%?u6-|ss! zX4mEDWUmp*(DM^svh-ny`>fpuwoCxYatJ7>XEJ7SY?-x_8OVlcAvr0`a{Uzh?wUlp^xtKFtJP(TpBw39^E_GjC+zS3{l|-$ d;a1eXVU2L*ys4jJ;SKaAgQu&X%Q~loCIE+MSReoZ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_delete_grey_700_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..10e27028790db44124e7d84e221e573500772758 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>VAS$-aSW-r_4dwb-X;eI*TAwH zJR8-l7P6ZL^={dNRbf7=GNpxU%~5 z?TFoP3d5gOsl_<;zctox^*@_wU9kH`^$we>XLmBNFf=eQ2td60>Lv5F + + Recently searched queries Image successfully added to %1$s on Wikidata! Failed to update corresponding wiki data entity! + Are you sure you want to clear your search history? + Search history deleted + From 1c3e616a84a7c4d8764524cc8c09227fd22e977f Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 12 Jun 2018 18:42:47 +0530 Subject: [PATCH 46/94] Scrollbar Recyclerview added --- app/src/main/res/layout/fragment_browse_image.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/layout/fragment_browse_image.xml b/app/src/main/res/layout/fragment_browse_image.xml index 4879f31892..f146dd6b5a 100644 --- a/app/src/main/res/layout/fragment_browse_image.xml +++ b/app/src/main/res/layout/fragment_browse_image.xml @@ -27,5 +27,7 @@ android:id="@+id/imagesListBox" android:layout_width="match_parent" android:layout_height="wrap_content" + android:scrollbars="vertical" + android:scrollbarSize="@dimen/standard_gap" android:fadingEdge="none" /> \ No newline at end of file From 12df1755763b421645e4aad340b711e07549f5ee Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 12 Jun 2018 21:15:52 +0530 Subject: [PATCH 47/94] Share Icon changed to 32 dp and back button added in explore, search activity. --- .../category/CategoryImagesActivity.java | 1 + .../nrw/commons/explore/SearchActivity.java | 1 + .../media/MediaDetailPagerFragment.java | 30 +++++++++++-------- .../main/res/menu/fragment_image_detail.xml | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 2cb15534c8..0dfe3b02da 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -113,6 +113,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(i); + initBackButton(); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 51663fdc26..f3ee66855b 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -135,6 +135,7 @@ public void onSearchImageClicked(int index) { supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(index); + initBackButton(); } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 2c58e8dbb1..fa71e1633d 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -127,7 +127,10 @@ public boolean onOptionsItemSelected(MenuItem item) { Media m = provider.getMediaAtPosition(pager.getCurrentItem()); switch (item.getItemId()) { case R.id.menu_share_current_image: - // Share - intent set in onCreateOptionsMenu, around line 252 + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + shareIntent.putExtra(Intent.EXTRA_TEXT, m.getDisplayTitle() + " \n" + m.getFilePageTitle().getCanonicalUri()); + startActivity(Intent.createChooser(shareIntent, "Share image via...")); return true; case R.id.menu_browser_current_image: // View in browser @@ -239,20 +242,21 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.findItem(R.id.menu_browser_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_share_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true); + menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true); // Set ShareActionProvider Intent - ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menu.findItem(R.id.menu_share_current_image)); - // On some phones null is returned for some reason: - // https://github.com/commons-app/apps-android-commons/issues/413 - if (mShareActionProvider != null) { - Intent shareIntent = new Intent(Intent.ACTION_SEND); - shareIntent.setType("text/plain"); - shareIntent.putExtra(Intent.EXTRA_TEXT, - m.getDisplayTitle() + " \n" + m.getFilePageTitle().getCanonicalUri()); - mShareActionProvider.setShareIntent(shareIntent); - } - - if (m instanceof Contribution) { +// ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menu.findItem(R.id.menu_share_current_image)); +// // On some phones null is returned for some reason: +// // https://github.com/commons-app/apps-android-commons/issues/413 +// if (mShareActionProvider != null) { +// Intent shareIntent = new Intent(Intent.ACTION_SEND); +// shareIntent.setType("text/plain"); +// shareIntent.putExtra(Intent.EXTRA_TEXT, +// m.getDisplayTitle() + " \n" + m.getFilePageTitle().getCanonicalUri()); +// mShareActionProvider.setShareIntent(shareIntent); +// } + + if (m instanceof Contribution ) { Contribution c = (Contribution) m; switch (c.getState()) { case Contribution.STATE_FAILED: diff --git a/app/src/main/res/menu/fragment_image_detail.xml b/app/src/main/res/menu/fragment_image_detail.xml index 70a35951a8..e0970e2561 100644 --- a/app/src/main/res/menu/fragment_image_detail.xml +++ b/app/src/main/res/menu/fragment_image_detail.xml @@ -4,7 +4,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> Date: Wed, 13 Jun 2018 10:03:35 +0530 Subject: [PATCH 48/94] Removed unnecessary code --- .../nrw/commons/media/MediaDetailPagerFragment.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index fa71e1633d..0b3bedc2be 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -242,19 +242,6 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.findItem(R.id.menu_browser_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_share_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true); - menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true); - - // Set ShareActionProvider Intent -// ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menu.findItem(R.id.menu_share_current_image)); -// // On some phones null is returned for some reason: -// // https://github.com/commons-app/apps-android-commons/issues/413 -// if (mShareActionProvider != null) { -// Intent shareIntent = new Intent(Intent.ACTION_SEND); -// shareIntent.setType("text/plain"); -// shareIntent.putExtra(Intent.EXTRA_TEXT, -// m.getDisplayTitle() + " \n" + m.getFilePageTitle().getCanonicalUri()); -// mShareActionProvider.setShareIntent(shareIntent); -// } if (m instanceof Contribution ) { Contribution c = (Contribution) m; From 28e43c2c3a8921acd24ac779b4419574d7ae37a0 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 14 Jun 2018 10:03:11 +0530 Subject: [PATCH 49/94] Wrote and run tests for Recent Searches (creating db, migrating from versions, deletion, finding,.. --- .../recentsearches/RecentSearchesDaoTest.kt | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt new file mode 100644 index 0000000000..d319f2708c --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt @@ -0,0 +1,244 @@ +package fr.free.nrw.commons.explore.recentsearches + +import android.content.ContentProviderClient +import android.content.ContentValues +import android.database.Cursor +import android.database.MatrixCursor +import android.database.sqlite.SQLiteDatabase +import android.os.RemoteException +import com.nhaarman.mockito_kotlin.* +import fr.free.nrw.commons.BuildConfig +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.BASE_URI +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.* +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import java.util.* + +@RunWith(RobolectricTestRunner::class) +@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +class RecentSearchesDaoTest { + + private val columns = arrayOf(COLUMN_ID, COLUMN_NAME, COLUMN_LAST_USED) + private val client: ContentProviderClient = mock() + private val database: SQLiteDatabase = mock() + private val captor = argumentCaptor() + private val queryCaptor = argumentCaptor>() + + private lateinit var testObject: RecentSearchesDao + + @Before + fun setUp() { + testObject = RecentSearchesDao { client } + } + + @Test + fun createTable() { + onCreate(database) + verify(database).execSQL(CREATE_TABLE_STATEMENT) + } + + @Test + fun deleteTable() { + onDelete(database) + inOrder(database) { + verify(database).execSQL(DROP_TABLE_STATEMENT) + verify(database).execSQL(CREATE_TABLE_STATEMENT) + } + } + + @Test + fun migrateTableVersionFrom_v1_to_v2() { + onUpdate(database, 1, 2) + // Table didnt exist before v7 + verifyZeroInteractions(database) + } + + @Test + fun migrateTableVersionFrom_v2_to_v3() { + onUpdate(database, 2, 3) + // Table didnt exist before v7 + verifyZeroInteractions(database) + } + + @Test + fun migrateTableVersionFrom_v3_to_v4() { + onUpdate(database, 3, 4) + // Table didnt exist before v7 + verifyZeroInteractions(database) + } + + @Test + fun migrateTableVersionFrom_v4_to_v5() { + onUpdate(database, 4, 5) + // Table didnt exist before v7 + verifyZeroInteractions(database) + } + + @Test + fun migrateTableVersionFrom_v5_to_v6() { + onUpdate(database, 5, 6) + // Table didnt exist before v7 + verifyZeroInteractions(database) + } + + @Test + fun migrateTableVersionFrom_v6_to_v7() { + onUpdate(database, 6, 7) + verify(database).execSQL(CREATE_TABLE_STATEMENT) + } + + @Test + fun migrateTableVersionFrom_v7_to_v8() { + onUpdate(database, 7, 8) + // Table didnt change in version 8 + verifyZeroInteractions(database) + } + + @Test + fun createFromCursor() { + createCursor(1).let { cursor -> + cursor.moveToFirst() + testObject.fromCursor(cursor).let { + assertEquals(uriForId(1), it.contentUri) + assertEquals("butterfly", it.query) + assertEquals(123, it.lastSearched.time) + } + } + } + + @Test + fun saveExistingQuery() { + createCursor(1).let { + val recentSearch = testObject.fromCursor(it.apply { moveToFirst() }) + + testObject.save(recentSearch) + + verify(client).update(eq(recentSearch.contentUri), captor.capture(), isNull(), isNull()) + captor.firstValue.let { cv -> + assertEquals(2, cv.size()) + assertEquals(recentSearch.query, cv.getAsString(COLUMN_NAME)) + assertEquals(recentSearch.lastSearched.time, cv.getAsLong(COLUMN_LAST_USED)) + } + } + } + + @Test + fun saveNewQuery() { + val contentUri = RecentSearchesContentProvider.uriForId(111) + whenever(client.insert(isA(), isA())).thenReturn(contentUri) + val recentSearch = RecentSearch(null, "butterfly", Date(234L)) + + testObject.save(recentSearch) + + verify(client).insert(eq(BASE_URI), captor.capture()) + captor.firstValue.let { cv -> + assertEquals(2, cv.size()) + assertEquals(recentSearch.query, cv.getAsString(COLUMN_NAME)) + assertEquals(recentSearch.lastSearched.time, cv.getAsLong(COLUMN_LAST_USED)) + assertEquals(contentUri, recentSearch.contentUri) + } + } + + @Test(expected = RuntimeException::class) + fun findRecentSearchTranslatesExceptions() { + whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenThrow(RemoteException("")) + testObject.find("butterfly") + } + + @Test + fun whenTheresNoDataFindReturnsNull_nullCursor() { + whenever(client.query(any(), any(), any(), any(), any())).thenReturn(null) + assertNull(testObject.find("butterfly")) + } + + @Test + fun whenTheresNoDataFindReturnsNull_emptyCursor() { + whenever(client.query(any(), any(), any(), any(), any())).thenReturn(createCursor(0)) + assertNull(testObject.find("butterfly")) + } + + @Test + fun cursorsAreClosedAfterUse() { + val mockCursor: Cursor = mock() + whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(mockCursor) + whenever(mockCursor.moveToFirst()).thenReturn(false) + + testObject.find("butterfly") + + verify(mockCursor).close() + } + + + @Test + fun findRecentSearchQuery() { + whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1)) + + val recentSearch = testObject.find("butterfly") + assertNotNull(recentSearch) + + assertEquals(uriForId(1), recentSearch?.contentUri) + assertEquals("butterfly", recentSearch?.query) + assertEquals(123L, recentSearch?.lastSearched?.time) + + verify(client).query( + eq(BASE_URI), + eq(ALL_FIELDS), + eq("$COLUMN_NAME=?"), + queryCaptor.capture(), + isNull() + ) + assertEquals("butterfly", queryCaptor.firstValue[0]) + } + + @Test + fun cursorsAreClosedAfterRecentSearchQuery() { + val mockCursor: Cursor = mock() + whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(mockCursor) + whenever(mockCursor.moveToFirst()).thenReturn(false) + + testObject.recentSearches(1) + + verify(mockCursor).close() + } + + @Test + fun recentSearchesReturnsLessThanLimit() { + whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(1)) + + val result = testObject.recentSearches(10) + + assertEquals(1, result.size) + assertEquals("butterfly", result[0]) + + verify(client).query( + eq(BASE_URI), + eq(ALL_FIELDS), + isNull(), + queryCaptor.capture(), + eq("$COLUMN_LAST_USED DESC") + ) + assertEquals(0, queryCaptor.firstValue.size) + } + + @Test + fun recentSearchesHonorsLimit() { + whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(10)) + + val result = testObject.recentSearches(5) + + assertEquals(5, result.size) + } + + private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { + for (i in 0 until rowCount) { + addRow(listOf("1", "butterfly", "123")) + } + } + +} \ No newline at end of file From f3fa475b9b80daa538841c97a42aa635fbc417a7 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 11:38:09 +0530 Subject: [PATCH 50/94] Category Search Fragment added --- .../categories/SearchCategoryFragment.java | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java new file mode 100644 index 0000000000..d5a618773a --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -0,0 +1,241 @@ +package fr.free.nrw.commons.explore.categories; + + +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.pedrogomez.renderers.RVRendererAdapter; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; + +import butterknife.BindView; +import butterknife.ButterKnife; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.explore.SearchActivity; +import fr.free.nrw.commons.explore.images.SearchImagesAdapterFactory; +import fr.free.nrw.commons.explore.recentsearches.RecentSearch; +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.NetworkUtils; +import fr.free.nrw.commons.utils.ViewUtil; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +/** + * Displays the image search screen. + */ + +public class SearchCategoryFragment extends CommonsDaggerSupportFragment { + + private static int TIMEOUT_SECONDS = 15; + + @BindView(R.id.imagesListBox) + RecyclerView imagesRecyclerView; + @BindView(R.id.imageSearchInProgress) + ProgressBar progressBar; + @BindView(R.id.imagesNotFound) + TextView imagesNotFoundView; + String query; + + @Inject RecentSearchesDao recentSearchesDao; + @Inject MediaWikiApi mwApi; + @Inject @Named("default_preferences") SharedPreferences prefs; + + private RVRendererAdapter imagesAdapter; + private List queryList = new ArrayList<>(); + + private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { + int index = queryList.indexOf(item); + ((SearchActivity)getContext()).onSearchImageClicked(index); + saveQuery(query); + }); + + private void saveQuery(String query) { + RecentSearch recentSearch = recentSearchesDao.find(query); + + // Newly searched query... + if (recentSearch == null) { + recentSearch = new RecentSearch(null, query, new Date()); + } + else { + recentSearch.setLastSearched(new Date()); + } + + recentSearchesDao.save(recentSearch); + + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); + ButterKnife.bind(this, rootView); + if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ + imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + } + else{ + imagesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); + } + ArrayList items = new ArrayList<>(); + imagesAdapter = adapterFactory.create(items); + imagesRecyclerView.setAdapter(imagesAdapter); + imagesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + // check if end of recycler view is reached, if yes then add more results to existing results + if (!recyclerView.canScrollVertically(1)) { + addImagesToList(query); + } + } + }); + return rootView; + } + + /** + * Checks for internet connection and then initializes the recycler view with 25 images of the searched query + * Clearing imageAdapter every time new keyword is searched so that user can see only new results + */ + public void updateImageList(String query) { + this.query = query; + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + progressBar.setVisibility(View.VISIBLE); + queryList.clear(); + imagesAdapter.clear(); + Observable.fromCallable(() -> mwApi.searchImages(query,queryList.size())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + + /** + * Adds more results to existing search results + */ + public void addImagesToList(String query) { + this.query = query; + progressBar.setVisibility(View.VISIBLE); + Observable.fromCallable(() -> mwApi.searchImages(query,queryList.size())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handlePaginationSuccess, this::handleError); + } + + /** + * Handles the success scenario + * it initializes the recycler view by adding items to the adapter + * @param mediaList + */ + private void handlePaginationSuccess(List mediaList) { + queryList.addAll(mediaList); + progressBar.setVisibility(View.GONE); + imagesAdapter.addAll(mediaList); + imagesAdapter.notifyDataSetChanged(); + } + + + + /** + * Handles the success scenario + * it initializes the recycler view by adding items to the adapter + * @param mediaList + */ + private void handleSuccess(List mediaList) { + imagesNotFoundView.setVisibility(GONE); + queryList = mediaList; + if(mediaList == null || mediaList.isEmpty()) { + initErrorView(); + } + else { + + progressBar.setVisibility(View.GONE); + imagesAdapter.addAll(mediaList); + imagesAdapter.notifyDataSetChanged(); + + // check if user is waiting for 5 seconds if yes then save search query to history. + Handler handler = new Handler(); + handler.postDelayed(() -> saveQuery(query), 5000); + } + } + + /** + * Logs and handles API error scenario + * @param throwable + */ + private void handleError(Throwable throwable) { + Timber.e(throwable, "Error occurred while loading queried images"); + initErrorView(); + ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); + } + + /** + * Handles the UI updates for a error scenario + */ + private void initErrorView() { + progressBar.setVisibility(GONE); + imagesNotFoundView.setVisibility(VISIBLE); + imagesNotFoundView.setText(getString(R.string.images_not_found, query)); + } + + /** + * Handles the UI updates for no internet scenario + */ + private void handleNoInternet() { + progressBar.setVisibility(GONE); + ViewUtil.showSnackbar(imagesRecyclerView, R.string.no_internet); + } + + /** + * returns total number of images present in the recyclerview adapter. + */ + public int getTotalImagesCount(){ + if (imagesAdapter == null) { + return 0; + } + else { + return imagesAdapter.getItemCount(); + } + } + + /** + * returns Media Object at position + * @param i position of Media in the recyclerview adapter. + */ + public Media getImageAtPosition(int i) { + if (imagesAdapter.getItem(i).getFilename() == null) { + // not yet ready to return data + return null; + } + else { + return new Media(imagesAdapter.getItem(i).getFilename()); + } + } +} From 900e40b72b9226c679db53b50e1b96e1fb873ec7 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 11:44:19 +0530 Subject: [PATCH 51/94] Adapter factory added --- .../SearchCategoriesAdapterFactory.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java new file mode 100644 index 0000000000..879e5f23aa --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -0,0 +1,28 @@ +package fr.free.nrw.commons.explore.categories; + +import com.pedrogomez.renderers.ListAdapteeCollection; +import com.pedrogomez.renderers.RVRendererAdapter; +import com.pedrogomez.renderers.RendererBuilder; + +import java.util.Collections; +import java.util.List; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.explore.images.SearchImagesRenderer; + + +class SearchCategoriesAdapterFactory { + private final SearchImagesRenderer.ImageClickedListener listener; + + SearchCategoriesAdapterFactory(SearchImagesRenderer.ImageClickedListener listener) { + this.listener = listener; + } + + public RVRendererAdapter create(List searchImageItemList) { + RendererBuilder builder = new RendererBuilder() + .bind(Media.class, new SearchImagesRenderer(listener)); + ListAdapteeCollection collection = new ListAdapteeCollection<>( + searchImageItemList != null ? searchImageItemList : Collections.emptyList()); + return new RVRendererAdapter<>(builder, collection); + } +} From de419b76e07b0e6043ec03ee09e279a64f662e9a Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 11:51:32 +0530 Subject: [PATCH 52/94] Renderer added --- .../categories/SearchCategoriesRenderer.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java new file mode 100644 index 0000000000..08fb642dd5 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -0,0 +1,76 @@ +package fr.free.nrw.commons.explore.categories; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +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; + +/** + * presentation logic of individual image in search is handled here + */ + +class SearchCategoriesRenderer extends Renderer { + @BindView(R.id.categoryImageTitle) TextView tvImageName; + @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; + @BindView(R.id.categoryImageView) + MediaWikiImageView browseImage; + + private final ImageClickedListener listener; + + SearchCategoriesRenderer(ImageClickedListener listener) { + this.listener = listener; + } + + @Override + protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { + return layoutInflater.inflate(R.layout.layout_category_images, viewGroup, false); + } + + @Override + protected void setUpView(View view) { + ButterKnife.bind(this, view); + } + + @Override + protected void hookListeners(View view) { + view.setOnClickListener(v -> { + Media item = getContent(); + if (listener != null) { + listener.imageClicked(item); + } + }); + } + + @Override + public void render() { + Media item = getContent(); + tvImageName.setText(item.getFilename()); + browseImage.setMedia(item); + setAuthorView(item, categoryImageAuthor); + } + + interface ImageClickedListener { + void imageClicked(Media item); + } + + /** + * formats author name as "Uploaded by: authorName" and sets it in textview + */ + private void setAuthorView(Media item, TextView author) { + if (item.getCreator() != null && !item.getCreator().equals("")) { + author.setVisibility(View.GONE); + String uploadedByTemplate = getContext().getString(R.string.image_uploaded_by); + author.setText(String.format(uploadedByTemplate, item.getCreator())); + } else { + author.setVisibility(View.VISIBLE); + } + } +} From 028fc0874827fe5631893e3e7306fac3c825611d Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 11:53:18 +0530 Subject: [PATCH 53/94] Improvements --- .../SearchCategoriesAdapterFactory.java | 7 +++---- .../categories/SearchCategoriesRenderer.java | 18 ------------------ .../categories/SearchCategoryFragment.java | 3 +-- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index 879e5f23aa..e4d5efa732 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -8,19 +8,18 @@ import java.util.List; import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.explore.images.SearchImagesRenderer; class SearchCategoriesAdapterFactory { - private final SearchImagesRenderer.ImageClickedListener listener; + private final SearchCategoriesRenderer.ImageClickedListener listener; - SearchCategoriesAdapterFactory(SearchImagesRenderer.ImageClickedListener listener) { + SearchCategoriesAdapterFactory(SearchCategoriesRenderer.ImageClickedListener listener) { this.listener = listener; } public RVRendererAdapter create(List searchImageItemList) { RendererBuilder builder = new RendererBuilder() - .bind(Media.class, new SearchImagesRenderer(listener)); + .bind(Media.class, new SearchCategoriesRenderer(listener)); ListAdapteeCollection collection = new ListAdapteeCollection<>( searchImageItemList != null ? searchImageItemList : Collections.emptyList()); return new RVRendererAdapter<>(builder, collection); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java index 08fb642dd5..ac61b24759 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -10,7 +10,6 @@ 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,9 +18,6 @@ class SearchCategoriesRenderer extends Renderer { @BindView(R.id.categoryImageTitle) TextView tvImageName; - @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; - @BindView(R.id.categoryImageView) - MediaWikiImageView browseImage; private final ImageClickedListener listener; @@ -53,24 +49,10 @@ protected void hookListeners(View view) { public void render() { Media item = getContent(); tvImageName.setText(item.getFilename()); - browseImage.setMedia(item); - setAuthorView(item, categoryImageAuthor); } interface ImageClickedListener { void imageClicked(Media item); } - /** - * formats author name as "Uploaded by: authorName" and sets it in textview - */ - private void setAuthorView(Media item, TextView author) { - if (item.getCreator() != null && !item.getCreator().equals("")) { - author.setVisibility(View.GONE); - String uploadedByTemplate = getContext().getString(R.string.image_uploaded_by); - author.setText(String.format(uploadedByTemplate, item.getCreator())); - } else { - author.setVisibility(View.VISIBLE); - } - } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index d5a618773a..c9ce465c23 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -30,7 +30,6 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; -import fr.free.nrw.commons.explore.images.SearchImagesAdapterFactory; import fr.free.nrw.commons.explore.recentsearches.RecentSearch; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; import fr.free.nrw.commons.mwapi.MediaWikiApi; @@ -67,7 +66,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { private RVRendererAdapter imagesAdapter; private List queryList = new ArrayList<>(); - private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { + private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { int index = queryList.indexOf(item); ((SearchActivity)getContext()).onSearchImageClicked(index); saveQuery(query); From 9624a8567daba3798b163263f20b2a679ba6ce53 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 11:56:10 +0530 Subject: [PATCH 54/94] Viewpager adapter added --- .../nrw/commons/explore/SearchActivity.java | 34 +++++++++++--- .../nrw/commons/explore/ViewPagerAdapter.java | 44 +++++++++++++++++++ 2 files changed, 71 insertions(+), 7 deletions(-) create mode 100755 app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 10ac83bf17..a387d4d70c 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -2,6 +2,7 @@ import android.database.DataSetObserver; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.Toolbar; @@ -13,12 +14,15 @@ import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxSearchView; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.explore.categories.SearchCategoryFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; @@ -41,6 +45,7 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; private String query; + ViewPagerAdapter viewPagerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { @@ -56,6 +61,27 @@ protected void onCreate(Bundle savedInstanceState) { searchView.setQueryHint(getString(R.string.search_commons)); searchView.onActionViewExpanded(); searchView.clearFocus(); + + } + + private void setSearchHistoryFragment() { + recentSearchesFragment = new RecentSearchesFragment(); + FragmentTransaction transaction = supportFragmentManager.beginTransaction(); + transaction.add(R.id.searchHistoryContainer, recentSearchesFragment).commit(); + } + + public void setTabs() { + List fragmentList = new ArrayList<>(); + List titleList = new ArrayList<>(); + SearchImageFragment browseImageFragment = new SearchImageFragment(); + SearchCategoryFragment browseCategoryFragment = new SearchCategoryFragment(); + fragmentList.add(browseImageFragment); + titleList.add("IMAGES"); + fragmentList.add(browseCategoryFragment); + titleList.add("CATEGORIES"); + + viewPagerAdapter.setTabData(fragmentList, titleList); + viewPagerAdapter.notifyDataSetChanged(); RxSearchView.queryTextChanges(searchView) .takeUntil(RxView.detaches(searchView)) .debounce(500, TimeUnit.MILLISECONDS) @@ -67,6 +93,7 @@ protected void onCreate(Bundle savedInstanceState) { searchHistoryContainer.setVisibility(View.GONE); this.query = query.toString(); searchImageFragment.updateImageList(query.toString()); + browseCategoryFragment.updateCategoryList(filter.toString()); }else { resultsContainer.setVisibility(View.GONE); searchHistoryContainer.setVisibility(View.VISIBLE); @@ -76,13 +103,6 @@ protected void onCreate(Bundle savedInstanceState) { ); } - private void setSearchHistoryFragment() { - recentSearchesFragment = new RecentSearchesFragment(); - FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction.add(R.id.searchHistoryContainer, recentSearchesFragment).commit(); - } - - private void setBrowseImagesFragment() { searchImageFragment = new SearchImageFragment(); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java new file mode 100755 index 0000000000..d01a124b8a --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java @@ -0,0 +1,44 @@ +package fr.free.nrw.commons.explore; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; + +import java.util.ArrayList; +import java.util.List; + + +class ViewPagerAdapter extends FragmentPagerAdapter { + private List fragmentList = new ArrayList<>(); + private List fragmentTitleList = new ArrayList<>(); + + public ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + + @Override + public Fragment getItem(int position) { + return fragmentList.get(position); + } + + @Override + public int getCount() { + return fragmentList.size(); + } + + public void setTabData(List fragmentList, List fragmentTitleList) { + this.fragmentList = fragmentList; + this.fragmentTitleList = fragmentTitleList; + } + + @Override + public CharSequence getPageTitle(int position) { + return fragmentTitleList.get(position); + } + + + @Override + public int getItemPosition(Object object) { + return POSITION_NONE; + } +} From 28e81ca380d563cf7aba73af3cc453371c15b12b Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 12:03:19 +0530 Subject: [PATCH 55/94] Updated XML --- .../nrw/commons/explore/SearchActivity.java | 32 ++++++++++++------- .../categories/SearchCategoryFragment.java | 2 +- app/src/main/res/layout/activity_search.xml | 32 +++++++++++++------ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index a387d4d70c..c92a33e29a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -2,9 +2,11 @@ import android.database.DataSetObserver; import android.os.Bundle; +import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.View; @@ -37,10 +39,14 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetailPagerFragment.MediaDetailProvider{ @BindView(R.id.toolbar_search) Toolbar toolbar; - @BindView(R.id.fragmentContainer) FrameLayout resultsContainer; +// @BindView(R.id.fragmentContainer) FrameLayout resultsContainer; @BindView(R.id.searchHistoryContainer) FrameLayout searchHistoryContainer; @BindView(R.id.searchBox) SearchView searchView; + @BindView(R.id.tabLayout) TabLayout tabLayout; + @BindView(R.id.viewPager) ViewPager viewPager; + private SearchImageFragment searchImageFragment; + private SearchCategoryFragment searchCategoryFragment; private RecentSearchesFragment recentSearchesFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; @@ -56,8 +62,12 @@ protected void onCreate(Bundle savedInstanceState) { setTitle(getString(R.string.title_activity_search)); toolbar.setNavigationOnClickListener(v->onBackPressed()); supportFragmentManager = getSupportFragmentManager(); - setBrowseImagesFragment(); +// setBrowseImagesFragment(); setSearchHistoryFragment(); + viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); + viewPager.setAdapter(viewPagerAdapter); + tabLayout.setupWithViewPager(viewPager); + setTabs(); searchView.setQueryHint(getString(R.string.search_commons)); searchView.onActionViewExpanded(); searchView.clearFocus(); @@ -73,12 +83,12 @@ private void setSearchHistoryFragment() { public void setTabs() { List fragmentList = new ArrayList<>(); List titleList = new ArrayList<>(); - SearchImageFragment browseImageFragment = new SearchImageFragment(); - SearchCategoryFragment browseCategoryFragment = new SearchCategoryFragment(); - fragmentList.add(browseImageFragment); + searchImageFragment = new SearchImageFragment(); + searchCategoryFragment= new SearchCategoryFragment(); + fragmentList.add(searchImageFragment); titleList.add("IMAGES"); - fragmentList.add(browseCategoryFragment); - titleList.add("CATEGORIES"); +// fragmentList.add(searchCategoryFragment); +// titleList.add("CATEGORIES"); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); @@ -89,13 +99,13 @@ public void setTabs() { .subscribe( query -> { //update image list if (!TextUtils.isEmpty(query)) { - resultsContainer.setVisibility(View.VISIBLE); + viewPager.setVisibility(View.VISIBLE); searchHistoryContainer.setVisibility(View.GONE); this.query = query.toString(); searchImageFragment.updateImageList(query.toString()); - browseCategoryFragment.updateCategoryList(filter.toString()); + searchCategoryFragment.updateCategoryList(query.toString()); }else { - resultsContainer.setVisibility(View.GONE); + viewPager.setVisibility(View.GONE); searchHistoryContainer.setVisibility(View.VISIBLE); // open search history fragment } @@ -190,6 +200,6 @@ public void updateText(String query) { searchView.setQuery(query, true);; // Clear focus of searchView now. searchView.clearFocus(); does not seem to work Check the below link for more details. // https://stackoverflow.com/questions/6117967/how-to-remove-focus-without-setting-focus-to-another-control/15481511 - resultsContainer.requestFocus(); + viewPager.requestFocus(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index c9ce465c23..f64965f0f9 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -118,7 +118,7 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { * Checks for internet connection and then initializes the recycler view with 25 images of the searched query * Clearing imageAdapter every time new keyword is searched so that user can see only new results */ - public void updateImageList(String query) { + public void updateCategoryList(String query) { this.query = query; if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { handleNoInternet(); diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index f2f9d50c0a..7715c8a30f 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -40,19 +40,33 @@ android:iconifiedByDefault="false" android:textSize="@dimen/normal_text" /> - + - + + + + + + + + + + + Date: Tue, 19 Jun 2018 13:15:19 +0530 Subject: [PATCH 56/94] Improvements in category card design --- .../nrw/commons/di/FragmentBuilderModule.java | 4 ++ .../nrw/commons/explore/SearchActivity.java | 13 +----- .../SearchCategoriesAdapterFactory.java | 12 ++---- .../categories/SearchCategoriesRenderer.java | 17 ++++---- .../categories/SearchCategoryFragment.java | 41 ++++--------------- .../mwapi/ApacheHttpClientMediaWikiApi.java | 37 +++++++++++++++++ .../free/nrw/commons/mwapi/MediaWikiApi.java | 3 ++ app/src/main/res/layout/activity_search.xml | 21 +++++----- 8 files changed, 76 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index 6df2ea72a2..bba7660a11 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -5,6 +5,7 @@ import fr.free.nrw.commons.category.CategorizationFragment; import fr.free.nrw.commons.contributions.ContributionsListFragment; import fr.free.nrw.commons.category.CategoryImagesListFragment; +import fr.free.nrw.commons.explore.categories.SearchCategoryFragment; import fr.free.nrw.commons.explore.images.SearchImageFragment; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment; import fr.free.nrw.commons.media.MediaDetailFragment; @@ -56,6 +57,9 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract SearchImageFragment bindBrowseImagesListFragment(); + @ContributesAndroidInjector + abstract SearchCategoryFragment bindSearchCategoryListFragment(); + @ContributesAndroidInjector abstract RecentSearchesFragment bindRecentSearchesFragment(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index c92a33e29a..4160774d22 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -39,7 +39,6 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetailPagerFragment.MediaDetailProvider{ @BindView(R.id.toolbar_search) Toolbar toolbar; -// @BindView(R.id.fragmentContainer) FrameLayout resultsContainer; @BindView(R.id.searchHistoryContainer) FrameLayout searchHistoryContainer; @BindView(R.id.searchBox) SearchView searchView; @BindView(R.id.tabLayout) TabLayout tabLayout; @@ -62,7 +61,6 @@ protected void onCreate(Bundle savedInstanceState) { setTitle(getString(R.string.title_activity_search)); toolbar.setNavigationOnClickListener(v->onBackPressed()); supportFragmentManager = getSupportFragmentManager(); -// setBrowseImagesFragment(); setSearchHistoryFragment(); viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(viewPagerAdapter); @@ -87,8 +85,8 @@ public void setTabs() { searchCategoryFragment= new SearchCategoryFragment(); fragmentList.add(searchImageFragment); titleList.add("IMAGES"); -// fragmentList.add(searchCategoryFragment); -// titleList.add("CATEGORIES"); + fragmentList.add(searchCategoryFragment); + titleList.add("CATEGORIES"); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); @@ -113,13 +111,6 @@ public void setTabs() { ); } - private void setBrowseImagesFragment() { - searchImageFragment = new SearchImageFragment(); - FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction.add(R.id.fragmentContainer, searchImageFragment).commit(); - - } - @Override public Media getMediaAtPosition(int i) { return searchImageFragment.getImageAtPosition(i); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index e4d5efa732..52ff9519dd 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -7,9 +7,6 @@ import java.util.Collections; import java.util.List; -import fr.free.nrw.commons.Media; - - class SearchCategoriesAdapterFactory { private final SearchCategoriesRenderer.ImageClickedListener listener; @@ -17,11 +14,10 @@ class SearchCategoriesAdapterFactory { this.listener = listener; } - public RVRendererAdapter create(List searchImageItemList) { - RendererBuilder builder = new RendererBuilder() - .bind(Media.class, new SearchCategoriesRenderer(listener)); - ListAdapteeCollection collection = new ListAdapteeCollection<>( - searchImageItemList != null ? searchImageItemList : Collections.emptyList()); + public RVRendererAdapter create(List searchImageItemList) { + RendererBuilder builder = new RendererBuilder().bind(String.class, new SearchCategoriesRenderer(listener)); + ListAdapteeCollection collection = new ListAdapteeCollection<>( + searchImageItemList != null ? searchImageItemList : Collections.emptyList()); return new RVRendererAdapter<>(builder, collection); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java index ac61b24759..32f63a502e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -9,15 +9,14 @@ import butterknife.BindView; import butterknife.ButterKnife; -import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; /** - * presentation logic of individual image in search is handled here + * presentation logic of individual category in search is handled here */ -class SearchCategoriesRenderer extends Renderer { - @BindView(R.id.categoryImageTitle) TextView tvImageName; +class SearchCategoriesRenderer extends Renderer { + @BindView(R.id.textView1) TextView tvCategoryName; private final ImageClickedListener listener; @@ -27,7 +26,7 @@ class SearchCategoriesRenderer extends Renderer { @Override protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return layoutInflater.inflate(R.layout.layout_category_images, viewGroup, false); + return layoutInflater.inflate(R.layout.item_recent_searches, viewGroup, false); } @Override @@ -38,7 +37,7 @@ protected void setUpView(View view) { @Override protected void hookListeners(View view) { view.setOnClickListener(v -> { - Media item = getContent(); + String item = getContent(); if (listener != null) { listener.imageClicked(item); } @@ -47,12 +46,12 @@ protected void hookListeners(View view) { @Override public void render() { - Media item = getContent(); - tvImageName.setText(item.getFilename()); + String item = getContent(); + tvCategoryName.setText(item); } interface ImageClickedListener { - void imageClicked(Media item); + void imageClicked(String item); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index f64965f0f9..58d43f7ffe 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -26,7 +26,6 @@ import butterknife.BindView; import butterknife.ButterKnife; -import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; @@ -63,8 +62,8 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; @Inject @Named("default_preferences") SharedPreferences prefs; - private RVRendererAdapter imagesAdapter; - private List queryList = new ArrayList<>(); + private RVRendererAdapter imagesAdapter; + private List queryList = new ArrayList<>(); private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { int index = queryList.indexOf(item); @@ -98,7 +97,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav else{ imagesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); } - ArrayList items = new ArrayList<>(); + ArrayList items = new ArrayList<>(); imagesAdapter = adapterFactory.create(items); imagesRecyclerView.setAdapter(imagesAdapter); imagesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -127,7 +126,7 @@ public void updateCategoryList(String query) { progressBar.setVisibility(View.VISIBLE); queryList.clear(); imagesAdapter.clear(); - Observable.fromCallable(() -> mwApi.searchImages(query,queryList.size())) + Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) @@ -141,7 +140,7 @@ public void updateCategoryList(String query) { public void addImagesToList(String query) { this.query = query; progressBar.setVisibility(View.VISIBLE); - Observable.fromCallable(() -> mwApi.searchImages(query,queryList.size())) + Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) @@ -153,7 +152,7 @@ public void addImagesToList(String query) { * it initializes the recycler view by adding items to the adapter * @param mediaList */ - private void handlePaginationSuccess(List mediaList) { + private void handlePaginationSuccess(List mediaList) { queryList.addAll(mediaList); progressBar.setVisibility(View.GONE); imagesAdapter.addAll(mediaList); @@ -167,7 +166,7 @@ private void handlePaginationSuccess(List mediaList) { * it initializes the recycler view by adding items to the adapter * @param mediaList */ - private void handleSuccess(List mediaList) { + private void handleSuccess(List mediaList) { imagesNotFoundView.setVisibility(GONE); queryList = mediaList; if(mediaList == null || mediaList.isEmpty()) { @@ -211,30 +210,4 @@ private void handleNoInternet() { progressBar.setVisibility(GONE); ViewUtil.showSnackbar(imagesRecyclerView, R.string.no_internet); } - - /** - * returns total number of images present in the recyclerview adapter. - */ - public int getTotalImagesCount(){ - if (imagesAdapter == null) { - return 0; - } - else { - return imagesAdapter.getItemCount(); - } - } - - /** - * returns Media Object at position - * @param i position of Media in the recyclerview adapter. - */ - public Media getImageAtPosition(int i) { - if (imagesAdapter.getItem(i).getFilename() == null) { - // not yet ready to return data - return null; - } - else { - return new Media(imagesAdapter.getItem(i).getFilename()); - } - } } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 32b7a3d277..1671819484 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -660,6 +660,43 @@ public List searchImages(String query, int offset) { return images; } + /** + * This method takes search keyword as input and returns a list of categories objects filtered using search query + * It uses the generator query API to get the images searched using a query, 25 at a time. + * @param query keyword to search images on commons + * @return + */ + @Override + @NonNull + public List searchCategory(String query, int offset) { + List imageNodes = null; + try { + imageNodes = api.action("query") + .param("format", "xml") + .param("list", "search") + .param("srwhat", "text") + .param("srnamespace", "14") + .param("srlimit", "25") + .param("sroffset",offset) + .param("srsearch", query) + .get() + .getNodes("/api/query/search/p/@title"); + } catch (IOException e) { + Timber.e("Failed to obtain searchImages", e); + } + + if (imageNodes == null) { + return new ArrayList(); + } + + List categories = new ArrayList<>(); + for (ApiResult imageNode : imageNodes) { + String catName = imageNode.getDocument().getTextContent(); + categories.add(catName); + } + return categories; + } + /** * For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index a9d334d083..215c747f71 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -44,6 +44,9 @@ public interface MediaWikiApi { @NonNull List searchImages(String title, int offset); + @NonNull + List searchCategory(String title, int offset); + @NonNull UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener) throws IOException; diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 7715c8a30f..e4103189a9 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -40,18 +40,18 @@ android:iconifiedByDefault="false" android:textSize="@dimen/normal_text" /> - + @@ -67,6 +67,7 @@ android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_below="@id/tabLayout" /> Date: Tue, 19 Jun 2018 13:51:53 +0530 Subject: [PATCH 57/94] tabs colors changed --- .../categories/SearchCategoryFragment.java | 33 +++++++++---------- app/src/main/res/layout/activity_search.xml | 22 ++++--------- .../main/res/layout/fragment_browse_image.xml | 2 +- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index 58d43f7ffe..b1493d1a29 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -28,7 +28,6 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; -import fr.free.nrw.commons.explore.SearchActivity; import fr.free.nrw.commons.explore.recentsearches.RecentSearch; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; import fr.free.nrw.commons.mwapi.MediaWikiApi; @@ -51,7 +50,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { private static int TIMEOUT_SECONDS = 15; @BindView(R.id.imagesListBox) - RecyclerView imagesRecyclerView; + RecyclerView categoriesRecyclerView; @BindView(R.id.imageSearchInProgress) ProgressBar progressBar; @BindView(R.id.imagesNotFound) @@ -62,12 +61,11 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; @Inject @Named("default_preferences") SharedPreferences prefs; - private RVRendererAdapter imagesAdapter; + private RVRendererAdapter categoriesAdapter; private List queryList = new ArrayList<>(); private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { - int index = queryList.indexOf(item); - ((SearchActivity)getContext()).onSearchImageClicked(index); + // Open Category Details activity saveQuery(query); }); @@ -81,7 +79,6 @@ private void saveQuery(String query) { else { recentSearch.setLastSearched(new Date()); } - recentSearchesDao.save(recentSearch); } @@ -92,15 +89,15 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); ButterKnife.bind(this, rootView); if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ - imagesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + categoriesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); } else{ - imagesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); + categoriesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); } ArrayList items = new ArrayList<>(); - imagesAdapter = adapterFactory.create(items); - imagesRecyclerView.setAdapter(imagesAdapter); - imagesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + categoriesAdapter = adapterFactory.create(items); + categoriesRecyclerView.setAdapter(categoriesAdapter); + categoriesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); @@ -125,7 +122,7 @@ public void updateCategoryList(String query) { } progressBar.setVisibility(View.VISIBLE); queryList.clear(); - imagesAdapter.clear(); + categoriesAdapter.clear(); Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -155,8 +152,8 @@ public void addImagesToList(String query) { private void handlePaginationSuccess(List mediaList) { queryList.addAll(mediaList); progressBar.setVisibility(View.GONE); - imagesAdapter.addAll(mediaList); - imagesAdapter.notifyDataSetChanged(); + categoriesAdapter.addAll(mediaList); + categoriesAdapter.notifyDataSetChanged(); } @@ -175,8 +172,8 @@ private void handleSuccess(List mediaList) { else { progressBar.setVisibility(View.GONE); - imagesAdapter.addAll(mediaList); - imagesAdapter.notifyDataSetChanged(); + categoriesAdapter.addAll(mediaList); + categoriesAdapter.notifyDataSetChanged(); // check if user is waiting for 5 seconds if yes then save search query to history. Handler handler = new Handler(); @@ -191,7 +188,7 @@ private void handleSuccess(List mediaList) { private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading queried images"); initErrorView(); - ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); + ViewUtil.showSnackbar(categoriesRecyclerView, R.string.error_loading_images); } /** @@ -208,6 +205,6 @@ private void initErrorView() { */ private void handleNoInternet() { progressBar.setVisibility(GONE); - ViewUtil.showSnackbar(imagesRecyclerView, R.string.no_internet); + ViewUtil.showSnackbar(categoriesRecyclerView, R.string.no_internet); } } diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index e4103189a9..d46351b643 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -41,33 +41,23 @@ android:textSize="@dimen/normal_text" /> - - - - - - - - - - - - + Date: Tue, 19 Jun 2018 14:08:51 +0530 Subject: [PATCH 58/94] renamed images to media --- .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 2 +- app/src/main/res/layout/activity_search.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 4160774d22..6ee47c04e2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -84,7 +84,7 @@ public void setTabs() { searchImageFragment = new SearchImageFragment(); searchCategoryFragment= new SearchCategoryFragment(); fragmentList.add(searchImageFragment); - titleList.add("IMAGES"); + titleList.add("MEDIA"); fragmentList.add(searchCategoryFragment); titleList.add("CATEGORIES"); diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index d46351b643..c005704a8b 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -39,6 +39,7 @@ android:singleLine="true" android:iconifiedByDefault="false" android:textSize="@dimen/normal_text" + android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" /> Date: Tue, 19 Jun 2018 15:08:46 +0530 Subject: [PATCH 59/94] Java docs improved --- .../SearchCategoriesAdapterFactory.java | 4 ++-- .../categories/SearchCategoriesRenderer.java | 10 ++++----- .../categories/SearchCategoryFragment.java | 22 +++++++++---------- .../mwapi/ApacheHttpClientMediaWikiApi.java | 16 +++++++------- app/src/main/res/values/strings.xml | 1 + 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index 52ff9519dd..9ad8883ded 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -8,9 +8,9 @@ import java.util.List; class SearchCategoriesAdapterFactory { - private final SearchCategoriesRenderer.ImageClickedListener listener; + private final SearchCategoriesRenderer.CategoryClickedListener listener; - SearchCategoriesAdapterFactory(SearchCategoriesRenderer.ImageClickedListener listener) { + SearchCategoriesAdapterFactory(SearchCategoriesRenderer.CategoryClickedListener listener) { this.listener = listener; } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java index 32f63a502e..1d4c272e30 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -18,9 +18,9 @@ class SearchCategoriesRenderer extends Renderer { @BindView(R.id.textView1) TextView tvCategoryName; - private final ImageClickedListener listener; + private final CategoryClickedListener listener; - SearchCategoriesRenderer(ImageClickedListener listener) { + SearchCategoriesRenderer(CategoryClickedListener listener) { this.listener = listener; } @@ -39,7 +39,7 @@ protected void hookListeners(View view) { view.setOnClickListener(v -> { String item = getContent(); if (listener != null) { - listener.imageClicked(item); + listener.categoryClicked(item); } }); } @@ -50,8 +50,8 @@ public void render() { tvCategoryName.setText(item); } - interface ImageClickedListener { - void imageClicked(String item); + interface CategoryClickedListener { + void categoryClicked(String item); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index b1493d1a29..51c2152fd8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -42,7 +42,7 @@ import static android.view.View.VISIBLE; /** - * Displays the image search screen. + * Displays the category search screen. */ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { @@ -54,7 +54,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { @BindView(R.id.imageSearchInProgress) ProgressBar progressBar; @BindView(R.id.imagesNotFound) - TextView imagesNotFoundView; + TextView categoriesNotFoundView; String query; @Inject RecentSearchesDao recentSearchesDao; @@ -103,7 +103,7 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); // check if end of recycler view is reached, if yes then add more results to existing results if (!recyclerView.canScrollVertically(1)) { - addImagesToList(query); + addCategoriesToList(query); } } }); @@ -111,8 +111,8 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } /** - * Checks for internet connection and then initializes the recycler view with 25 images of the searched query - * Clearing imageAdapter every time new keyword is searched so that user can see only new results + * Checks for internet connection and then initializes the recycler view with 25 categories of the searched query + * Clearing categoryAdapter every time new keyword is searched so that user can see only new results */ public void updateCategoryList(String query) { this.query = query; @@ -134,7 +134,7 @@ public void updateCategoryList(String query) { /** * Adds more results to existing search results */ - public void addImagesToList(String query) { + public void addCategoriesToList(String query) { this.query = query; progressBar.setVisibility(View.VISIBLE); Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size())) @@ -164,7 +164,7 @@ private void handlePaginationSuccess(List mediaList) { * @param mediaList */ private void handleSuccess(List mediaList) { - imagesNotFoundView.setVisibility(GONE); + categoriesNotFoundView.setVisibility(GONE); queryList = mediaList; if(mediaList == null || mediaList.isEmpty()) { initErrorView(); @@ -186,9 +186,9 @@ private void handleSuccess(List mediaList) { * @param throwable */ private void handleError(Throwable throwable) { - Timber.e(throwable, "Error occurred while loading queried images"); + Timber.e(throwable, "Error occurred while loading queried categories"); initErrorView(); - ViewUtil.showSnackbar(categoriesRecyclerView, R.string.error_loading_images); + ViewUtil.showSnackbar(categoriesRecyclerView, R.string.error_loading_categories); } /** @@ -196,8 +196,8 @@ private void handleError(Throwable throwable) { */ private void initErrorView() { progressBar.setVisibility(GONE); - imagesNotFoundView.setVisibility(VISIBLE); - imagesNotFoundView.setText(getString(R.string.images_not_found, query)); + categoriesNotFoundView.setVisibility(VISIBLE); + categoriesNotFoundView.setText(getString(R.string.categories_not_found, query)); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 1671819484..2fec8518a0 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -662,16 +662,16 @@ public List searchImages(String query, int offset) { /** * This method takes search keyword as input and returns a list of categories objects filtered using search query - * It uses the generator query API to get the images searched using a query, 25 at a time. - * @param query keyword to search images on commons + * It uses the generator query API to get the categories searched using a query, 25 at a time. + * @param query keyword to search categories on commons * @return */ @Override @NonNull public List searchCategory(String query, int offset) { - List imageNodes = null; + List categoryNodes = null; try { - imageNodes = api.action("query") + categoryNodes = api.action("query") .param("format", "xml") .param("list", "search") .param("srwhat", "text") @@ -682,16 +682,16 @@ public List searchCategory(String query, int offset) { .get() .getNodes("/api/query/search/p/@title"); } catch (IOException e) { - Timber.e("Failed to obtain searchImages", e); + Timber.e("Failed to obtain searchCategories", e); } - if (imageNodes == null) { + if (categoryNodes == null) { return new ArrayList(); } List categories = new ArrayList<>(); - for (ApiResult imageNode : imageNodes) { - String catName = imageNode.getDocument().getTextContent(); + for (ApiResult categoryNode : categoryNodes) { + String catName = categoryNode.getDocument().getTextContent(); categories.add(catName); } return categories; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ba94c9a90..990e2cf1e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -275,6 +275,7 @@ You can upload a picture for any place from your gallery or camera No images found! Error occurred while loading images. + Error occurred while loading categories. Uploaded by: %1$s Share App Coordinates were not specified during image selection From 202f3536dca9c93428506bb4459ecdfc52c4c79d Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 15:27:21 +0530 Subject: [PATCH 60/94] Javadoc added for setTabs --- .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 6ee47c04e2..8ac272c107 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -78,6 +78,9 @@ private void setSearchHistoryFragment() { transaction.add(R.id.searchHistoryContainer, recentSearchesFragment).commit(); } + /** + * Sets the titles in the tabLayout and fragments in the viewPager + */ public void setTabs() { List fragmentList = new ArrayList<>(); List titleList = new ArrayList<>(); From b1ff3cc186b4652418da43b8b1063b3d78adad32 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 15:31:46 +0530 Subject: [PATCH 61/94] JavaDoc for ViewPagerAdapter added --- .../java/fr/free/nrw/commons/explore/ViewPagerAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java index d01a124b8a..aae24c3a02 100755 --- a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java @@ -7,7 +7,9 @@ import java.util.ArrayList; import java.util.List; - +/** + * This adapter will be used to display fragments in a ViewPager + */ class ViewPagerAdapter extends FragmentPagerAdapter { private List fragmentList = new ArrayList<>(); private List fragmentTitleList = new ArrayList<>(); From 9d773524c2b33c1e816f977c8b73e4f37fb7f883 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 19 Jun 2018 15:58:30 +0530 Subject: [PATCH 62/94] Refreshed listview after delete --- .../explore/recentsearches/RecentSearchesFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java index 8b3cd87104..1023ca21b2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java @@ -44,6 +44,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, .setPositiveButton("YES", (dialog, which) -> { recentSearchesDao.deleteAll(recentSearches); Toast.makeText(getContext(),getString(R.string.search_history_deleted),Toast.LENGTH_SHORT).show(); + recentSearches = recentSearchesDao.recentSearches(10); + adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches, recentSearches); + recentSearchesList.setAdapter(adapter); + adapter.notifyDataSetChanged(); dialog.dismiss(); }) .setNegativeButton("NO", null) From 6ea071ffb4c22db17471f6210066dafb864788ef Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Fri, 22 Jun 2018 15:33:03 +0530 Subject: [PATCH 63/94] Added mediaContainer --- .../fr/free/nrw/commons/explore/SearchActivity.java | 12 ++++++++++-- .../explore/categories/SearchCategoryFragment.java | 1 - app/src/main/res/layout/activity_search.xml | 12 +++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 8ac272c107..7b0f30efd6 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -12,6 +12,7 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.SearchView; +import android.widget.Toast; import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxSearchView; @@ -40,6 +41,7 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai @BindView(R.id.toolbar_search) Toolbar toolbar; @BindView(R.id.searchHistoryContainer) FrameLayout searchHistoryContainer; + @BindView(R.id.mediaContainer) FrameLayout mediaContainer; @BindView(R.id.searchBox) SearchView searchView; @BindView(R.id.tabLayout) TabLayout tabLayout; @BindView(R.id.viewPager) ViewPager viewPager; @@ -146,6 +148,9 @@ public void unregisterDataSetObserver(DataSetObserver observer) { public void onSearchImageClicked(int index) { ViewUtil.hideKeyboard(this.findViewById(R.id.searchBox)); toolbar.setVisibility(View.GONE); + tabLayout.setVisibility(View.GONE); + viewPager.setVisibility(View.GONE); + mediaContainer.setVisibility(View.VISIBLE); setNavigationBaseToolbarVisibility(true); if (mediaDetails == null || !mediaDetails.isVisible()) { // set isFeaturedImage true for featured images, to include author field on media detail @@ -153,7 +158,7 @@ public void onSearchImageClicked(int index) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.fragmentContainer, mediaDetails) + .replace(R.id.mediaContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); @@ -179,6 +184,9 @@ public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount() == 1){ // back to search so show search toolbar and hide navigation toolbar toolbar.setVisibility(View.VISIBLE); + tabLayout.setVisibility(View.VISIBLE); + viewPager.setVisibility(View.VISIBLE); + mediaContainer.setVisibility(View.GONE); setNavigationBaseToolbarVisibility(false); if (!TextUtils.isEmpty(query)) { searchImageFragment.updateImageList(query); @@ -191,7 +199,7 @@ public void onBackPressed() { } public void updateText(String query) { - searchView.setQuery(query, true);; + searchView.setQuery(query, true); // Clear focus of searchView now. searchView.clearFocus(); does not seem to work Check the below link for more details. // https://stackoverflow.com/questions/6117967/how-to-remove-focus-without-setting-focus-to-another-control/15481511 viewPager.requestFocus(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index 51c2152fd8..c656c7abe2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -164,7 +164,6 @@ private void handlePaginationSuccess(List mediaList) { * @param mediaList */ private void handleSuccess(List mediaList) { - categoriesNotFoundView.setVisibility(GONE); queryList = mediaList; if(mediaList == null || mediaList.isEmpty()) { initErrorView(); diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index c005704a8b..98eeedff7a 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -54,6 +54,16 @@ android:layout_below="@id/toolbar_search" /> + + Date: Fri, 22 Jun 2018 16:05:26 +0530 Subject: [PATCH 64/94] Fixed ghost issue in image search fragment --- .../fr/free/nrw/commons/explore/images/SearchImageFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index fcc6ce950d..7e333284d3 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -116,6 +116,7 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { */ public void updateImageList(String query) { this.query = query; + imagesNotFoundView.setVisibility(GONE); if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { handleNoInternet(); return; @@ -164,7 +165,6 @@ private void handlePaginationSuccess(List mediaList) { * @param mediaList */ private void handleSuccess(List mediaList) { - imagesNotFoundView.setVisibility(GONE); queryList = mediaList; if(mediaList == null || mediaList.isEmpty()) { initErrorView(); From 9e72061e9e846b3caef055be9806eec35296dd9e Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Fri, 22 Jun 2018 16:46:26 +0530 Subject: [PATCH 65/94] Ghost issue for categories fixed --- .../nrw/commons/explore/categories/SearchCategoryFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index c656c7abe2..bb792298ff 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -116,6 +116,7 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { */ public void updateCategoryList(String query) { this.query = query; + categoriesNotFoundView.setVisibility(GONE); if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { handleNoInternet(); return; From 1ee2dc9d047400463646aa45bd625b8a0ee04ae7 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 26 Jun 2018 21:51:03 +0530 Subject: [PATCH 66/94] Removed Calling API call onback press --- .../java/fr/free/nrw/commons/explore/SearchActivity.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 7b0f30efd6..ad57954bc2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -158,7 +158,8 @@ public void onSearchImageClicked(int index) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.mediaContainer, mediaDetails) + .hide(supportFragmentManager.getFragments().get(supportFragmentManager.getBackStackEntryCount())) + .add(R.id.mediaContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); @@ -188,9 +189,6 @@ public void onBackPressed() { viewPager.setVisibility(View.VISIBLE); mediaContainer.setVisibility(View.GONE); setNavigationBaseToolbarVisibility(false); - if (!TextUtils.isEmpty(query)) { - searchImageFragment.updateImageList(query); - } }else { toolbar.setVisibility(View.GONE); setNavigationBaseToolbarVisibility(true); From 295223c91a53dee40f46b744cda72752bc02d8e1 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 27 Jun 2018 01:40:02 +0530 Subject: [PATCH 67/94] Category Details activity added --- app/src/main/AndroidManifest.xml | 5 + .../category/CategoryDetailsActivity.java | 165 ++++++++++++++++++ .../commons/category/CategoryImageUtils.java | 6 +- .../nrw/commons/di/ActivityBuilderModule.java | 4 + .../categories/SearchCategoryFragment.java | 2 + .../res/menu/fragment_category_detail.xml | 9 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java create mode 100644 app/src/main/res/menu/fragment_category_detail.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 21d66a8e7f..37b0c8ddde 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -97,6 +97,11 @@ android:label="@string/title_activity_featured_images" android:parentActivityName=".contributions.ContributionsActivity" /> + + adapterView, View view, int i, long l) { + if (mediaDetails == null || !mediaDetails.isVisible()) { + // set isFeaturedImage true for featured images, to include author field on media detail + mediaDetails = new MediaDetailPagerFragment(false, true); + FragmentManager supportFragmentManager = getSupportFragmentManager(); + supportFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, mediaDetails) + .addToBackStack(null) + .commit(); + supportFragmentManager.executePendingTransactions(); + } + mediaDetails.showImage(i); + initBackButton(); + } + + @Override + protected void onResume() { + if (supportFragmentManager.getBackStackEntryCount()==1){ + //FIXME: Temporary fix for screen rotation inside media details. If we don't call onBackPressed then fragment stack is increasing every time. + //FIXME: Similar issue like this https://github.com/commons-app/apps-android-commons/issues/894 + onBackPressed(); + } + super.onResume(); + } + + /** + * Consumers should be simply using this method to use this activity. + * @param context + * @param title Page title + * @param categoryName Name of the category for displaying its images + */ + public static void startYourself(Context context, String title, String categoryName) { + Intent intent = new Intent(context, CategoryDetailsActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + intent.putExtra("title", title); + intent.putExtra("categoryName", categoryName); + context.startActivity(intent); + } + + @Override + public Media getMediaAtPosition(int i) { + if (categoryImagesListFragment.getAdapter() == null) { + // not yet ready to return data + return null; + } else { + return (Media) categoryImagesListFragment.getAdapter().getItem(i); + } + } + + @Override + public int getTotalMediaCount() { + if (categoryImagesListFragment.getAdapter() == null) { + return 0; + } + return categoryImagesListFragment.getAdapter().getCount(); + } + + @Override + public void notifyDatasetChanged() { + + } + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index 18749847ee..1722628db8 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons.category; +import android.util.Log; + import org.jsoup.Jsoup; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -27,7 +29,9 @@ public static List getMediaList(NodeList childNodes) { List categoryImages = new ArrayList<>(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); - categoryImages.add(getMediaFromPage(node)); + if (getMediaFromPage(node).getFilename().substring(0,5).equals("File:")){ + categoryImages.add(getMediaFromPage(node)); + } } return categoryImages; diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java index d8f4e376b3..48248225e4 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java @@ -6,6 +6,7 @@ import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.auth.SignupActivity; +import fr.free.nrw.commons.category.CategoryDetailsActivity; import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.explore.SearchActivity; @@ -54,4 +55,7 @@ public abstract class ActivityBuilderModule { @ContributesAndroidInjector abstract SearchActivity bindSearchActivity(); + + @ContributesAndroidInjector + abstract CategoryDetailsActivity bindCategoryDetailsActivity(); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index bb792298ff..4cb4f8047b 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -27,6 +27,7 @@ import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.category.CategoryDetailsActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.recentsearches.RecentSearch; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; @@ -66,6 +67,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { // Open Category Details activity + CategoryDetailsActivity.startYourself(getContext(), item, item); saveQuery(query); }); diff --git a/app/src/main/res/menu/fragment_category_detail.xml b/app/src/main/res/menu/fragment_category_detail.xml new file mode 100644 index 0000000000..fb23763e30 --- /dev/null +++ b/app/src/main/res/menu/fragment_category_detail.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 990e2cf1e3..e348824fc3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -84,6 +84,7 @@ Settings Sign Up Featured Images + Category About The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. Wikimedia Commons From 72756493f38a500404be0175c1bc32cd01ce90f2 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 27 Jun 2018 01:48:31 +0530 Subject: [PATCH 68/94] Menu added in category details activity --- .../category/CategoryDetailsActivity.java | 44 +++++++++++++++++-- .../commons/category/CategoryImageUtils.java | 2 - .../commons/media/MediaDetailFragment.java | 12 +---- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 157a1a5a9e..57d94b8289 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -6,14 +6,22 @@ import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; +import android.widget.Toast; import butterknife.ButterKnife; import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.PageTitle; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.media.MediaDetailPagerFragment; +import fr.free.nrw.commons.theme.NavigationBaseActivity; + +import static android.widget.Toast.LENGTH_SHORT; /** * This activity displays pictures of a particular category @@ -32,6 +40,7 @@ public class CategoryDetailsActivity private FragmentManager supportFragmentManager; private CategoryImagesListFragment categoryImagesListFragment; private MediaDetailPagerFragment mediaDetails; + private String categoryName; @Override protected void onAuthCookieAcquired(String authCookie) { @@ -51,12 +60,12 @@ protected void onCreate(Bundle savedInstanceState) { // Activity can call methods in the fragment by acquiring a // reference to the Fragment from FragmentManager, using findFragmentById() - initDrawer(); supportFragmentManager = getSupportFragmentManager(); setCategoryImagesFragment(); supportFragmentManager.addOnBackStackChangedListener(this); requestAuthToken(); setPageTitle(); + initDrawer(); } /** @@ -64,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { */ private void setCategoryImagesFragment() { categoryImagesListFragment = new CategoryImagesListFragment(); - String categoryName = getIntent().getStringExtra("categoryName"); + categoryName = getIntent().getStringExtra("categoryName"); if (getIntent() != null && categoryName != null) { Bundle arguments = new Bundle(); arguments.putString("categoryName", categoryName); @@ -108,6 +117,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { @Override protected void onResume() { + initBackButton(); if (supportFragmentManager.getBackStackEntryCount()==1){ //FIXME: Temporary fix for screen rotation inside media details. If we don't call onBackPressed then fragment stack is increasing every time. //FIXME: Similar issue like this https://github.com/commons-app/apps-android-commons/issues/894 @@ -155,11 +165,39 @@ public void notifyDatasetChanged() { @Override public void registerDataSetObserver(DataSetObserver observer) { - } @Override public void unregisterDataSetObserver(DataSetObserver observer) { } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.fragment_category_detail, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + // Handle item selection + switch (item.getItemId()) { + case R.id.menu_browser_current_category: + Intent viewIntent = new Intent(); + viewIntent.setAction(Intent.ACTION_VIEW); + viewIntent.setData(new PageTitle(categoryName).getCanonicalUri()); + //check if web browser available + if (viewIntent.resolveActivity(this.getPackageManager()) != null) { + startActivity(viewIntent); + } else { + Toast toast = Toast.makeText(this, getString(R.string.no_web_browser), LENGTH_SHORT); + toast.show(); + } + return true; + default: + return super.onOptionsItemSelected(item); + } + } } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index 1722628db8..02e55651b0 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -1,7 +1,5 @@ package fr.free.nrw.commons.category; -import android.util.Log; - import org.jsoup.Jsoup; import org.w3c.dom.Element; import org.w3c.dom.Node; diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 54bb5981c7..789d96a931 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -45,6 +45,7 @@ import fr.free.nrw.commons.MediaWikiImageView; import fr.free.nrw.commons.PageTitle; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.category.CategoryDetailsActivity; import fr.free.nrw.commons.delete.DeleteTask; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.location.LatLng; @@ -430,16 +431,7 @@ private View buildCatLabel(final String catName, ViewGroup categoryContainer) { if (categoriesLoaded && categoriesPresent) { textView.setOnClickListener(view -> { String selectedCategoryTitle = "Category:" + catName; - Intent viewIntent = new Intent(); - viewIntent.setAction(Intent.ACTION_VIEW); - viewIntent.setData(new PageTitle(selectedCategoryTitle).getCanonicalUri()); - //check if web browser available - if (viewIntent.resolveActivity(getActivity().getPackageManager()) != null) { - startActivity(viewIntent); - } else { - Toast toast = Toast.makeText(getContext(), getString(R.string.no_web_browser), LENGTH_SHORT); - toast.show(); - } + CategoryDetailsActivity.startYourself(getContext(), selectedCategoryTitle, selectedCategoryTitle); }); } return item; From 69edaf7ff3fbb15b6d887b90341f25a3cf4d8e40 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 27 Jun 2018 01:50:17 +0530 Subject: [PATCH 69/94] back button added --- .../fr/free/nrw/commons/category/CategoryDetailsActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 57d94b8289..6a0ab764d2 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -19,7 +19,6 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.media.MediaDetailPagerFragment; -import fr.free.nrw.commons.theme.NavigationBaseActivity; import static android.widget.Toast.LENGTH_SHORT; @@ -66,6 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { requestAuthToken(); setPageTitle(); initDrawer(); + initBackButton(); } /** From d2069877cb1c2c4ef4650b63388787e25d5839af Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 28 Jun 2018 16:48:53 +0530 Subject: [PATCH 70/94] back button bugs --- .../category/CategoryDetailsActivity.java | 51 +++++-------------- .../category/CategoryImagesActivity.java | 9 +++- .../category/CategoryImagesListFragment.java | 11 +--- .../categories/SearchCategoryFragment.java | 2 +- .../commons/media/MediaDetailFragment.java | 6 ++- .../commons/theme/NavigationBaseActivity.java | 3 +- 6 files changed, 28 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 6a0ab764d2..e2decb8e38 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -19,6 +19,7 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.media.MediaDetailPagerFragment; +import fr.free.nrw.commons.theme.NavigationBaseActivity; import static android.widget.Toast.LENGTH_SHORT; @@ -30,9 +31,8 @@ */ public class CategoryDetailsActivity - extends AuthenticatedActivity - implements FragmentManager.OnBackStackChangedListener, - MediaDetailPagerFragment.MediaDetailProvider, + extends NavigationBaseActivity + implements MediaDetailPagerFragment.MediaDetailProvider, AdapterView.OnItemClickListener{ @@ -41,16 +41,6 @@ public class CategoryDetailsActivity private MediaDetailPagerFragment mediaDetails; private String categoryName; - @Override - protected void onAuthCookieAcquired(String authCookie) { - - } - - @Override - protected void onAuthFailure() { - - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -60,9 +50,8 @@ protected void onCreate(Bundle savedInstanceState) { // Activity can call methods in the fragment by acquiring a // reference to the Fragment from FragmentManager, using findFragmentById() supportFragmentManager = getSupportFragmentManager(); +// supportFragmentManager.addOnBackStackChangedListener(this); setCategoryImagesFragment(); - supportFragmentManager.addOnBackStackChangedListener(this); - requestAuthToken(); setPageTitle(); initDrawer(); initBackButton(); @@ -79,25 +68,20 @@ private void setCategoryImagesFragment() { arguments.putString("categoryName", categoryName); categoryImagesListFragment.setArguments(arguments); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction - .add(R.id.fragmentContainer, categoryImagesListFragment) - .commit(); + transaction.replace(R.id.fragmentContainer, categoryImagesListFragment) + .commit(); } } /** - * Gets the passed title from the intents and displays it as the page title + * Gets the passed categoryName from the intents and displays it as the page title */ private void setPageTitle() { - if (getIntent() != null && getIntent().getStringExtra("title") != null) { - setTitle(getIntent().getStringExtra("title")); + if (getIntent() != null && getIntent().getStringExtra("categoryName") != null) { + setTitle(getIntent().getStringExtra("categoryName")); } } - @Override - public void onBackStackChanged() { - } - @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (mediaDetails == null || !mediaDetails.isVisible()) { @@ -106,7 +90,8 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.fragmentContainer, mediaDetails) + .hide(supportFragmentManager.getFragments().get(supportFragmentManager.getBackStackEntryCount())) + .add(R.id.fragmentContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); @@ -115,27 +100,15 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { initBackButton(); } - @Override - protected void onResume() { - initBackButton(); - if (supportFragmentManager.getBackStackEntryCount()==1){ - //FIXME: Temporary fix for screen rotation inside media details. If we don't call onBackPressed then fragment stack is increasing every time. - //FIXME: Similar issue like this https://github.com/commons-app/apps-android-commons/issues/894 - onBackPressed(); - } - super.onResume(); - } /** * Consumers should be simply using this method to use this activity. * @param context - * @param title Page title * @param categoryName Name of the category for displaying its images */ - public static void startYourself(Context context, String title, String categoryName) { + public static void startYourself(Context context, String categoryName) { Intent intent = new Intent(context, CategoryDetailsActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - intent.putExtra("title", title); intent.putExtra("categoryName", categoryName); context.startActivity(intent); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 8e54bd1e0d..c109307250 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -48,6 +48,12 @@ protected void onAuthFailure() { } + @Override + public void onBackPressed() { + initDrawer(); + super.onBackPressed(); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -102,7 +108,8 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .replace(R.id.fragmentContainer, mediaDetails) + .hide(supportFragmentManager.getFragments().get(supportFragmentManager.getBackStackEntryCount())) + .add(R.id.fragmentContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index a44e19a29c..7d4e3bf9df 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -140,12 +140,12 @@ private void handleError(Throwable throwable) { * Handles the UI updates for a error scenario */ private void initErrorView() { - ViewUtil.showSnackbar(gridView, R.string.error_loading_images); progressBar.setVisibility(GONE); if (gridAdapter == null || gridAdapter.isEmpty()) { statusTextView.setVisibility(VISIBLE); statusTextView.setText(getString(R.string.no_images_found)); } else { + ViewUtil.showSnackbar(gridView, R.string.error_loading_images); statusTextView.setVisibility(GONE); } } @@ -225,13 +225,4 @@ public ListAdapter getAdapter() { return gridView.getAdapter(); } - /** - * This method will be called on back pressed of CategoryImagesActivity. - * It initializes the grid view by setting adapter. - */ - @Override - public void onResume() { - gridView.setAdapter(gridAdapter); - super.onResume(); - } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index 4cb4f8047b..06b4c74449 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -67,7 +67,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { // Open Category Details activity - CategoryDetailsActivity.startYourself(getContext(), item, item); + CategoryDetailsActivity.startYourself(getContext(), item); saveQuery(query); }); diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 789d96a931..8b9e9dd6b7 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -431,7 +431,11 @@ private View buildCatLabel(final String catName, ViewGroup categoryContainer) { if (categoriesLoaded && categoriesPresent) { textView.setOnClickListener(view -> { String selectedCategoryTitle = "Category:" + catName; - CategoryDetailsActivity.startYourself(getContext(), selectedCategoryTitle, selectedCategoryTitle); + Intent intent = new Intent(getContext(), CategoryDetailsActivity.class); + intent.putExtra("categoryName", selectedCategoryTitle); + getContext().startActivity(intent); + +// CategoryDetailsActivity.startYourself(getContext(), selectedCategoryTitle); }); } return item; diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index 38ff8c1898..cc62fbd935 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -77,8 +77,7 @@ private void setUserName() { } public void initBackButton() { - int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); - toggle.setDrawerIndicatorEnabled(backStackEntryCount == 0); + toggle.setDrawerIndicatorEnabled(false); toggle.setToolbarNavigationClickListener(v -> onBackPressed()); } From da94f1217e2f94b9efbb1a188ad7b4cca7947fff Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 28 Jun 2018 17:08:59 +0530 Subject: [PATCH 71/94] Improvements in category images fragment --- .../free/nrw/commons/category/CategoryImagesListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 7d4e3bf9df..340dcc83f2 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -172,7 +172,7 @@ public void onScrollStateChanged(AbsListView view, int scrollState) { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount + 1 >= totalItemCount)) { + if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount >= totalItemCount)) { isLoading = true; fetchMoreImages(); } From 9b669177850ef40a23d7b0fa4112d9c27cc5c3e6 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 28 Jun 2018 17:13:36 +0530 Subject: [PATCH 72/94] JavaDoc added for some methods --- .../category/CategoryDetailsActivity.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index e2decb8e38..e0b227e8f8 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -24,14 +24,12 @@ import static android.widget.Toast.LENGTH_SHORT; /** - * This activity displays pictures of a particular category - * Its generic and simply takes the name of category name in its start intent to load all images in - * a particular category. This activity is currently being used to display a list of featured images, - * which is nothing but another category on wikimedia commons. + * This activity displays details of a particular category + * Its generic and simply takes the name of category name in its start intent to load all images, subcategories in + * a particular category on wikimedia commons. */ -public class CategoryDetailsActivity - extends NavigationBaseActivity +public class CategoryDetailsActivity extends NavigationBaseActivity implements MediaDetailPagerFragment.MediaDetailProvider, AdapterView.OnItemClickListener{ @@ -46,11 +44,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_category_images); ButterKnife.bind(this); - - // Activity can call methods in the fragment by acquiring a - // reference to the Fragment from FragmentManager, using findFragmentById() supportFragmentManager = getSupportFragmentManager(); -// supportFragmentManager.addOnBackStackChangedListener(this); setCategoryImagesFragment(); setPageTitle(); initDrawer(); @@ -85,7 +79,6 @@ private void setPageTitle() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (mediaDetails == null || !mediaDetails.isVisible()) { - // set isFeaturedImage true for featured images, to include author field on media detail mediaDetails = new MediaDetailPagerFragment(false, true); FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager @@ -104,7 +97,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { /** * Consumers should be simply using this method to use this activity. * @param context - * @param categoryName Name of the category for displaying its images + * @param categoryName Name of the category for displaying its details */ public static void startYourself(Context context, String categoryName) { Intent intent = new Intent(context, CategoryDetailsActivity.class); From a041a0319b84b2e39451aba93388ffe75bf0399d Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Fri, 29 Jun 2018 14:28:54 +0530 Subject: [PATCH 73/94] trimming added, Tab layout hided, recent searches refreshed --- .../free/nrw/commons/category/CategoryDetailsActivity.java | 4 ++-- .../free/nrw/commons/category/CategoryImagesActivity.java | 2 +- .../nrw/commons/category/CategoryImagesListFragment.java | 2 +- .../java/fr/free/nrw/commons/category/GridViewAdapter.java | 2 +- .../java/fr/free/nrw/commons/explore/SearchActivity.java | 5 ++++- .../explore/categories/SearchCategoriesRenderer.java | 2 +- .../nrw/commons/explore/images/SearchImagesRenderer.java | 2 +- .../explore/recentsearches/RecentSearchesFragment.java | 7 +++++++ .../fr/free/nrw/commons/media/MediaDetailFragment.java | 2 -- .../fr/free/nrw/commons/theme/NavigationBaseActivity.java | 6 ++++++ app/src/main/res/layout/activity_search.xml | 1 + 11 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index e0b227e8f8..a25caeb9f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -48,7 +48,7 @@ protected void onCreate(Bundle savedInstanceState) { setCategoryImagesFragment(); setPageTitle(); initDrawer(); - initBackButton(); + forceInitBackButton(); } /** @@ -90,7 +90,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(i); - initBackButton(); + forceInitBackButton(); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index c109307250..7992c3f8b3 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -115,7 +115,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(i); - initBackButton(); + forceInitBackButton(); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 340dcc83f2..c95b715ac9 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -143,7 +143,7 @@ private void initErrorView() { progressBar.setVisibility(GONE); if (gridAdapter == null || gridAdapter.isEmpty()) { statusTextView.setVisibility(VISIBLE); - statusTextView.setText(getString(R.string.no_images_found)); + statusTextView.setText(getContext().getString(R.string.no_images_found)); } else { ViewUtil.showSnackbar(gridView, R.string.error_loading_images); statusTextView.setVisibility(GONE); diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java index c8e6066f65..fe99c4d733 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -66,7 +66,7 @@ public View getView(int position, View convertView, ViewGroup parent) { MediaWikiImageView imageView = convertView.findViewById(R.id.categoryImageView); TextView fileName = convertView.findViewById(R.id.categoryImageTitle); TextView author = convertView.findViewById(R.id.categoryImageAuthor); - fileName.setText(item.getFilename()); + fileName.setText(item.getDisplayTitle()); setAuthorView(item, author); imageView.setMedia(item); return convertView; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 7b0f30efd6..ecb8d8e1e2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -103,13 +103,16 @@ public void setTabs() { //update image list if (!TextUtils.isEmpty(query)) { viewPager.setVisibility(View.VISIBLE); + tabLayout.setVisibility(View.VISIBLE); searchHistoryContainer.setVisibility(View.GONE); this.query = query.toString(); searchImageFragment.updateImageList(query.toString()); searchCategoryFragment.updateCategoryList(query.toString()); }else { viewPager.setVisibility(View.GONE); + tabLayout.setVisibility(View.GONE); searchHistoryContainer.setVisibility(View.VISIBLE); + recentSearchesFragment.updateRecentSearches(); // open search history fragment } } @@ -164,7 +167,7 @@ public void onSearchImageClicked(int index) { supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(index); - initBackButton(); + forceInitBackButton(); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java index 1d4c272e30..0031cc6cec 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -47,7 +47,7 @@ protected void hookListeners(View view) { @Override public void render() { String item = getContent(); - tvCategoryName.setText(item); + tvCategoryName.setText(item.replaceFirst("^Category:", "")); } interface CategoryClickedListener { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java index aa9db68779..eb0ba7bf60 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java @@ -52,7 +52,7 @@ protected void hookListeners(View view) { @Override public void render() { Media item = getContent(); - tvImageName.setText(item.getFilename()); + tvImageName.setText(item.getDisplayTitle()); browseImage.setMedia(item); setAuthorView(item, categoryImageAuthor); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java index 1023ca21b2..76a04c0aee 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java @@ -72,4 +72,11 @@ public void onResume() { adapter.notifyDataSetChanged(); super.onResume(); } + + public void updateRecentSearches() { + recentSearches = recentSearchesDao.recentSearches(10); + adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches, recentSearches); + recentSearchesList.setAdapter(adapter); + adapter.notifyDataSetChanged(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 8b9e9dd6b7..4b782136db 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -434,8 +434,6 @@ private View buildCatLabel(final String catName, ViewGroup categoryContainer) { Intent intent = new Intent(getContext(), CategoryDetailsActivity.class); intent.putExtra("categoryName", selectedCategoryTitle); getContext().startActivity(intent); - -// CategoryDetailsActivity.startYourself(getContext(), selectedCategoryTitle); }); } return item; diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index cc62fbd935..5ddc31d5b5 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -77,6 +77,12 @@ private void setUserName() { } public void initBackButton() { + int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); + toggle.setDrawerIndicatorEnabled(backStackEntryCount == 0); + toggle.setToolbarNavigationClickListener(v -> onBackPressed()); + } + + public void forceInitBackButton() { toggle.setDrawerIndicatorEnabled(false); toggle.setToolbarNavigationClickListener(v -> onBackPressed()); } diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 98eeedff7a..dce1ef1631 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -44,6 +44,7 @@ Date: Tue, 3 Jul 2018 13:43:18 +0530 Subject: [PATCH 74/94] SubCategory list fragment added, API added to extract subCategory Details --- .../category/CategoryDetailsActivity.java | 61 ++++-- .../commons/category/CategoryImageUtils.java | 12 ++ .../category/SubCategoryListFragment.java | 188 ++++++++++++++++++ .../nrw/commons/di/FragmentBuilderModule.java | 4 + .../nrw/commons/explore/ViewPagerAdapter.java | 2 +- .../SearchCategoriesAdapterFactory.java | 4 +- .../categories/SearchCategoriesRenderer.java | 2 +- .../mwapi/ApacheHttpClientMediaWikiApi.java | 54 +++++ .../free/nrw/commons/mwapi/MediaWikiApi.java | 2 + .../res/layout/activity_category_details.xml | 61 ++++++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 374 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java create mode 100644 app/src/main/res/layout/activity_category_details.xml diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index a25caeb9f0..82bed91e7c 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -4,20 +4,27 @@ import android.content.Intent; import android.database.DataSetObserver; import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; +import android.widget.FrameLayout; import android.widget.Toast; +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.PageTitle; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.auth.AuthenticatedActivity; +import fr.free.nrw.commons.explore.ViewPagerAdapter; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; @@ -36,35 +43,49 @@ public class CategoryDetailsActivity extends NavigationBaseActivity private FragmentManager supportFragmentManager; private CategoryImagesListFragment categoryImagesListFragment; + private SubCategoryListFragment subCategoryListFragment; private MediaDetailPagerFragment mediaDetails; private String categoryName; + @BindView(R.id.mediaContainer) FrameLayout mediaContainer; + @BindView(R.id.tabLayout) TabLayout tabLayout; + @BindView(R.id.viewPager) ViewPager viewPager; + + ViewPagerAdapter viewPagerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_category_images); + setContentView(R.layout.activity_category_details); ButterKnife.bind(this); supportFragmentManager = getSupportFragmentManager(); - setCategoryImagesFragment(); + viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); + viewPager.setAdapter(viewPagerAdapter); + tabLayout.setupWithViewPager(viewPager); + setTabs(); setPageTitle(); initDrawer(); forceInitBackButton(); } - /** - * Gets the categoryName from the intent and initializes the fragment for showing images of that category - */ - private void setCategoryImagesFragment() { + private void setTabs() { + List fragmentList = new ArrayList<>(); + List titleList = new ArrayList<>(); categoryImagesListFragment = new CategoryImagesListFragment(); + subCategoryListFragment = new SubCategoryListFragment(); categoryName = getIntent().getStringExtra("categoryName"); if (getIntent() != null && categoryName != null) { Bundle arguments = new Bundle(); arguments.putString("categoryName", categoryName); categoryImagesListFragment.setArguments(arguments); - FragmentTransaction transaction = supportFragmentManager.beginTransaction(); - transaction.replace(R.id.fragmentContainer, categoryImagesListFragment) - .commit(); + subCategoryListFragment.setArguments(arguments); } + fragmentList.add(categoryImagesListFragment); + titleList.add("MEDIA"); + fragmentList.add(subCategoryListFragment); + titleList.add("CATEGORIES"); + viewPagerAdapter.setTabData(fragmentList, titleList); + viewPagerAdapter.notifyDataSetChanged(); + } /** @@ -78,13 +99,16 @@ private void setPageTitle() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { + tabLayout.setVisibility(View.GONE); + viewPager.setVisibility(View.GONE); + mediaContainer.setVisibility(View.VISIBLE); if (mediaDetails == null || !mediaDetails.isVisible()) { + // set isFeaturedImage true for featured images, to include author field on media detail mediaDetails = new MediaDetailPagerFragment(false, true); FragmentManager supportFragmentManager = getSupportFragmentManager(); supportFragmentManager .beginTransaction() - .hide(supportFragmentManager.getFragments().get(supportFragmentManager.getBackStackEntryCount())) - .add(R.id.fragmentContainer, mediaDetails) + .replace(R.id.mediaContainer, mediaDetails) .addToBackStack(null) .commit(); supportFragmentManager.executePendingTransactions(); @@ -166,4 +190,15 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } } + + @Override + public void onBackPressed() { + if (getSupportFragmentManager().getBackStackEntryCount() == 1){ + // back to search so show search toolbar and hide navigation toolbar + tabLayout.setVisibility(View.VISIBLE); + viewPager.setVisibility(View.VISIBLE); + mediaContainer.setVisibility(View.GONE); + } + super.onBackPressed(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index 02e55651b0..1d14f732b9 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -35,6 +35,18 @@ public static List getMediaList(NodeList childNodes) { return categoryImages; } + public static List getSubCategoryList(NodeList childNodes) { + List subCategories = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (getMediaFromPage(node).getFilename().substring(0,9).equals("Category:")){ + subCategories.add(getMediaFromPage(node).getFilename()); + } + } + + return subCategories; + } + /** * Creates a new Media object from the XML response as received by the API * @param node diff --git a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java new file mode 100644 index 0000000000..a148d0757a --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java @@ -0,0 +1,188 @@ +package fr.free.nrw.commons.category; + + +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.pedrogomez.renderers.RVRendererAdapter; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.explore.categories.SearchCategoriesAdapterFactory; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.NetworkUtils; +import fr.free.nrw.commons.utils.ViewUtil; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +/** + * Displays the category search screen. + */ + +public class SubCategoryListFragment extends CommonsDaggerSupportFragment { + + private static int TIMEOUT_SECONDS = 15; + + @BindView(R.id.imagesListBox) + RecyclerView categoriesRecyclerView; + @BindView(R.id.imageSearchInProgress) + ProgressBar progressBar; + @BindView(R.id.imagesNotFound) + TextView categoriesNotFoundView; + + private String categoryName = null; + @Inject MediaWikiApi mwApi; + + private RVRendererAdapter categoriesAdapter; + private List subCategoryList = new ArrayList<>(); + + private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { + // Open SubCategory Details page + Intent intent = new Intent(getContext(), CategoryDetailsActivity.class); + intent.putExtra("categoryName", item); + getContext().startActivity(intent); + + }); + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); + ButterKnife.bind(this, rootView); + categoryName = getArguments().getString("categoryName"); + initSubCategoryList(); + if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ + categoriesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + } + else{ + categoriesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); + } + ArrayList items = new ArrayList<>(); + categoriesAdapter = adapterFactory.create(items); + categoriesRecyclerView.setAdapter(categoriesAdapter); + categoriesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + // check if end of recycler view is reached, if yes then add more results to existing results + if (!recyclerView.canScrollVertically(1)) { + addCategoriesToList(); + } + } + }); + return rootView; + } + + /** + * Checks for internet connection and then initializes the recycler view with 25 categories of the searched query + * Clearing categoryAdapter every time new keyword is searched so that user can see only new results + */ + public void initSubCategoryList() { + categoriesNotFoundView.setVisibility(GONE); + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + progressBar.setVisibility(View.VISIBLE); +// subCategoryList.clear(); +// categoriesAdapter.clear(); + + Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + + /** + * Adds more results to existing search results + */ + public void addCategoriesToList() { + progressBar.setVisibility(View.VISIBLE); + Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handlePaginationSuccess, this::handleError); + } + + /** + * Handles the success scenario + * it initializes the recycler view by adding items to the adapter + * @param subCategoryList + */ + private void handlePaginationSuccess(List subCategoryList) { + this.subCategoryList.addAll(subCategoryList); + progressBar.setVisibility(View.GONE); + categoriesAdapter.addAll(subCategoryList); + categoriesAdapter.notifyDataSetChanged(); + } + + + + /** + * Handles the success scenario + * it initializes the recycler view by adding items to the adapter + * @param subCategoryList + */ + private void handleSuccess(List subCategoryList) { + this.subCategoryList = subCategoryList; + if(subCategoryList == null || subCategoryList.isEmpty()) { + initErrorView(); + } + else { + progressBar.setVisibility(View.GONE); + categoriesAdapter.addAll(subCategoryList); + categoriesAdapter.notifyDataSetChanged(); + } + } + + /** + * Logs and handles API error scenario + * @param throwable + */ + private void handleError(Throwable throwable) { + Timber.e(throwable, "Error occurred while loading queried categories"); + initErrorView(); + } + + /** + * Handles the UI updates for a error scenario + */ + private void initErrorView() { + progressBar.setVisibility(GONE); + categoriesNotFoundView.setVisibility(VISIBLE); + categoriesNotFoundView.setText(getString(R.string.no_subcategory_found)); + } + + /** + * Handles the UI updates for no internet scenario + */ + private void handleNoInternet() { + progressBar.setVisibility(GONE); + ViewUtil.showSnackbar(categoriesRecyclerView, R.string.no_internet); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index bba7660a11..b14d8feeff 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -3,6 +3,7 @@ import dagger.Module; import dagger.android.ContributesAndroidInjector; import fr.free.nrw.commons.category.CategorizationFragment; +import fr.free.nrw.commons.category.SubCategoryListFragment; import fr.free.nrw.commons.contributions.ContributionsListFragment; import fr.free.nrw.commons.category.CategoryImagesListFragment; import fr.free.nrw.commons.explore.categories.SearchCategoryFragment; @@ -54,6 +55,9 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract CategoryImagesListFragment bindFeaturedImagesListFragment(); + @ContributesAndroidInjector + abstract SubCategoryListFragment bindSubCategoryListFragment(); + @ContributesAndroidInjector abstract SearchImageFragment bindBrowseImagesListFragment(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java index aae24c3a02..499ec2386c 100755 --- a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java @@ -10,7 +10,7 @@ /** * This adapter will be used to display fragments in a ViewPager */ -class ViewPagerAdapter extends FragmentPagerAdapter { +public class ViewPagerAdapter extends FragmentPagerAdapter { private List fragmentList = new ArrayList<>(); private List fragmentTitleList = new ArrayList<>(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index 9ad8883ded..2a28a83767 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -7,10 +7,10 @@ import java.util.Collections; import java.util.List; -class SearchCategoriesAdapterFactory { +public class SearchCategoriesAdapterFactory { private final SearchCategoriesRenderer.CategoryClickedListener listener; - SearchCategoriesAdapterFactory(SearchCategoriesRenderer.CategoryClickedListener listener) { + public SearchCategoriesAdapterFactory(SearchCategoriesRenderer.CategoryClickedListener listener) { this.listener = listener; } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java index 0031cc6cec..46e61c8aee 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -50,7 +50,7 @@ public void render() { tvCategoryName.setText(item.replaceFirst("^Category:", "")); } - interface CategoryClickedListener { + public interface CategoryClickedListener { void categoryClicked(String item); } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 2fec8518a0..ffb103e55a 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -569,6 +569,60 @@ public List getNotifications() { return NotificationUtils.getNotificationsFromList(context, childNodes); } + /** + * The method takes categoryName as input and returns a List of Subcategories + * It uses the generator query API to get the subcategories in a category, 10 at a time. + * Uses the query continue values for fetching paginated responses + * @param categoryName Category name as defined on commons + * @return + */ + @Override + @NonNull + public List getSubCategoryList(String categoryName) { + ApiResult apiResult = null; + try { + MWApi.RequestBuilder requestBuilder = api.action("query") + .param("generator", "categorymembers") + .param("format", "xml") + .param("gcmtype", "file") + .param("gcmtitle", categoryName) + .param("gcmsort", "timestamp")//property to sort by;timestamp + .param("gcmdir", "desc")//in which direction to sort;descending + .param("prop", "imageinfo") + .param("gcmlimit", "10") + .param("iiprop", "url|extmetadata"); + + QueryContinue queryContinueValues = getQueryContinueValues(categoryName); + if (queryContinueValues != null) { + requestBuilder.param("continue", queryContinueValues.getContinueParam()); + requestBuilder.param("gcmcontinue", queryContinueValues.getGcmContinueParam()); + } + + apiResult = requestBuilder.get(); + } catch (IOException e) { + Timber.e("Failed to obtain searchCategories", e); + } + + if (apiResult == null) { + return new ArrayList<>(); + } + + ApiResult categoryImagesNode = apiResult.getNode("/api/query/pages"); + if (categoryImagesNode == null + || categoryImagesNode.getDocument() == null + || categoryImagesNode.getDocument().getChildNodes() == null + || categoryImagesNode.getDocument().getChildNodes().getLength() == 0) { + return new ArrayList<>(); + } + + QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); + setQueryContinueValues(categoryName, queryContinue); + + NodeList childNodes = categoryImagesNode.getDocument().getChildNodes(); + return CategoryImageUtils.getSubCategoryList(childNodes); + } + + /** * The method takes categoryName as input and returns a List of Media objects * It uses the generator query API to get the images in a category, 10 at a time. diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index 215c747f71..9c50ec6711 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -41,6 +41,8 @@ public interface MediaWikiApi { List getCategoryImages(String categoryName); + List getSubCategoryList(String categoryName); + @NonNull List searchImages(String title, int offset); diff --git a/app/src/main/res/layout/activity_category_details.xml b/app/src/main/res/layout/activity_category_details.xml new file mode 100644 index 0000000000..04b50074a8 --- /dev/null +++ b/app/src/main/res/layout/activity_category_details.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e348824fc3..0d9aa17593 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -198,6 +198,7 @@ Background Image Media Image Failed No Image Found + No Subcategory Found Upload Image Mount Zao Llamas From 673321dd1046700bd7839155df976d3de79b80a3 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 3 Jul 2018 15:49:34 +0530 Subject: [PATCH 75/94] API params updated to get more precise results --- .../nrw/commons/category/CategoryDetailsActivity.java | 2 +- .../free/nrw/commons/category/CategoryImageUtils.java | 4 +--- .../nrw/commons/category/SubCategoryListFragment.java | 10 ++++------ .../commons/mwapi/ApacheHttpClientMediaWikiApi.java | 8 +++----- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 82bed91e7c..6980d57a5b 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -82,7 +82,7 @@ private void setTabs() { fragmentList.add(categoryImagesListFragment); titleList.add("MEDIA"); fragmentList.add(subCategoryListFragment); - titleList.add("CATEGORIES"); + titleList.add("SUB-CATEGORIES"); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index 1d14f732b9..1b9fdde86f 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -39,9 +39,7 @@ public static List getSubCategoryList(NodeList childNodes) { List subCategories = new ArrayList<>(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); - if (getMediaFromPage(node).getFilename().substring(0,9).equals("Category:")){ - subCategories.add(getMediaFromPage(node).getFilename()); - } + subCategories.add(getMediaFromPage(node).getFilename()); } return subCategories; diff --git a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java index a148d0757a..681747089f 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java @@ -12,7 +12,6 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; -import android.widget.Toast; import com.pedrogomez.renderers.RVRendererAdapter; @@ -151,7 +150,7 @@ private void handlePaginationSuccess(List subCategoryList) { private void handleSuccess(List subCategoryList) { this.subCategoryList = subCategoryList; if(subCategoryList == null || subCategoryList.isEmpty()) { - initErrorView(); + initEmptyView(); } else { progressBar.setVisibility(View.GONE); @@ -165,14 +164,13 @@ private void handleSuccess(List subCategoryList) { * @param throwable */ private void handleError(Throwable throwable) { - Timber.e(throwable, "Error occurred while loading queried categories"); - initErrorView(); + Timber.e(throwable, "Error occurred while loading queried subcategories"); } /** - * Handles the UI updates for a error scenario + * Handles the UI updates for a empty results scenario */ - private void initErrorView() { + private void initEmptyView() { progressBar.setVisibility(GONE); categoriesNotFoundView.setVisibility(VISIBLE); categoriesNotFoundView.setText(getString(R.string.no_subcategory_found)); diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index ffb103e55a..5f1debb733 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -571,7 +571,7 @@ public List getNotifications() { /** * The method takes categoryName as input and returns a List of Subcategories - * It uses the generator query API to get the subcategories in a category, 10 at a time. + * It uses the generator query API to get the subcategories in a category, 20 at a time. * Uses the query continue values for fetching paginated responses * @param categoryName Category name as defined on commons * @return @@ -584,11 +584,9 @@ public List getSubCategoryList(String categoryName) { MWApi.RequestBuilder requestBuilder = api.action("query") .param("generator", "categorymembers") .param("format", "xml") - .param("gcmtype", "file") + .param("gcmtype","subcat") .param("gcmtitle", categoryName) - .param("gcmsort", "timestamp")//property to sort by;timestamp - .param("gcmdir", "desc")//in which direction to sort;descending - .param("prop", "imageinfo") + .param("prop", "info") .param("gcmlimit", "10") .param("iiprop", "url|extmetadata"); From 986d92f75e39cef4a1a23c861207ddd137423f6d Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 3 Jul 2018 15:54:51 +0530 Subject: [PATCH 76/94] Javadocs added for MWAPI method --- .../fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 5f1debb733..37d2dcecdc 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -587,7 +587,7 @@ public List getSubCategoryList(String categoryName) { .param("gcmtype","subcat") .param("gcmtitle", categoryName) .param("prop", "info") - .param("gcmlimit", "10") + .param("gcmlimit", "20") .param("iiprop", "url|extmetadata"); QueryContinue queryContinueValues = getQueryContinueValues(categoryName); From 48d8f646b4f5b6a3c75441a02dfdd695d249cb45 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 5 Jul 2018 17:44:34 +0530 Subject: [PATCH 77/94] Pagination removed --- .../category/CategoryDetailsActivity.java | 2 +- .../commons/category/CategoryImageUtils.java | 3 +- .../category/SubCategoryListFragment.java | 42 +----------------- .../mwapi/ApacheHttpClientMediaWikiApi.java | 12 +---- .../ic_notifications_black_24dp.png | Bin 0 -> 282 bytes .../ic_notifications_none_black_24dp.png | Bin 0 -> 350 bytes .../ic_notifications_black_24dp.png | Bin 0 -> 213 bytes .../ic_notifications_none_black_24dp.png | Bin 0 -> 248 bytes .../ic_notifications_black_24dp.png | Bin 0 -> 337 bytes .../ic_notifications_none_black_24dp.png | Bin 0 -> 429 bytes .../ic_notifications_black_24dp.png | Bin 0 -> 498 bytes .../ic_notifications_none_black_24dp.png | Bin 0 -> 638 bytes .../ic_notifications_black_24dp.png | Bin 0 -> 633 bytes .../ic_notifications_none_black_24dp.png | Bin 0 -> 830 bytes app/src/main/res/values/strings.xml | 1 + 15 files changed, 8 insertions(+), 52 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_notifications_none_black_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_notifications_none_black_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_notifications_none_black_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_notifications_none_black_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_notifications_none_black_24dp.png diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 6980d57a5b..8da447ef1b 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -82,7 +82,7 @@ private void setTabs() { fragmentList.add(categoryImagesListFragment); titleList.add("MEDIA"); fragmentList.add(subCategoryListFragment); - titleList.add("SUB-CATEGORIES"); + titleList.add("SUBCATEGORIES"); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index 1b9fdde86f..f3b0501b08 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -8,6 +8,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -41,7 +42,7 @@ public static List getSubCategoryList(NodeList childNodes) { Node node = childNodes.item(i); subCategories.add(getMediaFromPage(node).getFilename()); } - + Collections.sort(subCategories); return subCategories; } diff --git a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java index 681747089f..8e39c6f45e 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java @@ -12,6 +12,7 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import com.pedrogomez.renderers.RVRendererAdapter; @@ -56,7 +57,6 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; private RVRendererAdapter categoriesAdapter; - private List subCategoryList = new ArrayList<>(); private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { // Open SubCategory Details page @@ -81,16 +81,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav ArrayList items = new ArrayList<>(); categoriesAdapter = adapterFactory.create(items); categoriesRecyclerView.setAdapter(categoriesAdapter); - categoriesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - super.onScrollStateChanged(recyclerView, newState); - // check if end of recycler view is reached, if yes then add more results to existing results - if (!recyclerView.canScrollVertically(1)) { - addCategoriesToList(); - } - } - }); return rootView; } @@ -105,8 +95,6 @@ public void initSubCategoryList() { return; } progressBar.setVisibility(View.VISIBLE); -// subCategoryList.clear(); -// categoriesAdapter.clear(); Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName)) .subscribeOn(Schedulers.io()) @@ -116,39 +104,12 @@ public void initSubCategoryList() { } - /** - * Adds more results to existing search results - */ - public void addCategoriesToList() { - progressBar.setVisibility(View.VISIBLE); - Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) - .subscribe(this::handlePaginationSuccess, this::handleError); - } - - /** - * Handles the success scenario - * it initializes the recycler view by adding items to the adapter - * @param subCategoryList - */ - private void handlePaginationSuccess(List subCategoryList) { - this.subCategoryList.addAll(subCategoryList); - progressBar.setVisibility(View.GONE); - categoriesAdapter.addAll(subCategoryList); - categoriesAdapter.notifyDataSetChanged(); - } - - - /** * Handles the success scenario * it initializes the recycler view by adding items to the adapter * @param subCategoryList */ private void handleSuccess(List subCategoryList) { - this.subCategoryList = subCategoryList; if(subCategoryList == null || subCategoryList.isEmpty()) { initEmptyView(); } @@ -165,6 +126,7 @@ private void handleSuccess(List subCategoryList) { */ private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading queried subcategories"); + ViewUtil.showSnackbar(categoriesRecyclerView,R.string.error_loading_categories); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 37d2dcecdc..d18660e1ce 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -35,6 +35,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; @@ -587,15 +588,9 @@ public List getSubCategoryList(String categoryName) { .param("gcmtype","subcat") .param("gcmtitle", categoryName) .param("prop", "info") - .param("gcmlimit", "20") + .param("gcmlimit", "500") .param("iiprop", "url|extmetadata"); - QueryContinue queryContinueValues = getQueryContinueValues(categoryName); - if (queryContinueValues != null) { - requestBuilder.param("continue", queryContinueValues.getContinueParam()); - requestBuilder.param("gcmcontinue", queryContinueValues.getGcmContinueParam()); - } - apiResult = requestBuilder.get(); } catch (IOException e) { Timber.e("Failed to obtain searchCategories", e); @@ -613,9 +608,6 @@ public List getSubCategoryList(String categoryName) { return new ArrayList<>(); } - QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); - setQueryContinueValues(categoryName, queryContinue); - NodeList childNodes = categoryImagesNode.getDocument().getChildNodes(); return CategoryImageUtils.getSubCategoryList(childNodes); } diff --git a/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..42407d2925cdd708129a9234bc6f89b480a68511 GIT binary patch literal 282 zcmV+#0pQhkwIRY_NFF+|JRjF?f`@-m$Pf%PS5dpgVP-rmjF+>TQ5j zdr=$PpnU^KwVf@{k}6w(3TlA{jj3A;jP6Q}kf3!3jBbSlodKW|5|jZz84{EOKu;v7 z1b_-8NP-HW01BYL03|?B(iMcnG(x$?)S_JM%}@JOHzAu1<0&l+S0Q|YxBa;wAD021+@(LX1yd`&{oVEanV9I$VHe}u1-*00;=l(6`>JykbsWR7%C+N z>Ooftizj~g_QZ0tiTkuNzvj0@C)9%!Qz)P-y`#umrUXk0j_Ne#+Fd$h?KDt{Os zp4*`DJ19p9>Y&B%Q1y#BH8J)zuT2S>qJ?J)mwArnO(;QIv~WWK)I{^Pl%NY**iisE zZ%-o`2tQq_uL^wck%2e?h#Qar8PLCg7$J!L3SyG)H%u|Ef>Om$#iD?C%tiWy;w&3K wr31;O2y}ux3CPn4DhNOz^29OocT9FbPt_;}1|JWbX+c<4TqCw;V zjwAO1&UkQhJFxw4Ps-1VKEQHh^8}q4tfGs%SSLEv@SiM@*vCF~@rkAog@5fw3nc!r zhdNJV-Q@6xyZ@m9Pmr>6AM0Kh3BJR&Pa+E1Sh)QrCm*a9(y4IoWBu=WWU&~JA-B|t z4GS70xK9|$ELXk4A+^WRbVb&d=N{{7{J6Po7P2*O_&s6Gmy?GWepc^R{O~UDI?$aA Mp00i_>zopr0Lu}1|JWbX+c<4TqCw;V zjwAO1&UkQhJFxw4Ps-1VKEQHh^8}q4tfGs%SSLEv@SiM@*vCF~@rkAog@5d(AJ_!W zw>^>$*jOK6ni8qNePsUvwyO(Vb(BNi*oXA;Sc-jd;*IEcKg+gamRymJ1@qN64eq_l zA!jp-bSn6->NL6cYJ{A%F4AdOD(1w=xPDh4H`{tv-js+74r^F>BTmUC6*3!&{aG%+ we^2VI~$6tJ@0Cm8R#!9B?U<%Hv1B^6+4 zdm;cHaBp{%G00000NkvXXu0mjfA^d_E literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_notifications_none_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_notifications_none_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..56a194cc113d805f8f47681aeec49ffa0d8317c7 GIT binary patch literal 429 zcmV;e0aE^nP)kBmw;g zr59)d1B<`_*OX4+5tvzuQ)W0*3V~N(b4K|i8=NgvV3?ygWmuFz7uXpJEr34I2I@!~ zuPqGgcbLU0@Bn-nZizRbVwt4uMO*VLIiBfCz|y2#A0PT$Mn=AdvVHNIV@@X%`9C@;*r}B~V29 ze@nQWb8;a9@`))l0cE)zR0sl@MIZ}7AjY#!Z8-moLwN>b{I!o?DKj7C+S8mUWWrDj(zkh z6%<#u*E^6c!1GS_Xk_4$R}UUdF>O|eYh8R@!1V*0QKoqL(&h^rj~)EMcJ5$QkFSs4 zrzdmLfB5G*|D3odzx}db(dLVZHw7CVSd;`BIb0TKeNu^kD)?e|s^*kQE1ka1`s}d( zp^uU!?~}ici$BM(eJW=7smIJ}*}%jk_`;n#$bZ)~H_HaMS9@*0MrJqey0p^dOyi`h z0kMm-r7uh~(O@*b>kxaHYk~61#ZEG@Is=OTJ)acp)d65&MgU z!E66vEu{;341Ia*f-kr@UI+=k;Ny59CisGr_6o8BPK~ zKbfNBD2=w{_%q{yI^q0AG8!mhgVRdFWT1=#pp3~tmIELQKmhuaQA(0>La8_as_@t( zZ#%R=A<8DD>;OednQMfG4Jb}2xqxX;iE9o8&>H2#L!4HcHB){E+E)P6oOQDg2B-kqve}6@%Dju(3tU60zpXpa1m#7T{3j*mL5~>M>eS84e1_ z6Qq7TggHnc*GQ^ybcxk(AV1-_LNW#C-$*LXdEEmb0BH_{zhoi+@&!<>hwDboM4&U~=$uaSW-r_4bCnzH^|&@j~A# zXS_-T)$|YeapX6y^%hpM7EIg{dZ|2NqJ$(zbJtSkStZxcdTHm{`kwR5$hcc?``zX* z-~FX8-{#iI7ruWR@jNH7=gv_M1_lQN;?YqSUD9>MAoNVOB*Ua!w_TI1>PxhJnJ3(d zuiO7|zj6vg$Noe1$F_?CX)lHbE{2cSIT$W5DEzZxWM*KnW!NdeV9Bt^RQs)0e~>kc z!)=+}*>>Cw^=9gG+hz93AKUpehv8pFkQHaecHu2;md8~X?4u{W;^ey@eRqy9L#FpH ziAO)Sy|XlTmwj;I;D^1keOd?nxQ=bl?Uy>x^<}0~!@KfGInjo9Yac{+9oxQl=MLXe z#)7GbKITrp#b_2}voAMR+aYXK6~hOm-fWf=)du@?*9$&4sNAs0tbw)v*!Hv8Ocvh+ z9(V{lTy<_(CDyQVM*VI1u(yeSa%_1TO!<#*Uz^2noGsccMT_y+Kh8I(n7^X0E+*@>o z%dqaW47UT%+%^V{BEi1(oG;Z5{J8#1f+5LGgYQTA6{Ghji==10FOplL~KZ8LO-gU!HZNZ4<~kPgg&ebxsLQ0G~+h9{>OV literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_notifications_none_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_notifications_none_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f0237c57e1fc96900bc24e6f7f10914d140b4668 GIT binary patch literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>U}p7naSW-r_4dxjY~ete10UIP z6p|Fzb;Wiq%n&av6cNddEe&3BL3KfJdG-pcU7Anq3pC3@R)hv>>q%a9YRh}Opycu_ z52^abcn9vY)%xYr%fG*~u9<$}PWAWSA@66k^pwsIKXxLXhmnDS1A&rRS8ACqnRe~M zaV`dx+nz7yUa?)W{Sqg`q~B-dP5#siaxol?S{}Y$%ETWqau1_t$GHwcj(GmSd~m@N^H;9q)r5e&0PSeL(bO|KkbY zmWW^4Q{uVG+U=k$flG23O1l)rT9dHt>L>*1oeI|CSxE%ohY%<)baiuk`q z?)H{{<}W=u7`VmM{|C?VE8xGpt1VNWTal^8>*U)7XWzbe%U}@go*XBAarR5r*wb6) zUshza*=F&uSozQOihD`+{Z32*W|1@Hu=*TXIg|@cS3V zj2#Qpof&MND#^Sr|GmbV=YQ$-^W_4pwbD^n?!W4LxPR`w^F77Vv)-64Tvu+9yLM&& j*$?Fmzyt|J6Tbap5=i_fzok;63}maPtDnm{r-UW|+M!yG literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0d9aa17593..1e4b9cb80f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -278,6 +278,7 @@ No images found! Error occurred while loading images. Error occurred while loading categories. + Error occurred while loading subcategories. Uploaded by: %1$s Share App Coordinates were not specified during image selection From 5f77ba90f7c95047c02bfe5e98450c8f25db7555 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Thu, 5 Jul 2018 18:10:50 +0530 Subject: [PATCH 78/94] Fix API for fetching images inside category --- .../nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index d18660e1ce..34f2d65d1c 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -659,8 +659,12 @@ public List getCategoryImages(String categoryName) { return new ArrayList<>(); } - QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); - setQueryContinueValues(categoryName, queryContinue); + if (apiResult.getNode("/api/continue").getDocument()!=null){ + QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); + setQueryContinueValues(categoryName, queryContinue); + }else { + return new ArrayList<>(); + } NodeList childNodes = categoryImagesNode.getDocument().getChildNodes(); return CategoryImageUtils.getMediaList(childNodes); From 93f62a7e2294115fa8533c7497ed76726ca91700 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Tue, 10 Jul 2018 16:54:18 +0530 Subject: [PATCH 79/94] Parent category API added --- .../category/CategoryDetailsActivity.java | 10 ++++ .../category/CategoryImagesListFragment.java | 17 ++++-- .../nrw/commons/category/GridViewAdapter.java | 13 +++++ .../category/SubCategoryListFragment.java | 30 ++++++++--- .../mwapi/ApacheHttpClientMediaWikiApi.java | 52 +++++++++++++++++-- .../free/nrw/commons/mwapi/MediaWikiApi.java | 2 + .../res/layout/fragment_category_images.xml | 1 + 7 files changed, 108 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 8da447ef1b..8c3bec80c0 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -44,6 +44,7 @@ public class CategoryDetailsActivity extends NavigationBaseActivity private FragmentManager supportFragmentManager; private CategoryImagesListFragment categoryImagesListFragment; private SubCategoryListFragment subCategoryListFragment; + private SubCategoryListFragment parentCategoryListFragment; private MediaDetailPagerFragment mediaDetails; private String categoryName; @BindView(R.id.mediaContainer) FrameLayout mediaContainer; @@ -60,6 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { supportFragmentManager = getSupportFragmentManager(); viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(viewPagerAdapter); + viewPager.setOffscreenPageLimit(2); tabLayout.setupWithViewPager(viewPager); setTabs(); setPageTitle(); @@ -72,17 +74,25 @@ private void setTabs() { List titleList = new ArrayList<>(); categoryImagesListFragment = new CategoryImagesListFragment(); subCategoryListFragment = new SubCategoryListFragment(); + parentCategoryListFragment = new SubCategoryListFragment(); categoryName = getIntent().getStringExtra("categoryName"); if (getIntent() != null && categoryName != null) { Bundle arguments = new Bundle(); arguments.putString("categoryName", categoryName); + arguments.putBoolean("isParentCategory", false); categoryImagesListFragment.setArguments(arguments); subCategoryListFragment.setArguments(arguments); + Bundle parentCategoryArguments = new Bundle(); + parentCategoryArguments.putString("categoryName", categoryName); + parentCategoryArguments.putBoolean("isParentCategory", true); + parentCategoryListFragment.setArguments(parentCategoryArguments); } fragmentList.add(categoryImagesListFragment); titleList.add("MEDIA"); fragmentList.add(subCategoryListFragment); titleList.add("SUBCATEGORIES"); + fragmentList.add(parentCategoryListFragment); + titleList.add("PARENT CATEGORIES"); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index c95b715ac9..d5d0b0dcee 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -12,7 +12,9 @@ import android.widget.GridView; import android.widget.ListAdapter; import android.widget.ProgressBar; +import android.widget.RelativeLayout; import android.widget.TextView; +import android.widget.Toast; import java.util.List; import java.util.concurrent.TimeUnit; @@ -48,7 +50,7 @@ public class CategoryImagesListFragment extends DaggerFragment { TextView statusTextView; @BindView(R.id.loadingImagesProgressBar) ProgressBar progressBar; @BindView(R.id.categoryImagesList) GridView gridView; - + @BindView(R.id.parentLayout) RelativeLayout parentLayout; private boolean hasMoreImages = true; private boolean isLoading; private String categoryName = null; @@ -123,7 +125,7 @@ private void handleNoInternet() { statusTextView.setVisibility(VISIBLE); statusTextView.setText(getString(R.string.no_internet)); } else { - ViewUtil.showSnackbar(gridView, R.string.no_internet); + ViewUtil.showSnackbar(parentLayout, R.string.no_internet); } } @@ -132,7 +134,8 @@ private void handleNoInternet() { * @param throwable */ private void handleError(Throwable throwable) { - Timber.e(throwable, "Error occurred while loading featured images"); + Timber.e(throwable, "Error occurred while loading images inside a category"); + ViewUtil.showSnackbar(parentLayout, R.string.error_loading_images); initErrorView(); } @@ -145,7 +148,6 @@ private void initErrorView() { statusTextView.setVisibility(VISIBLE); statusTextView.setText(getContext().getString(R.string.no_images_found)); } else { - ViewUtil.showSnackbar(gridView, R.string.error_loading_images); statusTextView.setVisibility(GONE); } } @@ -175,6 +177,9 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount >= totalItemCount)) { isLoading = true; fetchMoreImages(); + }else { + isLoading = false; + progressBar.setVisibility(GONE); } } }); @@ -213,6 +218,10 @@ private void handleSuccess(List collection) { if(gridAdapter == null) { setAdapter(collection); } else { + if (gridAdapter.containsAll(collection)){ + hasMoreImages = false; + return; + } gridAdapter.addItems(collection); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java index fe99c4d733..0a0f8aa0c1 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.content.Context; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -42,6 +43,18 @@ public void addItems(List images) { notifyDataSetChanged(); } + /** + * Check the last item in the new list with old list and returns true if they are same + * Its triggered on successful response of the fetch images API. + * @param images + */ + public boolean containsAll(List images){ + if (data == null) { + data = new ArrayList<>(); + } + return images.get(images.size()-1).getFilename().equals(data.get(data.size()-1).getFilename()); + } + @Override public boolean isEmpty() { return data == null || data.isEmpty(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java index 8e39c6f45e..9a760bc732 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java @@ -57,6 +57,7 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; private RVRendererAdapter categoriesAdapter; + private boolean isParentCategory = true; private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { // Open SubCategory Details page @@ -71,6 +72,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); ButterKnife.bind(this, rootView); categoryName = getArguments().getString("categoryName"); + isParentCategory = getArguments().getBoolean("isParentCategory"); initSubCategoryList(); if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ categoriesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); @@ -95,12 +97,19 @@ public void initSubCategoryList() { return; } progressBar.setVisibility(View.VISIBLE); - - Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) - .subscribe(this::handleSuccess, this::handleError); + if (!isParentCategory){ + Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + }else { + Observable.fromCallable(() -> mwApi.getParentCategoryList(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } } @@ -125,8 +134,13 @@ private void handleSuccess(List subCategoryList) { * @param throwable */ private void handleError(Throwable throwable) { - Timber.e(throwable, "Error occurred while loading queried subcategories"); - ViewUtil.showSnackbar(categoriesRecyclerView,R.string.error_loading_categories); + if (!isParentCategory){ + Timber.e(throwable, "Error occurred while loading queried subcategories"); + ViewUtil.showSnackbar(categoriesRecyclerView,R.string.error_loading_categories); + }else { + Timber.e(throwable, "Error occurred while loading queried subcategories"); + ViewUtil.showSnackbar(categoriesRecyclerView,R.string.error_loading_categories); + } } /** diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index d18660e1ce..253748c56f 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -572,7 +572,7 @@ public List getNotifications() { /** * The method takes categoryName as input and returns a List of Subcategories - * It uses the generator query API to get the subcategories in a category, 20 at a time. + * It uses the generator query API to get the subcategories in a category, 500 at a time. * Uses the query continue values for fetching paginated responses * @param categoryName Category name as defined on commons * @return @@ -612,6 +612,46 @@ public List getSubCategoryList(String categoryName) { return CategoryImageUtils.getSubCategoryList(childNodes); } + /** + * The method takes categoryName as input and returns a List of parent categories + * It uses the generator query API to get the parent categories of a category, 500 at a time. + * @param categoryName Category name as defined on commons + * @return + */ + @Override + @NonNull + public List getParentCategoryList(String categoryName) { + ApiResult apiResult = null; + try { + MWApi.RequestBuilder requestBuilder = api.action("query") + .param("generator", "categories") + .param("format", "xml") + .param("titles", categoryName) + .param("prop", "info") + .param("cllimit", "500") + .param("iiprop", "url|extmetadata"); + + apiResult = requestBuilder.get(); + } catch (IOException e) { + Timber.e("Failed to obtain parent Categories", e); + } + + if (apiResult == null) { + return new ArrayList<>(); + } + + ApiResult categoryImagesNode = apiResult.getNode("/api/query/pages"); + if (categoryImagesNode == null + || categoryImagesNode.getDocument() == null + || categoryImagesNode.getDocument().getChildNodes() == null + || categoryImagesNode.getDocument().getChildNodes().getLength() == 0) { + return new ArrayList<>(); + } + + NodeList childNodes = categoryImagesNode.getDocument().getChildNodes(); + return CategoryImageUtils.getSubCategoryList(childNodes); + } + /** * The method takes categoryName as input and returns a List of Media objects @@ -641,7 +681,6 @@ public List getCategoryImages(String categoryName) { requestBuilder.param("continue", queryContinueValues.getContinueParam()); requestBuilder.param("gcmcontinue", queryContinueValues.getGcmContinueParam()); } - apiResult = requestBuilder.get(); } catch (IOException e) { Timber.e("Failed to obtain searchCategories", e); @@ -659,9 +698,12 @@ public List getCategoryImages(String categoryName) { return new ArrayList<>(); } - QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); - setQueryContinueValues(categoryName, queryContinue); - + if (apiResult.getNode("/api/continue").getDocument()==null){ + setQueryContinueValues(categoryName, null); + }else { + QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); + setQueryContinueValues(categoryName, queryContinue); + } NodeList childNodes = categoryImagesNode.getDocument().getChildNodes(); return CategoryImageUtils.getMediaList(childNodes); } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index 9c50ec6711..d44752f225 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -43,6 +43,8 @@ public interface MediaWikiApi { List getSubCategoryList(String categoryName); + List getParentCategoryList(String categoryName); + @NonNull List searchImages(String title, int offset); diff --git a/app/src/main/res/layout/fragment_category_images.xml b/app/src/main/res/layout/fragment_category_images.xml index 001f0a7807..f317b2f23a 100644 --- a/app/src/main/res/layout/fragment_category_images.xml +++ b/app/src/main/res/layout/fragment_category_images.xml @@ -1,6 +1,7 @@ Date: Fri, 13 Jul 2018 23:24:02 +0530 Subject: [PATCH 80/94] Fix #1704 --- .../nrw/commons/category/SubCategoryListFragment.java | 9 +++++++-- app/src/main/res/values/strings.xml | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java index 9a760bc732..d3000e9b63 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java @@ -138,7 +138,7 @@ private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading queried subcategories"); ViewUtil.showSnackbar(categoriesRecyclerView,R.string.error_loading_categories); }else { - Timber.e(throwable, "Error occurred while loading queried subcategories"); + Timber.e(throwable, "Error occurred while loading queried parentcategories"); ViewUtil.showSnackbar(categoriesRecyclerView,R.string.error_loading_categories); } } @@ -149,7 +149,12 @@ private void handleError(Throwable throwable) { private void initEmptyView() { progressBar.setVisibility(GONE); categoriesNotFoundView.setVisibility(VISIBLE); - categoriesNotFoundView.setText(getString(R.string.no_subcategory_found)); + if (!isParentCategory){ + categoriesNotFoundView.setText(getString(R.string.no_subcategory_found)); + }else { + categoriesNotFoundView.setText(getString(R.string.no_parentcategory_found)); + } + } /** diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d18f1c40d..fcb0150b79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,6 +199,7 @@ Media Image Failed No Image Found No Subcategory Found + No Parentcategory Found Upload Image Mount Zao Llamas From 861d3fd7e085a0df9bdd58d48ef7c2e5702ca119 Mon Sep 17 00:00:00 2001 From: VaishSiddharth Date: Fri, 13 Jul 2018 23:49:02 +0530 Subject: [PATCH 81/94] Fix #1704 corrected --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fcb0150b79..782457303c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,7 +199,7 @@ Media Image Failed No Image Found No Subcategory Found - No Parentcategory Found + No Parent Categories Found Upload Image Mount Zao Llamas From 65eb685a7b4e0aabe138f82643e1720c81785d7b Mon Sep 17 00:00:00 2001 From: VaishSiddharth Date: Sat, 14 Jul 2018 00:09:49 +0530 Subject: [PATCH 82/94] Fix #1702 --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 782457303c..7f99bdd195 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -198,7 +198,7 @@ Background Image Media Image Failed No Image Found - No Subcategory Found + No subcategories found No Parent Categories Found Upload Image Mount Zao From 57a7f911a57c09be7c054750a66fab34941db07b Mon Sep 17 00:00:00 2001 From: VaishSiddharth Date: Sat, 14 Jul 2018 00:14:40 +0530 Subject: [PATCH 83/94] Fix #1702 and #1704 --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f99bdd195..3ce5fb534b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,7 +199,7 @@ Media Image Failed No Image Found No subcategories found - No Parent Categories Found + No parent categories found Upload Image Mount Zao Llamas From 09fdd71636ac44bb0f84ab8adc26ff678216c015 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Sat, 14 Jul 2018 17:16:53 +0530 Subject: [PATCH 84/94] added try catch statements --- .../commons/category/CategoryImagesListFragment.java | 11 +++++++---- .../fr/free/nrw/commons/category/GridViewAdapter.java | 1 + .../explore/categories/SearchCategoryFragment.java | 9 +++++++-- .../commons/explore/images/SearchImageFragment.java | 8 ++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index d5d0b0dcee..874abd6ccd 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -14,7 +14,6 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; -import android.widget.Toast; import java.util.List; import java.util.concurrent.TimeUnit; @@ -135,8 +134,13 @@ private void handleNoInternet() { */ private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading images inside a category"); - ViewUtil.showSnackbar(parentLayout, R.string.error_loading_images); - initErrorView(); + try{ + ViewUtil.showSnackbar(parentLayout, R.string.error_loading_images); + initErrorView(); + }catch (Exception e){ + e.printStackTrace(); + } + } /** @@ -219,7 +223,6 @@ private void handleSuccess(List collection) { setAdapter(collection); } else { if (gridAdapter.containsAll(collection)){ - hasMoreImages = false; return; } gridAdapter.addItems(collection); diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java index 0a0f8aa0c1..9f2dcd05e1 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -8,6 +8,7 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; +import android.widget.Toast; import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index 06b4c74449..3eddffe547 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -189,8 +189,13 @@ private void handleSuccess(List mediaList) { */ private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading queried categories"); - initErrorView(); - ViewUtil.showSnackbar(categoriesRecyclerView, R.string.error_loading_categories); + try { + initErrorView(); + ViewUtil.showSnackbar(categoriesRecyclerView, R.string.error_loading_categories); + }catch (Exception e){ + e.printStackTrace(); + } + } /** diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 7e333284d3..5b4ed8cde5 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -187,8 +187,12 @@ private void handleSuccess(List mediaList) { */ private void handleError(Throwable throwable) { Timber.e(throwable, "Error occurred while loading queried images"); - initErrorView(); - ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); + try { + initErrorView(); + ViewUtil.showSnackbar(imagesRecyclerView, R.string.error_loading_images); + }catch (Exception e){ + e.printStackTrace(); + } } /** From a2c5fc219abd825b40053dff91171ea21fd54336 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Sat, 14 Jul 2018 17:27:54 +0530 Subject: [PATCH 85/94] Optimimzed imports --- .../main/java/fr/free/nrw/commons/category/GridViewAdapter.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java index 9f2dcd05e1..b650570e3e 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -2,13 +2,11 @@ import android.app.Activity; import android.content.Context; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; -import android.widget.Toast; import java.util.ArrayList; import java.util.List; From bdb5f1615dd78eac5d340fe9d9d01e4b5aea332d Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Sat, 14 Jul 2018 22:05:03 +0530 Subject: [PATCH 86/94] loops replaced with Functions --- app/src/main/java/fr/free/nrw/commons/Media.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/Media.java b/app/src/main/java/fr/free/nrw/commons/Media.java index 5f6a498ea9..04f097d08e 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.java +++ b/app/src/main/java/fr/free/nrw/commons/Media.java @@ -3,13 +3,17 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -356,12 +360,8 @@ public void setCategories(List categories) { * @param descriptions Media descriptions */ void setDescriptions(Map descriptions) { - for (String key : this.descriptions.keySet()) { - this.descriptions.remove(key); - } - for (String key : descriptions.keySet()) { - this.descriptions.put(key, descriptions.get(key)); - } + this.descriptions.clear(); + this.descriptions.putAll(descriptions); } /** From 0bfcac1733690e05e3882c2c5e9aa4a3b0dfcaf4 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 18 Jul 2018 14:06:51 +0530 Subject: [PATCH 87/94] Javadocs for various methods added --- .../commons/category/CategoryDetailsActivity.java | 7 +++++++ .../nrw/commons/category/CategoryImageUtils.java | 6 ++++++ .../nrw/commons/di/CommonsApplicationModule.java | 7 ++++++- .../SearchCategoriesAdapterFactory.java | 4 ++++ .../images/SearchImagesAdapterFactory.java | 5 ++++- .../RecentSearchesContentProvider.java | 5 +++++ .../explore/recentsearches/RecentSearchesDao.java | 15 ++++++++++++++- .../nrw/commons/theme/NavigationBaseActivity.java | 4 ++++ 8 files changed, 50 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 8c3bec80c0..c37b914651 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -69,6 +69,10 @@ protected void onCreate(Bundle savedInstanceState) { forceInitBackButton(); } + /** + * This activity contains 3 tabs and a viewpager. This method is used to set the titles of tab, + * Set the fragments according to the tab selected in the viewPager. + */ private void setTabs() { List fragmentList = new ArrayList<>(); List titleList = new ArrayList<>(); @@ -107,6 +111,9 @@ private void setPageTitle() { } } + /** + * This method is called onClick of media inside category details (CategoryImageListFragment). + */ @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { tabLayout.setVisibility(View.GONE); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index f3b0501b08..941201235a 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -36,6 +36,12 @@ public static List getMediaList(NodeList childNodes) { return categoryImages; } + /** + * The method iterates over the child nodes to return a list of Subcategory name + * sorted alphabetically + * @param childNodes + * @return + */ public static List getSubCategoryList(NodeList childNodes) { List subCategories = new ArrayList<>(); for (int i = 0; i < childNodes.getLength(); i++) { diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 011f410751..39a9dac828 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -54,13 +54,18 @@ public ContentProviderClient provideCategoryContentProviderClient(Context contex return context.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY); } + /** + * This method is used to provide instance of RecentSearchContentProviderClient + * which provides content of Recent Searches from database + * @param context + * @return returns RecentSearchContentProviderClient + */ @Provides @Named("recentsearch") public ContentProviderClient provideRecentSearchContentProviderClient(Context context) { return context.getContentResolver().acquireContentProviderClient(RECENT_SEARCH_AUTHORITY); } - @Provides @Named("contribution") public ContentProviderClient provideContributionContentProviderClient(Context context) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index 2a28a83767..ad7f3241d0 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -7,6 +7,10 @@ import java.util.Collections; import java.util.List; +/** + * This class helps in creating adapter for categoriesRecyclerView in SearchCategoryFragment, + * implementing onClicks on categoriesRecyclerView Items + **/ public class SearchCategoriesAdapterFactory { private final SearchCategoriesRenderer.CategoryClickedListener listener; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java index b095669952..002236383f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java @@ -9,7 +9,10 @@ import fr.free.nrw.commons.Media; - +/** + * This class helps in creating adapter for imagesRecyclerView in SearchImagesFragment, + * implementing onClicks on imagesRecyclerView Items + **/ class SearchImagesAdapterFactory { private final SearchImagesRenderer.ImageClickedListener listener; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java index 810a781d40..a01571a719 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java @@ -21,6 +21,11 @@ import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID; import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME; + +/** + * This class contains functions for executing queries for + * inserting, searching, deleting, editing recent searches in SqLite DB + **/ public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { public static final String RECENT_SEARCH_AUTHORITY = "fr.free.nrw.commons.explore.recentsearches.contentprovider"; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index cc9ebc0441..e0e9d6844e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -17,7 +17,10 @@ import javax.inject.Named; import javax.inject.Provider; - +/** + * This class doesn't execute queries in database directly instead it contains the logic behind + * inserting, deleting, searching data from recent searches database. + **/ public class RecentSearchesDao { private final Provider clientProvider; @@ -121,6 +124,12 @@ public List recentSearches(int limit) { return items; } + + /** + * It creates an Recent Searches object from data stored in the SQLite DB by using cursor + * @param cursor + * @returns RecentSearch object + */ @NonNull RecentSearch fromCursor(Cursor cursor) { // Hardcoding column positions! @@ -131,6 +140,10 @@ RecentSearch fromCursor(Cursor cursor) { ); } + /** + * This class contains the database table architechture for recent searches, + * It also contains queries and logic necessary to the create, update, delete this table. + */ private ContentValues toContentValues(RecentSearch recentSearch) { ContentValues cv = new ContentValues(); cv.put(RecentSearchesDao.Table.COLUMN_NAME, recentSearch.getQuery()); diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index 5ddc31d5b5..7d975d2bfc 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -82,6 +82,10 @@ public void initBackButton() { toggle.setToolbarNavigationClickListener(v -> onBackPressed()); } + /** + * This method changes the toolbar icon to back regardless of any conditions that + * there is any fragment in the backStack or not + */ public void forceInitBackButton() { toggle.setDrawerIndicatorEnabled(false); toggle.setToolbarNavigationClickListener(v -> onBackPressed()); From 3e8612277a392f4072dbfc23527e5599d9e63e5a Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 18 Jul 2018 14:10:38 +0530 Subject: [PATCH 88/94] Fix java docs for Dao --- .../nrw/commons/explore/recentsearches/RecentSearchesDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index e0e9d6844e..28f6a65a8f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -128,7 +128,7 @@ public List recentSearches(int limit) { /** * It creates an Recent Searches object from data stored in the SQLite DB by using cursor * @param cursor - * @returns RecentSearch object + * @return RecentSearch object */ @NonNull RecentSearch fromCursor(Cursor cursor) { From 565d2b36d9b02658c4ab53756085d72a95372ed6 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 18 Jul 2018 14:06:51 +0530 Subject: [PATCH 89/94] Javadocs for various methods added --- .../commons/category/CategoryDetailsActivity.java | 7 +++++++ .../nrw/commons/category/CategoryImageUtils.java | 6 ++++++ .../nrw/commons/di/CommonsApplicationModule.java | 7 ++++++- .../SearchCategoriesAdapterFactory.java | 4 ++++ .../images/SearchImagesAdapterFactory.java | 5 ++++- .../RecentSearchesContentProvider.java | 5 +++++ .../explore/recentsearches/RecentSearchesDao.java | 15 ++++++++++++++- .../nrw/commons/theme/NavigationBaseActivity.java | 4 ++++ 8 files changed, 50 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index 8c3bec80c0..c37b914651 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -69,6 +69,10 @@ protected void onCreate(Bundle savedInstanceState) { forceInitBackButton(); } + /** + * This activity contains 3 tabs and a viewpager. This method is used to set the titles of tab, + * Set the fragments according to the tab selected in the viewPager. + */ private void setTabs() { List fragmentList = new ArrayList<>(); List titleList = new ArrayList<>(); @@ -107,6 +111,9 @@ private void setPageTitle() { } } + /** + * This method is called onClick of media inside category details (CategoryImageListFragment). + */ @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { tabLayout.setVisibility(View.GONE); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java index f3b0501b08..941201235a 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -36,6 +36,12 @@ public static List getMediaList(NodeList childNodes) { return categoryImages; } + /** + * The method iterates over the child nodes to return a list of Subcategory name + * sorted alphabetically + * @param childNodes + * @return + */ public static List getSubCategoryList(NodeList childNodes) { List subCategories = new ArrayList<>(); for (int i = 0; i < childNodes.getLength(); i++) { diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 011f410751..39a9dac828 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -54,13 +54,18 @@ public ContentProviderClient provideCategoryContentProviderClient(Context contex return context.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY); } + /** + * This method is used to provide instance of RecentSearchContentProviderClient + * which provides content of Recent Searches from database + * @param context + * @return returns RecentSearchContentProviderClient + */ @Provides @Named("recentsearch") public ContentProviderClient provideRecentSearchContentProviderClient(Context context) { return context.getContentResolver().acquireContentProviderClient(RECENT_SEARCH_AUTHORITY); } - @Provides @Named("contribution") public ContentProviderClient provideContributionContentProviderClient(Context context) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index 2a28a83767..ad7f3241d0 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -7,6 +7,10 @@ import java.util.Collections; import java.util.List; +/** + * This class helps in creating adapter for categoriesRecyclerView in SearchCategoryFragment, + * implementing onClicks on categoriesRecyclerView Items + **/ public class SearchCategoriesAdapterFactory { private final SearchCategoriesRenderer.CategoryClickedListener listener; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java index b095669952..002236383f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java @@ -9,7 +9,10 @@ import fr.free.nrw.commons.Media; - +/** + * This class helps in creating adapter for imagesRecyclerView in SearchImagesFragment, + * implementing onClicks on imagesRecyclerView Items + **/ class SearchImagesAdapterFactory { private final SearchImagesRenderer.ImageClickedListener listener; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java index 810a781d40..a01571a719 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java @@ -21,6 +21,11 @@ import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID; import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME; + +/** + * This class contains functions for executing queries for + * inserting, searching, deleting, editing recent searches in SqLite DB + **/ public class RecentSearchesContentProvider extends CommonsDaggerContentProvider { public static final String RECENT_SEARCH_AUTHORITY = "fr.free.nrw.commons.explore.recentsearches.contentprovider"; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index cc9ebc0441..e0e9d6844e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -17,7 +17,10 @@ import javax.inject.Named; import javax.inject.Provider; - +/** + * This class doesn't execute queries in database directly instead it contains the logic behind + * inserting, deleting, searching data from recent searches database. + **/ public class RecentSearchesDao { private final Provider clientProvider; @@ -121,6 +124,12 @@ public List recentSearches(int limit) { return items; } + + /** + * It creates an Recent Searches object from data stored in the SQLite DB by using cursor + * @param cursor + * @returns RecentSearch object + */ @NonNull RecentSearch fromCursor(Cursor cursor) { // Hardcoding column positions! @@ -131,6 +140,10 @@ RecentSearch fromCursor(Cursor cursor) { ); } + /** + * This class contains the database table architechture for recent searches, + * It also contains queries and logic necessary to the create, update, delete this table. + */ private ContentValues toContentValues(RecentSearch recentSearch) { ContentValues cv = new ContentValues(); cv.put(RecentSearchesDao.Table.COLUMN_NAME, recentSearch.getQuery()); diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index 5ddc31d5b5..7d975d2bfc 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -82,6 +82,10 @@ public void initBackButton() { toggle.setToolbarNavigationClickListener(v -> onBackPressed()); } + /** + * This method changes the toolbar icon to back regardless of any conditions that + * there is any fragment in the backStack or not + */ public void forceInitBackButton() { toggle.setDrawerIndicatorEnabled(false); toggle.setToolbarNavigationClickListener(v -> onBackPressed()); From 7aeb905fa59c6e1f7850b23377282447093364b7 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 18 Jul 2018 14:10:38 +0530 Subject: [PATCH 90/94] Fix java docs for Dao --- .../nrw/commons/explore/recentsearches/RecentSearchesDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index e0e9d6844e..28f6a65a8f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -128,7 +128,7 @@ public List recentSearches(int limit) { /** * It creates an Recent Searches object from data stored in the SQLite DB by using cursor * @param cursor - * @returns RecentSearch object + * @return RecentSearch object */ @NonNull RecentSearch fromCursor(Cursor cursor) { From 093b3172d954aaf90eee0c706204c8e5e5e68dff Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Wed, 18 Jul 2018 16:09:37 +0530 Subject: [PATCH 91/94] More javadocs added for explore Feature --- .../category/CategoryDetailsActivity.java | 34 +++++++++++++++++++ .../nrw/commons/explore/SearchActivity.java | 30 ++++++++++++++++ .../SearchCategoriesAdapterFactory.java | 5 +++ .../categories/SearchCategoriesRenderer.java | 3 +- .../categories/SearchCategoryFragment.java | 5 +++ .../explore/images/SearchImageFragment.java | 9 +++-- .../images/SearchImagesAdapterFactory.java | 5 +++ .../explore/images/SearchImagesRenderer.java | 3 +- .../explore/recentsearches/RecentSearch.java | 6 ++++ .../RecentSearchesContentProvider.java | 15 ++++++++ .../recentsearches/RecentSearchesDao.java | 23 +++++++++++++ .../RecentSearchesFragment.java | 3 ++ 12 files changed, 135 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index c37b914651..c335da6f41 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -147,6 +147,12 @@ public static void startYourself(Context context, String categoryName) { context.startActivity(intent); } + /** + * This method is called mediaDetailPagerFragment. It returns the Media Object at that Index + * @param i It is the index of which media object is to be returned which is same as + * current index of viewPager. + * @return Media Object + */ @Override public Media getMediaAtPosition(int i) { if (categoryImagesListFragment.getAdapter() == null) { @@ -157,6 +163,11 @@ public Media getMediaAtPosition(int i) { } } + /** + * This method is called on from getCount of MediaDetailPagerFragment + * The viewpager will contain same number of media items as that of media elements in adapter. + * @return Total Media count in the adapter + */ @Override public int getTotalMediaCount() { if (categoryImagesListFragment.getAdapter() == null) { @@ -165,20 +176,35 @@ public int getTotalMediaCount() { return categoryImagesListFragment.getAdapter().getCount(); } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void notifyDatasetChanged() { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void registerDataSetObserver(DataSetObserver observer) { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void unregisterDataSetObserver(DataSetObserver observer) { } + /** + * This method inflates the menu in the toolbar + */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -186,6 +212,10 @@ public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu); } + /** + * This method handles the logic on ItemSelect in toolbar menu + * Currently only 1 choice is available to open category details page in browser + */ @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -208,6 +238,10 @@ public boolean onOptionsItemSelected(MenuItem item) { } } + /** + * This method is called on backPressed of anyFragment in the activity. + * If condition is called when mediaDetailFragment is opened. + */ @Override public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount() == 1){ diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 6796ef0c3d..2d2f23e96e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -119,26 +119,45 @@ public void setTabs() { ); } + /** + * returns Media Object at position + * @param i position of Media in the imagesRecyclerView adapter. + */ @Override public Media getMediaAtPosition(int i) { return searchImageFragment.getImageAtPosition(i); } + /** + * returns total number of images present in the imagesRecyclerView adapter. + */ @Override public int getTotalMediaCount() { return searchImageFragment.getTotalImagesCount(); } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void notifyDatasetChanged() { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void registerDataSetObserver(DataSetObserver observer) { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void unregisterDataSetObserver(DataSetObserver observer) { @@ -171,6 +190,9 @@ public void onSearchImageClicked(int index) { forceInitBackButton(); } + /** + * This method is called on Screen Rotation + */ @Override protected void onResume() { if (supportFragmentManager.getBackStackEntryCount()==1){ @@ -183,6 +205,10 @@ protected void onResume() { super.onResume(); } + /** + * This method is called on backPressed of anyFragment in the activity. + * If condition is called when mediaDetailFragment is opened. + */ @Override public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount() == 1){ @@ -199,6 +225,10 @@ public void onBackPressed() { super.onBackPressed(); } + /** + * This method is called on click of a recent search to update query in SearchView. + * @param query Recent Search Query + */ public void updateText(String query) { searchView.setQuery(query, true); // Clear focus of searchView now. searchView.clearFocus(); does not seem to work Check the below link for more details. diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java index ad7f3241d0..3ac75e07e2 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesAdapterFactory.java @@ -18,6 +18,11 @@ public SearchCategoriesAdapterFactory(SearchCategoriesRenderer.CategoryClickedLi this.listener = listener; } + /** + * This method creates a recyclerViewAdapter for Categories. + * @param searchImageItemList List of category name to be displayed + * @return categoriesAdapter + **/ public RVRendererAdapter create(List searchImageItemList) { RendererBuilder builder = new RendererBuilder().bind(String.class, new SearchCategoriesRenderer(listener)); ListAdapteeCollection collection = new ListAdapteeCollection<>( diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java index 46e61c8aee..631b7b552a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoriesRenderer.java @@ -13,8 +13,7 @@ /** * presentation logic of individual category in search is handled here - */ - + **/ class SearchCategoriesRenderer extends Renderer { @BindView(R.id.textView1) TextView tvCategoryName; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index 3eddffe547..7f2b3ff930 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -66,11 +66,16 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { private List queryList = new ArrayList<>(); private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> { + // Called on Click of a individual category Item // Open Category Details activity CategoryDetailsActivity.startYourself(getContext(), item); saveQuery(query); }); + /** + * This method saves Search Query in the Recent Searches Database. + * @param query + */ private void saveQuery(String query) { RecentSearch recentSearch = recentSearchesDao.find(query); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index 5b4ed8cde5..a503207e4d 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -63,11 +63,16 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { private List queryList = new ArrayList<>(); private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> { + // Called on Click of a individual media Item int index = queryList.indexOf(item); ((SearchActivity)getContext()).onSearchImageClicked(index); saveQuery(query); }); + /** + * This method saves Search Query in the Recent Searches Database. + * @param query + */ private void saveQuery(String query) { RecentSearch recentSearch = recentSearchesDao.find(query); @@ -148,7 +153,7 @@ public void addImagesToList(String query) { /** * Handles the success scenario * it initializes the recycler view by adding items to the adapter - * @param mediaList + * @param mediaList List of media to be added */ private void handlePaginationSuccess(List mediaList) { queryList.addAll(mediaList); @@ -162,7 +167,7 @@ private void handlePaginationSuccess(List mediaList) { /** * Handles the success scenario * it initializes the recycler view by adding items to the adapter - * @param mediaList + * @param mediaList List of media to be shown */ private void handleSuccess(List mediaList) { queryList = mediaList; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java index 002236383f..94930e2619 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesAdapterFactory.java @@ -20,6 +20,11 @@ class SearchImagesAdapterFactory { this.listener = listener; } + /** + * This method creates a recyclerViewAdapter for Media. + * @param searchImageItemList List of Media objects to be displayed + * @return imagesAdapter + **/ public RVRendererAdapter create(List searchImageItemList) { RendererBuilder builder = new RendererBuilder() .bind(Media.class, new SearchImagesRenderer(listener)); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java index eb0ba7bf60..42c044d705 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImagesRenderer.java @@ -15,8 +15,7 @@ /** * presentation logic of individual image in search is handled here - */ - + **/ class SearchImagesRenderer extends Renderer { @BindView(R.id.categoryImageTitle) TextView tvImageName; @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java index 6cc30e21a8..c5172bb7cd 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearch.java @@ -12,6 +12,12 @@ public class RecentSearch { private String query; private Date lastSearched; + /** + * Constructor + * @param contentUri the content URI for this query + * @param query query name + * @param lastSearched last searched date + */ public RecentSearch(Uri contentUri, String query, Date lastSearched) { this.contentUri = contentUri; this.query = query; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java index a01571a719..bf3cf959ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.java @@ -47,6 +47,9 @@ public static Uri uriForId(int id) { @Inject DBOpenHelper dbOpenHelper; + /** + * This functions executes query for searching recent searches in SqLite DB + **/ @SuppressWarnings("ConstantConditions") @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection, @@ -88,6 +91,9 @@ public String getType(@NonNull Uri uri) { return null; } + /** + * This functions executes query for inserting a recentSearch object in SqLite DB + **/ @SuppressWarnings("ConstantConditions") @Override public Uri insert(@NonNull Uri uri, ContentValues contentValues) { @@ -105,6 +111,9 @@ public Uri insert(@NonNull Uri uri, ContentValues contentValues) { return Uri.parse(BASE_URI + "/" + id); } + /** + * This functions executes query for deleting a recentSearch object in SqLite DB + **/ @Override public int delete(@NonNull Uri uri, String s, String[] strings) { int rows; @@ -125,6 +134,9 @@ public int delete(@NonNull Uri uri, String s, String[] strings) { return rows; } + /** + * This functions executes query for inserting multiple recentSearch objects in SqLite DB + **/ @SuppressWarnings("ConstantConditions") @Override public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { @@ -148,6 +160,9 @@ public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { return values.length; } + /** + * This functions executes query for updating a particular recentSearch object in SqLite DB + **/ @SuppressWarnings("ConstantConditions") @Override public int update(@NonNull Uri uri, ContentValues contentValues, String selection, diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java index 28f6a65a8f..4bc84b7b17 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.java @@ -30,6 +30,10 @@ public RecentSearchesDao(@Named("recentsearch") Provider this.clientProvider = clientProvider; } + /** + * This method is called on click of media/ categories for storing them in recent searches + * @param recentSearch a recent searches object that is to be added in SqLite DB + */ public void save(RecentSearch recentSearch) { ContentProviderClient db = clientProvider.get(); try { @@ -45,6 +49,11 @@ public void save(RecentSearch recentSearch) { } } + /** + * This method is called on confirmation of delete recent searches. + * It deletes latest 10 recent searches from the database + * @param recentSearchesStringList list of recent searches to be deleted + */ public void deleteAll(List recentSearchesStringList) { ContentProviderClient db = clientProvider.get(); for (String recentSearchName : recentSearchesStringList) { @@ -176,15 +185,29 @@ public static class Table { + COLUMN_LAST_USED + " INTEGER" + ");"; + /** + * This method creates a RecentSearchesTable in SQLiteDatabase + * @param db SQLiteDatabase + */ public static void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_STATEMENT); } + /** + * This method deletes RecentSearchesTable from SQLiteDatabase + * @param db SQLiteDatabase + */ public static void onDelete(SQLiteDatabase db) { db.execSQL(DROP_TABLE_STATEMENT); onCreate(db); } + /** + * This method is called on migrating from a older version to a newer version + * @param db SQLiteDatabase + * @param from Version from which we are migrating + * @param to Version to which we are migrating + */ public static void onUpdate(SQLiteDatabase db, int from, int to) { if (from == to) { return; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java index 76a04c0aee..5c109fbb4e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.java @@ -73,6 +73,9 @@ public void onResume() { super.onResume(); } + /** + * This method is called when search query is null to update Recent Searches + */ public void updateRecentSearches() { recentSearches = recentSearchesDao.recentSearches(10); adapter = new ArrayAdapter(getContext(),R.layout.item_recent_searches, recentSearches); From 9e4f8be88ee91b837e48441c89b2d7648de771d8 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 23 Jul 2018 13:54:37 +0530 Subject: [PATCH 92/94] Javadocs added --- .../category/CategoryDetailsActivity.java | 10 ++- .../category/CategoryImagesActivity.java | 42 +++++++++++- .../category/CategoryImagesListFragment.java | 21 ++++-- .../nrw/commons/category/GridViewAdapter.java | 4 +- .../nrw/commons/explore/SearchActivity.java | 6 +- .../nrw/commons/explore/ViewPagerAdapter.java | 23 +++++-- .../commons/media/MediaDetailFragment.java | 1 + .../recentsearches/RecentSearchesDaoTest.kt | 65 ++++++++++++++++++- 8 files changed, 147 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index c335da6f41..3ab3c2c072 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -43,8 +43,6 @@ public class CategoryDetailsActivity extends NavigationBaseActivity private FragmentManager supportFragmentManager; private CategoryImagesListFragment categoryImagesListFragment; - private SubCategoryListFragment subCategoryListFragment; - private SubCategoryListFragment parentCategoryListFragment; private MediaDetailPagerFragment mediaDetails; private String categoryName; @BindView(R.id.mediaContainer) FrameLayout mediaContainer; @@ -77,8 +75,8 @@ private void setTabs() { List fragmentList = new ArrayList<>(); List titleList = new ArrayList<>(); categoryImagesListFragment = new CategoryImagesListFragment(); - subCategoryListFragment = new SubCategoryListFragment(); - parentCategoryListFragment = new SubCategoryListFragment(); + SubCategoryListFragment subCategoryListFragment = new SubCategoryListFragment(); + SubCategoryListFragment parentCategoryListFragment = new SubCategoryListFragment(); categoryName = getIntent().getStringExtra("categoryName"); if (getIntent() != null && categoryName != null) { Bundle arguments = new Bundle(); @@ -137,7 +135,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { /** * Consumers should be simply using this method to use this activity. - * @param context + * @param context A Context of the application package implementing this class. * @param categoryName Name of the category for displaying its details */ public static void startYourself(Context context, String categoryName) { @@ -244,7 +242,7 @@ public boolean onOptionsItemSelected(MenuItem item) { */ @Override public void onBackPressed() { - if (getSupportFragmentManager().getBackStackEntryCount() == 1){ + if (supportFragmentManager.getBackStackEntryCount() == 1){ // back to search so show search toolbar and hide navigation toolbar tabLayout.setVisibility(View.VISIBLE); viewPager.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 7992c3f8b3..cb154c8192 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -48,6 +48,10 @@ protected void onAuthFailure() { } + /** + * This method is called on backPressed of anyFragment in the activity. + * We are changing the icon here from back to hamburger icon. + */ @Override public void onBackPressed() { initDrawer(); @@ -100,6 +104,9 @@ private void setPageTitle() { public void onBackStackChanged() { } + /** + * This method is called onClick of media inside category details (CategoryImageListFragment). + */ @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (mediaDetails == null || !mediaDetails.isVisible()) { @@ -118,6 +125,9 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { forceInitBackButton(); } + /** + * This method is called on backPressed when mediaDetailFragment is opened in the activity. + */ @Override protected void onResume() { if (supportFragmentManager.getBackStackEntryCount()==1){ @@ -130,7 +140,7 @@ protected void onResume() { /** * Consumers should be simply using this method to use this activity. - * @param context + * @param context A Context of the application package implementing this class. * @param title Page title * @param categoryName Name of the category for displaying its images */ @@ -142,6 +152,12 @@ public static void startYourself(Context context, String title, String categoryN context.startActivity(intent); } + /** + * This method is called mediaDetailPagerFragment. It returns the Media Object at that Index + * @param i It is the index of which media object is to be returned which is same as + * current index of viewPager. + * @return Media Object + */ @Override public Media getMediaAtPosition(int i) { if (categoryImagesListFragment.getAdapter() == null) { @@ -152,6 +168,11 @@ public Media getMediaAtPosition(int i) { } } + /** + * This method is called on from getCount of MediaDetailPagerFragment + * The viewpager will contain same number of media items as that of media elements in adapter. + * @return Total Media count in the adapter + */ @Override public int getTotalMediaCount() { if (categoryImagesListFragment.getAdapter() == null) { @@ -160,21 +181,36 @@ public int getTotalMediaCount() { return categoryImagesListFragment.getAdapter().getCount(); } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void notifyDatasetChanged() { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void registerDataSetObserver(DataSetObserver observer) { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void unregisterDataSetObserver(DataSetObserver observer) { } + /** + * This method inflates the menu in the toolbar + */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -182,6 +218,10 @@ public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu); } + /** + * This method handles the logic on ItemSelect in toolbar menu + * Currently only 1 choice is available to open search page of the app + */ @Override public boolean onOptionsItemSelected(MenuItem item) { diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 874abd6ccd..7fb36542c3 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -14,6 +14,7 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; +import android.widget.Toast; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,7 +52,7 @@ public class CategoryImagesListFragment extends DaggerFragment { @BindView(R.id.categoryImagesList) GridView gridView; @BindView(R.id.parentLayout) RelativeLayout parentLayout; private boolean hasMoreImages = true; - private boolean isLoading; + private boolean isLoading=true; private String categoryName = null; @Inject CategoryImageController controller; @@ -150,7 +151,7 @@ private void initErrorView() { progressBar.setVisibility(GONE); if (gridAdapter == null || gridAdapter.isEmpty()) { statusTextView.setVisibility(VISIBLE); - statusTextView.setText(getContext().getString(R.string.no_images_found)); + statusTextView.setText(getString(R.string.no_images_found)); } else { statusTextView.setVisibility(GONE); } @@ -158,7 +159,7 @@ private void initErrorView() { /** * Initializes the adapter with a list of Media objects - * @param mediaList + * @param mediaList List of new Media to be displayed */ private void setAdapter(List mediaList) { gridAdapter = new GridViewAdapter(this.getContext(), R.layout.layout_category_images, mediaList); @@ -178,11 +179,11 @@ public void onScrollStateChanged(AbsListView view, int scrollState) { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount >= totalItemCount)) { + if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount + 1 >= totalItemCount)) { isLoading = true; fetchMoreImages(); - }else { - isLoading = false; + } + if (!hasMoreImages){ progressBar.setVisibility(GONE); } } @@ -210,7 +211,7 @@ private void fetchMoreImages() { /** * Handles the success scenario * On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter - * @param collection + * @param collection List of new Media to be displayed */ private void handleSuccess(List collection) { if(collection == null || collection.isEmpty()) { @@ -223,6 +224,7 @@ private void handleSuccess(List collection) { setAdapter(collection); } else { if (gridAdapter.containsAll(collection)){ + hasMoreImages = false; return; } gridAdapter.addItems(collection); @@ -233,6 +235,11 @@ private void handleSuccess(List collection) { statusTextView.setVisibility(GONE); } + /** + * It return an instance of gridView adapter which helps in extracting media details + * used by the gridView + * @return GridView Adapter + */ public ListAdapter getAdapter() { return gridView.getAdapter(); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java index b650570e3e..f8c54905e2 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -43,7 +43,7 @@ public void addItems(List images) { } /** - * Check the last item in the new list with old list and returns true if they are same + * Check the first item in the new list with old list and returns true if they are same * Its triggered on successful response of the fetch images API. * @param images */ @@ -51,7 +51,7 @@ public boolean containsAll(List images){ if (data == null) { data = new ArrayList<>(); } - return images.get(images.size()-1).getFilename().equals(data.get(data.size()-1).getFilename()); + return images.get(0).getFilename().equals(data.get(0).getFilename()); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 2d2f23e96e..24df6be5fa 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -51,7 +51,6 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai private RecentSearchesFragment recentSearchesFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; - private String query; ViewPagerAdapter viewPagerAdapter; @Override @@ -74,6 +73,10 @@ protected void onCreate(Bundle savedInstanceState) { } + /** + * This method sets the search history fragment. + * Search history fragment is displayed when query is empty. + */ private void setSearchHistoryFragment() { recentSearchesFragment = new RecentSearchesFragment(); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); @@ -105,7 +108,6 @@ public void setTabs() { viewPager.setVisibility(View.VISIBLE); tabLayout.setVisibility(View.VISIBLE); searchHistoryContainer.setVisibility(View.GONE); - this.query = query.toString(); searchImageFragment.updateImageList(query.toString()); searchCategoryFragment.updateCategoryList(query.toString()); }else { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java index 499ec2386c..5ebbd4f376 100755 --- a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java @@ -18,29 +18,40 @@ public ViewPagerAdapter(FragmentManager manager) { super(manager); } + /** + * This method returns the fragment of the viewpager at a particular position + * @param position + */ @Override public Fragment getItem(int position) { return fragmentList.get(position); } + /** + * This method returns the total number of fragments in the viewpager. + * @return size + */ @Override public int getCount() { return fragmentList.size(); } + /** + * This method sets the fragment and title list in the viewpager + * @param fragmentList List of all fragments to be displayed in the viewpager + * @param fragmentTitleList List of all titles of the fragments + */ public void setTabData(List fragmentList, List fragmentTitleList) { this.fragmentList = fragmentList; this.fragmentTitleList = fragmentTitleList; } + /** + * This method returns the title of the page at a particular position + * @param position + */ @Override public CharSequence getPageTitle(int position) { return fragmentTitleList.get(position); } - - - @Override - public int getItemPosition(Object object) { - return POSITION_NONE; - } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 642b1ca33e..d1c6ecbe24 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -427,6 +427,7 @@ private View buildCatLabel(final String catName, ViewGroup categoryContainer) { textView.setText(catName); if (categoriesLoaded && categoriesPresent) { textView.setOnClickListener(view -> { + // Open Category Details page String selectedCategoryTitle = "Category:" + catName; Intent intent = new Intent(getContext(), CategoryDetailsActivity.class); intent.putExtra("categoryName", selectedCategoryTitle); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt index d319f2708c..d764c8da7c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt @@ -37,12 +37,18 @@ class RecentSearchesDaoTest { testObject = RecentSearchesDao { client } } + /** + * Unit Test for creating a table for recent Searches + */ @Test fun createTable() { onCreate(database) verify(database).execSQL(CREATE_TABLE_STATEMENT) } + /** + * Unit Test for deleting table for recent Searches + */ @Test fun deleteTable() { onDelete(database) @@ -52,6 +58,9 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for migrating from database version 1 to 2 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v1_to_v2() { onUpdate(database, 1, 2) @@ -59,6 +68,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 2 to 3 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v2_to_v3() { onUpdate(database, 2, 3) @@ -66,6 +78,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 3 to 4 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v3_to_v4() { onUpdate(database, 3, 4) @@ -73,6 +88,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 4 to 5 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v4_to_v5() { onUpdate(database, 4, 5) @@ -80,6 +98,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 5 to 6 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v5_to_v6() { onUpdate(database, 5, 6) @@ -87,12 +108,18 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 6 to 7 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v6_to_v7() { onUpdate(database, 6, 7) verify(database).execSQL(CREATE_TABLE_STATEMENT) } + /** + * Unit Test for migrating from database version 7 to 8 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v7_to_v8() { onUpdate(database, 7, 8) @@ -100,6 +127,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from creating a row without using ID in recent Searches Table + */ @Test fun createFromCursor() { createCursor(1).let { cursor -> @@ -112,6 +142,9 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for migrating from updating a row using contentUri in recent Searches Table + */ @Test fun saveExistingQuery() { createCursor(1).let { @@ -128,6 +161,9 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for migrating from creating a row using ID in recent Searches Table + */ @Test fun saveNewQuery() { val contentUri = RecentSearchesContentProvider.uriForId(111) @@ -145,24 +181,36 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for checking translation exceptions in searching a row from DB using recent search query + */ @Test(expected = RuntimeException::class) fun findRecentSearchTranslatesExceptions() { whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenThrow(RemoteException("")) testObject.find("butterfly") } + /** + * Unit Test for checking data if it's not present in searching a row from DB using recent search query + */ @Test fun whenTheresNoDataFindReturnsNull_nullCursor() { whenever(client.query(any(), any(), any(), any(), any())).thenReturn(null) assertNull(testObject.find("butterfly")) } + /** + * Unit Test for checking data if it's not present in searching a row from DB using recent search query + */ @Test fun whenTheresNoDataFindReturnsNull_emptyCursor() { whenever(client.query(any(), any(), any(), any(), any())).thenReturn(createCursor(0)) assertNull(testObject.find("butterfly")) } + /** + * Unit Test for checking if cursor's are closed after use or not + */ @Test fun cursorsAreClosedAfterUse() { val mockCursor: Cursor = mock() @@ -174,7 +222,9 @@ class RecentSearchesDaoTest { verify(mockCursor).close() } - + /** + * Unit Test for checking search results after searching a row from DB using recent search query + */ @Test fun findRecentSearchQuery() { whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1)) @@ -196,6 +246,9 @@ class RecentSearchesDaoTest { assertEquals("butterfly", queryCaptor.firstValue[0]) } + /** + * Unit Test for checking if cursor's are closed after recent search query or not + */ @Test fun cursorsAreClosedAfterRecentSearchQuery() { val mockCursor: Cursor = mock() @@ -207,6 +260,9 @@ class RecentSearchesDaoTest { verify(mockCursor).close() } + /** + * Unit Test for checking when recent searches returns less than the limit + */ @Test fun recentSearchesReturnsLessThanLimit() { whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(1)) @@ -226,6 +282,9 @@ class RecentSearchesDaoTest { assertEquals(0, queryCaptor.firstValue.size) } + /** + * Unit Test for checking size or list recieved from recent searches + */ @Test fun recentSearchesHonorsLimit() { whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(10)) @@ -235,6 +294,10 @@ class RecentSearchesDaoTest { assertEquals(5, result.size) } + /** + * Unit Test for creating entries in recent searches database. + * @param rowCount No of rows + */ private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { for (i in 0 until rowCount) { addRow(listOf("1", "butterfly", "123")) From 1249afefba72e448d89025b828957d94ea31f6da Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 23 Jul 2018 13:54:37 +0530 Subject: [PATCH 93/94] Javadocs added --- .../category/CategoryDetailsActivity.java | 10 ++- .../category/CategoryImagesActivity.java | 42 +++++++++++- .../category/CategoryImagesListFragment.java | 21 ++++-- .../nrw/commons/category/GridViewAdapter.java | 4 +- .../nrw/commons/explore/SearchActivity.java | 6 +- .../nrw/commons/explore/ViewPagerAdapter.java | 23 +++++-- .../commons/media/MediaDetailFragment.java | 1 + .../recentsearches/RecentSearchesDaoTest.kt | 65 ++++++++++++++++++- 8 files changed, 147 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index c335da6f41..3ab3c2c072 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -43,8 +43,6 @@ public class CategoryDetailsActivity extends NavigationBaseActivity private FragmentManager supportFragmentManager; private CategoryImagesListFragment categoryImagesListFragment; - private SubCategoryListFragment subCategoryListFragment; - private SubCategoryListFragment parentCategoryListFragment; private MediaDetailPagerFragment mediaDetails; private String categoryName; @BindView(R.id.mediaContainer) FrameLayout mediaContainer; @@ -77,8 +75,8 @@ private void setTabs() { List fragmentList = new ArrayList<>(); List titleList = new ArrayList<>(); categoryImagesListFragment = new CategoryImagesListFragment(); - subCategoryListFragment = new SubCategoryListFragment(); - parentCategoryListFragment = new SubCategoryListFragment(); + SubCategoryListFragment subCategoryListFragment = new SubCategoryListFragment(); + SubCategoryListFragment parentCategoryListFragment = new SubCategoryListFragment(); categoryName = getIntent().getStringExtra("categoryName"); if (getIntent() != null && categoryName != null) { Bundle arguments = new Bundle(); @@ -137,7 +135,7 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { /** * Consumers should be simply using this method to use this activity. - * @param context + * @param context A Context of the application package implementing this class. * @param categoryName Name of the category for displaying its details */ public static void startYourself(Context context, String categoryName) { @@ -244,7 +242,7 @@ public boolean onOptionsItemSelected(MenuItem item) { */ @Override public void onBackPressed() { - if (getSupportFragmentManager().getBackStackEntryCount() == 1){ + if (supportFragmentManager.getBackStackEntryCount() == 1){ // back to search so show search toolbar and hide navigation toolbar tabLayout.setVisibility(View.VISIBLE); viewPager.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 7992c3f8b3..cb154c8192 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -48,6 +48,10 @@ protected void onAuthFailure() { } + /** + * This method is called on backPressed of anyFragment in the activity. + * We are changing the icon here from back to hamburger icon. + */ @Override public void onBackPressed() { initDrawer(); @@ -100,6 +104,9 @@ private void setPageTitle() { public void onBackStackChanged() { } + /** + * This method is called onClick of media inside category details (CategoryImageListFragment). + */ @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (mediaDetails == null || !mediaDetails.isVisible()) { @@ -118,6 +125,9 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { forceInitBackButton(); } + /** + * This method is called on backPressed when mediaDetailFragment is opened in the activity. + */ @Override protected void onResume() { if (supportFragmentManager.getBackStackEntryCount()==1){ @@ -130,7 +140,7 @@ protected void onResume() { /** * Consumers should be simply using this method to use this activity. - * @param context + * @param context A Context of the application package implementing this class. * @param title Page title * @param categoryName Name of the category for displaying its images */ @@ -142,6 +152,12 @@ public static void startYourself(Context context, String title, String categoryN context.startActivity(intent); } + /** + * This method is called mediaDetailPagerFragment. It returns the Media Object at that Index + * @param i It is the index of which media object is to be returned which is same as + * current index of viewPager. + * @return Media Object + */ @Override public Media getMediaAtPosition(int i) { if (categoryImagesListFragment.getAdapter() == null) { @@ -152,6 +168,11 @@ public Media getMediaAtPosition(int i) { } } + /** + * This method is called on from getCount of MediaDetailPagerFragment + * The viewpager will contain same number of media items as that of media elements in adapter. + * @return Total Media count in the adapter + */ @Override public int getTotalMediaCount() { if (categoryImagesListFragment.getAdapter() == null) { @@ -160,21 +181,36 @@ public int getTotalMediaCount() { return categoryImagesListFragment.getAdapter().getCount(); } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void notifyDatasetChanged() { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void registerDataSetObserver(DataSetObserver observer) { } + /** + * This method is never called but it was in MediaDetailProvider Interface + * so it needs to be overrided. + */ @Override public void unregisterDataSetObserver(DataSetObserver observer) { } + /** + * This method inflates the menu in the toolbar + */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -182,6 +218,10 @@ public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu); } + /** + * This method handles the logic on ItemSelect in toolbar menu + * Currently only 1 choice is available to open search page of the app + */ @Override public boolean onOptionsItemSelected(MenuItem item) { diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 874abd6ccd..7fb36542c3 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -14,6 +14,7 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; +import android.widget.Toast; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,7 +52,7 @@ public class CategoryImagesListFragment extends DaggerFragment { @BindView(R.id.categoryImagesList) GridView gridView; @BindView(R.id.parentLayout) RelativeLayout parentLayout; private boolean hasMoreImages = true; - private boolean isLoading; + private boolean isLoading=true; private String categoryName = null; @Inject CategoryImageController controller; @@ -150,7 +151,7 @@ private void initErrorView() { progressBar.setVisibility(GONE); if (gridAdapter == null || gridAdapter.isEmpty()) { statusTextView.setVisibility(VISIBLE); - statusTextView.setText(getContext().getString(R.string.no_images_found)); + statusTextView.setText(getString(R.string.no_images_found)); } else { statusTextView.setVisibility(GONE); } @@ -158,7 +159,7 @@ private void initErrorView() { /** * Initializes the adapter with a list of Media objects - * @param mediaList + * @param mediaList List of new Media to be displayed */ private void setAdapter(List mediaList) { gridAdapter = new GridViewAdapter(this.getContext(), R.layout.layout_category_images, mediaList); @@ -178,11 +179,11 @@ public void onScrollStateChanged(AbsListView view, int scrollState) { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount >= totalItemCount)) { + if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount + 1 >= totalItemCount)) { isLoading = true; fetchMoreImages(); - }else { - isLoading = false; + } + if (!hasMoreImages){ progressBar.setVisibility(GONE); } } @@ -210,7 +211,7 @@ private void fetchMoreImages() { /** * Handles the success scenario * On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter - * @param collection + * @param collection List of new Media to be displayed */ private void handleSuccess(List collection) { if(collection == null || collection.isEmpty()) { @@ -223,6 +224,7 @@ private void handleSuccess(List collection) { setAdapter(collection); } else { if (gridAdapter.containsAll(collection)){ + hasMoreImages = false; return; } gridAdapter.addItems(collection); @@ -233,6 +235,11 @@ private void handleSuccess(List collection) { statusTextView.setVisibility(GONE); } + /** + * It return an instance of gridView adapter which helps in extracting media details + * used by the gridView + * @return GridView Adapter + */ public ListAdapter getAdapter() { return gridView.getAdapter(); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java index b650570e3e..f8c54905e2 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -43,7 +43,7 @@ public void addItems(List images) { } /** - * Check the last item in the new list with old list and returns true if they are same + * Check the first item in the new list with old list and returns true if they are same * Its triggered on successful response of the fetch images API. * @param images */ @@ -51,7 +51,7 @@ public boolean containsAll(List images){ if (data == null) { data = new ArrayList<>(); } - return images.get(images.size()-1).getFilename().equals(data.get(data.size()-1).getFilename()); + return images.get(0).getFilename().equals(data.get(0).getFilename()); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 2d2f23e96e..24df6be5fa 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -51,7 +51,6 @@ public class SearchActivity extends NavigationBaseActivity implements MediaDetai private RecentSearchesFragment recentSearchesFragment; private FragmentManager supportFragmentManager; private MediaDetailPagerFragment mediaDetails; - private String query; ViewPagerAdapter viewPagerAdapter; @Override @@ -74,6 +73,10 @@ protected void onCreate(Bundle savedInstanceState) { } + /** + * This method sets the search history fragment. + * Search history fragment is displayed when query is empty. + */ private void setSearchHistoryFragment() { recentSearchesFragment = new RecentSearchesFragment(); FragmentTransaction transaction = supportFragmentManager.beginTransaction(); @@ -105,7 +108,6 @@ public void setTabs() { viewPager.setVisibility(View.VISIBLE); tabLayout.setVisibility(View.VISIBLE); searchHistoryContainer.setVisibility(View.GONE); - this.query = query.toString(); searchImageFragment.updateImageList(query.toString()); searchCategoryFragment.updateCategoryList(query.toString()); }else { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java index 499ec2386c..5ebbd4f376 100755 --- a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java @@ -18,29 +18,40 @@ public ViewPagerAdapter(FragmentManager manager) { super(manager); } + /** + * This method returns the fragment of the viewpager at a particular position + * @param position + */ @Override public Fragment getItem(int position) { return fragmentList.get(position); } + /** + * This method returns the total number of fragments in the viewpager. + * @return size + */ @Override public int getCount() { return fragmentList.size(); } + /** + * This method sets the fragment and title list in the viewpager + * @param fragmentList List of all fragments to be displayed in the viewpager + * @param fragmentTitleList List of all titles of the fragments + */ public void setTabData(List fragmentList, List fragmentTitleList) { this.fragmentList = fragmentList; this.fragmentTitleList = fragmentTitleList; } + /** + * This method returns the title of the page at a particular position + * @param position + */ @Override public CharSequence getPageTitle(int position) { return fragmentTitleList.get(position); } - - - @Override - public int getItemPosition(Object object) { - return POSITION_NONE; - } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 642b1ca33e..d1c6ecbe24 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -427,6 +427,7 @@ private View buildCatLabel(final String catName, ViewGroup categoryContainer) { textView.setText(catName); if (categoriesLoaded && categoriesPresent) { textView.setOnClickListener(view -> { + // Open Category Details page String selectedCategoryTitle = "Category:" + catName; Intent intent = new Intent(getContext(), CategoryDetailsActivity.class); intent.putExtra("categoryName", selectedCategoryTitle); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt index d319f2708c..d764c8da7c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt @@ -37,12 +37,18 @@ class RecentSearchesDaoTest { testObject = RecentSearchesDao { client } } + /** + * Unit Test for creating a table for recent Searches + */ @Test fun createTable() { onCreate(database) verify(database).execSQL(CREATE_TABLE_STATEMENT) } + /** + * Unit Test for deleting table for recent Searches + */ @Test fun deleteTable() { onDelete(database) @@ -52,6 +58,9 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for migrating from database version 1 to 2 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v1_to_v2() { onUpdate(database, 1, 2) @@ -59,6 +68,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 2 to 3 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v2_to_v3() { onUpdate(database, 2, 3) @@ -66,6 +78,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 3 to 4 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v3_to_v4() { onUpdate(database, 3, 4) @@ -73,6 +88,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 4 to 5 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v4_to_v5() { onUpdate(database, 4, 5) @@ -80,6 +98,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 5 to 6 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v5_to_v6() { onUpdate(database, 5, 6) @@ -87,12 +108,18 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from database version 6 to 7 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v6_to_v7() { onUpdate(database, 6, 7) verify(database).execSQL(CREATE_TABLE_STATEMENT) } + /** + * Unit Test for migrating from database version 7 to 8 for recent Searches Table + */ @Test fun migrateTableVersionFrom_v7_to_v8() { onUpdate(database, 7, 8) @@ -100,6 +127,9 @@ class RecentSearchesDaoTest { verifyZeroInteractions(database) } + /** + * Unit Test for migrating from creating a row without using ID in recent Searches Table + */ @Test fun createFromCursor() { createCursor(1).let { cursor -> @@ -112,6 +142,9 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for migrating from updating a row using contentUri in recent Searches Table + */ @Test fun saveExistingQuery() { createCursor(1).let { @@ -128,6 +161,9 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for migrating from creating a row using ID in recent Searches Table + */ @Test fun saveNewQuery() { val contentUri = RecentSearchesContentProvider.uriForId(111) @@ -145,24 +181,36 @@ class RecentSearchesDaoTest { } } + /** + * Unit Test for checking translation exceptions in searching a row from DB using recent search query + */ @Test(expected = RuntimeException::class) fun findRecentSearchTranslatesExceptions() { whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenThrow(RemoteException("")) testObject.find("butterfly") } + /** + * Unit Test for checking data if it's not present in searching a row from DB using recent search query + */ @Test fun whenTheresNoDataFindReturnsNull_nullCursor() { whenever(client.query(any(), any(), any(), any(), any())).thenReturn(null) assertNull(testObject.find("butterfly")) } + /** + * Unit Test for checking data if it's not present in searching a row from DB using recent search query + */ @Test fun whenTheresNoDataFindReturnsNull_emptyCursor() { whenever(client.query(any(), any(), any(), any(), any())).thenReturn(createCursor(0)) assertNull(testObject.find("butterfly")) } + /** + * Unit Test for checking if cursor's are closed after use or not + */ @Test fun cursorsAreClosedAfterUse() { val mockCursor: Cursor = mock() @@ -174,7 +222,9 @@ class RecentSearchesDaoTest { verify(mockCursor).close() } - + /** + * Unit Test for checking search results after searching a row from DB using recent search query + */ @Test fun findRecentSearchQuery() { whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1)) @@ -196,6 +246,9 @@ class RecentSearchesDaoTest { assertEquals("butterfly", queryCaptor.firstValue[0]) } + /** + * Unit Test for checking if cursor's are closed after recent search query or not + */ @Test fun cursorsAreClosedAfterRecentSearchQuery() { val mockCursor: Cursor = mock() @@ -207,6 +260,9 @@ class RecentSearchesDaoTest { verify(mockCursor).close() } + /** + * Unit Test for checking when recent searches returns less than the limit + */ @Test fun recentSearchesReturnsLessThanLimit() { whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(1)) @@ -226,6 +282,9 @@ class RecentSearchesDaoTest { assertEquals(0, queryCaptor.firstValue.size) } + /** + * Unit Test for checking size or list recieved from recent searches + */ @Test fun recentSearchesHonorsLimit() { whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(10)) @@ -235,6 +294,10 @@ class RecentSearchesDaoTest { assertEquals(5, result.size) } + /** + * Unit Test for creating entries in recent searches database. + * @param rowCount No of rows + */ private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { for (i in 0 until rowCount) { addRow(listOf("1", "butterfly", "123")) From 11b13bed4897df05cc3ec5ef6e81bd793792e623 Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 23 Jul 2018 16:53:39 +0530 Subject: [PATCH 94/94] Improvements in indentation (#1739) --- .../fr/free/nrw/commons/category/CategoryImagesActivity.java | 4 +++- .../free/nrw/commons/category/CategoryImagesListFragment.java | 4 ++-- .../main/java/fr/free/nrw/commons/explore/SearchActivity.java | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index cb154c8192..3821344724 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -119,7 +119,9 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { .add(R.id.fragmentContainer, mediaDetails) .addToBackStack(null) .commit(); - supportFragmentManager.executePendingTransactions(); + // Reason for using hide, add instead of replace is to maintain scroll position after + // coming back to the search activity. See https://github.com/commons-app/apps-android-commons/issues/1631 + // https://stackoverflow.com/questions/11353075/how-can-i-maintain-fragment-state-when-added-to-the-back-stack/19022550#19022550 supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(i); forceInitBackButton(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 7fb36542c3..385662a054 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -52,7 +52,7 @@ public class CategoryImagesListFragment extends DaggerFragment { @BindView(R.id.categoryImagesList) GridView gridView; @BindView(R.id.parentLayout) RelativeLayout parentLayout; private boolean hasMoreImages = true; - private boolean isLoading=true; + private boolean isLoading = true; private String categoryName = null; @Inject CategoryImageController controller; @@ -223,7 +223,7 @@ private void handleSuccess(List collection) { if(gridAdapter == null) { setAdapter(collection); } else { - if (gridAdapter.containsAll(collection)){ + if (gridAdapter.containsAll(collection)) { hasMoreImages = false; return; } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 24df6be5fa..f28065eb5e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -186,6 +186,9 @@ public void onSearchImageClicked(int index) { .add(R.id.mediaContainer, mediaDetails) .addToBackStack(null) .commit(); + // Reason for using hide, add instead of replace is to maintain scroll position after + // coming back to the search activity. See https://github.com/commons-app/apps-android-commons/issues/1631 + // https://stackoverflow.com/questions/11353075/how-can-i-maintain-fragment-state-when-added-to-the-back-stack/19022550#19022550 supportFragmentManager.executePendingTransactions(); } mediaDetails.showImage(index);