From 5aba7459bdd71082410b3bb4cf9977d95ebc584e Mon Sep 17 00:00:00 2001 From: Aarnav Jindal Date: Mon, 30 Apr 2018 16:52:50 +0530 Subject: [PATCH 001/134] Fix issue #1332 : Contribution Image auto/manual refresh if fetching fails --- .../commons/contributions/ContributionsActivity.java | 1 - .../contributions/ContributionsListFragment.java | 10 ++++++++++ app/src/main/res/layout/fragment_contributions.xml | 7 ++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java index ad6cff606..312839a24 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java @@ -144,7 +144,6 @@ public class ContributionsActivity if(!BuildConfig.FLAVOR.equalsIgnoreCase("beta")){ setUploadCount(); } - } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index ff400a8dd..2b6c56c43 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -7,6 +7,7 @@ import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.Menu; @@ -47,6 +48,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment { TextView waitingMessage; @BindView(R.id.loadingContributionsProgressBar) ProgressBar progressBar; + @BindView(R.id.swipeRefreshLayout) + SwipeRefreshLayout swipeRefreshLayout; @Inject @Named("prefs") @@ -64,6 +67,13 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment { ButterKnife.bind(this, v); contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity()); + swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + ((ContributionsListAdapter)contributionsList.getAdapter()).notifyDataSetChanged(); + swipeRefreshLayout.setRefreshing(false); + } + }); if (savedInstanceState != null) { Timber.d("Scrolling to %d", savedInstanceState.getInt("grid-position")); contributionsList.setSelection(savedInstanceState.getInt("grid-position")); diff --git a/app/src/main/res/layout/fragment_contributions.xml b/app/src/main/res/layout/fragment_contributions.xml index fa53d9721..d089c7053 100644 --- a/app/src/main/res/layout/fragment_contributions.xml +++ b/app/src/main/res/layout/fragment_contributions.xml @@ -24,6 +24,11 @@ android:id="@+id/loadingContributionsProgressBar" /> + + - + \ No newline at end of file From dcd767351bf45524d8d8c994b47aa0184f615f31 Mon Sep 17 00:00:00 2001 From: Anubhav Date: Tue, 1 May 2018 17:03:59 +0530 Subject: [PATCH 002/134] Deprecation removed --- app/quality.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/quality.gradle b/app/quality.gradle index 7ea20916a..1afdf0d68 100644 --- a/app/quality.gradle +++ b/app/quality.gradle @@ -18,7 +18,7 @@ task checkstyle(type: Checkstyle) { reports { html { enabled true - destination "${project.buildDir}/reports/checkstyle/checkstyle.html" + destination file("${project.buildDir}/reports/checkstyle/checkstyle.html") } } } @@ -36,10 +36,10 @@ task pmd(type: Pmd) { xml.enabled = false html.enabled = true xml { - destination "${project.buildDir}/reports/pmd/pmd.xml" + destination file("${project.buildDir}/reports/pmd/pmd.xml") } html { - destination "${project.buildDir}/reports/pmd/pmd.html" + destination file("${project.buildDir}/reports/pmd/pmd.html") } } } From d3057979d1b657332897f7540a1a170b08c7e800 Mon Sep 17 00:00:00 2001 From: Ashish Date: Fri, 4 May 2018 16:04:14 +0530 Subject: [PATCH 003/134] Implemented butterknife in MediaDetailFragment [issue #1491] --- .../commons/media/MediaDetailFragment.java | 96 ++++++++++++------- 1 file changed, 59 insertions(+), 37 deletions(-) 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 e078a3d0e..29667c026 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 @@ -14,6 +14,9 @@ import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -48,24 +51,35 @@ public class MediaDetailFragment extends Fragment { return mf; } - private MediaWikiImageView image; - private MediaDetailSpacer spacer; private int initialListTop = 0; - private TextView title; - private TextView desc; - private TextView license; - private TextView coordinates; - private TextView uploadedDate; - private LinearLayout categoryContainer; - private ScrollView scrollView; + //Changed the access specifiers of these view as well [ButterKnife wont let us bind private variables] + @BindView(R.id.mediaDetailImage) + MediaWikiImageView image; + @BindView(R.id.mediaDetailSpacer) + MediaDetailSpacer spacer; + @BindView(R.id.mediaDetailTitle) + TextView title; + @BindView(R.id.mediaDetailDesc) + TextView desc; + @BindView(R.id.mediaDetailLicense) + TextView license; + @BindView(R.id.mediaDetailCoordinates) + TextView coordinates; + @BindView(R.id.mediaDetailuploadeddate) + TextView uploadedDate; + @BindView(R.id.mediaDetailCategoryContainer) + LinearLayout categoryContainer; + @BindView(R.id.mediaDetailScrollView) + ScrollView scrollView; + private ArrayList categoryNames; private boolean categoriesLoaded = false; private boolean categoriesPresent = false; private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once! private ViewTreeObserver.OnScrollChangedListener scrollListener; DataSetObserver dataObserver; - private AsyncTask detailFetchTask; + private AsyncTask detailFetchTask; private LicenseList licenseList; @Override @@ -100,17 +114,7 @@ public class MediaDetailFragment extends Fragment { final View view = inflater.inflate(R.layout.fragment_media_detail, container, false); - image = (MediaWikiImageView) view.findViewById(R.id.mediaDetailImage); - scrollView = (ScrollView) view.findViewById(R.id.mediaDetailScrollView); - - // Detail consists of a list view with main pane in header view, plus category list. - spacer = (MediaDetailSpacer) view.findViewById(R.id.mediaDetailSpacer); - title = (TextView) view.findViewById(R.id.mediaDetailTitle); - desc = (TextView) view.findViewById(R.id.mediaDetailDesc); - license = (TextView) view.findViewById(R.id.mediaDetailLicense); - coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates); - uploadedDate = (TextView) view.findViewById(R.id.mediaDetailuploadeddate); - categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer); + ButterKnife.bind(this, view); licenseList = new LicenseList(getActivity()); @@ -271,22 +275,10 @@ public class MediaDetailFragment extends Fragment { private View buildCatLabel(String cat) { final String catName = cat; - final View item = getLayoutInflater(null).inflate(R.layout.detail_category_item, null, false); - final TextView textView = (TextView)item.findViewById(R.id.mediaDetailCategoryItemText); - - textView.setText(cat); - if (categoriesLoaded && categoriesPresent) { - textView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - String selectedCategoryTitle = "Category:" + catName; - Intent viewIntent = new Intent(); - viewIntent.setAction(Intent.ACTION_VIEW); - viewIntent.setData(Utils.uriForWikiPage(selectedCategoryTitle)); - startActivity(viewIntent); - } - }); - } + final View item = getLayoutInflater(null) + .inflate(R.layout.detail_category_item, null, false); + CategoryItemViewHolder categoryItemViewHolder = new CategoryItemViewHolder(item); + categoryItemViewHolder.init(catName); return item; } @@ -343,4 +335,34 @@ public class MediaDetailFragment extends Fragment { private String prettyCoordinates(Media media) { return media.getCoordinates(); } + + /** + * Holds the individual category item views + */ + + public class CategoryItemViewHolder { + + @BindView(R.id.mediaDetailCategoryItemText) + TextView mediaDetailCategoryItemText; + + private String categoryTitle; + + public CategoryItemViewHolder(View item) { + ButterKnife.bind(this, item); + } + + public void init(String categoryName) { + this.categoryTitle = categoryName; + mediaDetailCategoryItemText.setText(categoryName); + } + + @OnClick(R.id.mediaDetailCategoryItemText) + public void onCategoryItemClicked() { + String selectedCategoryTitle = "Category:" + categoryTitle; + Intent viewIntent = new Intent(); + viewIntent.setAction(Intent.ACTION_VIEW); + viewIntent.setData(Utils.uriForWikiPage(selectedCategoryTitle)); + startActivity(viewIntent); + } + } } From f7ad2cbf8f28420958d83e4ec88266b44dbf54c2 Mon Sep 17 00:00:00 2001 From: Ashish Date: Fri, 4 May 2018 19:05:35 +0530 Subject: [PATCH 004/134] Implemented butterknife in MediaDetailPagerFragment [[issue #1491]] --- .../free/nrw/commons/media/MediaDetailPagerFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 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 c7e06cefa..7f061eba2 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 @@ -27,6 +27,8 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import butterknife.BindView; +import butterknife.ButterKnife; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.EventLog; import fr.free.nrw.commons.Media; @@ -35,7 +37,8 @@ import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.contributions.ContributionsActivity; public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPageChangeListener { - private ViewPager pager; + @BindView(R.id.mediaDetailsPager) + ViewPager pager; private Boolean editable; private CommonsApplication app; @@ -86,7 +89,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false); - pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager); + ButterKnife.bind(this,view); pager.addOnPageChangeListener(this); final MediaDetailAdapter adapter = new MediaDetailAdapter(getChildFragmentManager()); From f9989ba1a3dc545f227171cdf6230aefe03cc931 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sat, 5 May 2018 14:03:59 +0530 Subject: [PATCH 005/134] post merge upstream master wip [[issue #1491]] --- .../commons/media/MediaDetailFragment.java | 196 ++++++++++-------- .../media/MediaDetailPagerFragment.java | 7 +- 2 files changed, 110 insertions(+), 93 deletions(-) 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 037f92e56..1bc165704 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 @@ -9,6 +9,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.text.Editable; +import android.text.TextUtils; import android.text.TextWatcher; import android.util.TypedValue; import android.view.LayoutInflater; @@ -22,6 +23,9 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -74,23 +78,37 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; - - private MediaWikiImageView image; - private MediaDetailSpacer spacer; private int initialListTop = 0; - private TextView title; - private TextView desc; - private TextView author; - private TextView license; - private TextView coordinates; - private TextView uploadedDate; - private TextView seeMore; - private LinearLayout nominatedforDeletion; - private LinearLayout categoryContainer; - private LinearLayout authorLayout; - private Button delete; - private ScrollView scrollView; + @BindView(R.id.mediaDetailImage) + MediaWikiImageView image; + @BindView(R.id.mediaDetailSpacer) + MediaDetailSpacer spacer; + @BindView(R.id.mediaDetailTitle) + TextView title; + @BindView(R.id.mediaDetailDesc) + TextView desc; + @BindView(R.id.mediaDetailAuthor) + TextView author; + @BindView(R.id.mediaDetailLicense) + TextView license; + @BindView(R.id.mediaDetailCoordinates) + TextView coordinates; + @BindView(R.id.mediaDetailuploadeddate) + TextView uploadedDate; + @BindView(R.id.seeMore) + TextView seeMore; + @BindView(R.id.nominatedDeletionBanner) + LinearLayout nominatedForDeletion; + @BindView(R.id.mediaDetailCategoryContainer) + LinearLayout categoryContainer; + @BindView(R.id.authorLinearLayout) + LinearLayout authorLayout; + @BindView(R.id.nominateDeletion) + Button delete; + @BindView(R.id.mediaDetailScrollView) + ScrollView scrollView; + private ArrayList categoryNames; private boolean categoriesLoaded = false; private boolean categoriesPresent = false; @@ -100,6 +118,9 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private AsyncTask detailFetchTask; private LicenseList licenseList; + //Had to make this class variable, to implement various onClicks, which access the media, also I fell why make separate variables when one can serve the purpose + private Media media; + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -136,22 +157,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { final View view = inflater.inflate(R.layout.fragment_media_detail, container, false); - image = (MediaWikiImageView) view.findViewById(R.id.mediaDetailImage); - scrollView = (ScrollView) view.findViewById(R.id.mediaDetailScrollView); - - // Detail consists of a list view with main pane in header view, plus category list. - spacer = (MediaDetailSpacer) view.findViewById(R.id.mediaDetailSpacer); - title = (TextView) view.findViewById(R.id.mediaDetailTitle); - desc = (TextView) view.findViewById(R.id.mediaDetailDesc); - author = (TextView) view.findViewById(R.id.mediaDetailAuthor); - license = (TextView) view.findViewById(R.id.mediaDetailLicense); - coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates); - uploadedDate = (TextView) view.findViewById(R.id.mediaDetailuploadeddate); - seeMore = (TextView) view.findViewById(R.id.seeMore); - nominatedforDeletion = (LinearLayout) view.findViewById(R.id.nominatedDeletionBanner); - delete = (Button) view.findViewById(R.id.nominateDeletion); - categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer); - authorLayout = (LinearLayout) view.findViewById(R.id.authorLinearLayout); + ButterKnife.bind(this,view); if (isFeaturedMedia){ authorLayout.setVisibility(View.VISIBLE); @@ -195,7 +201,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { @Override public void onResume() { super.onResume(); - Media media = detailProvider.getMediaAtPosition(index); + media = detailProvider.getMediaAtPosition(index); if (media == null) { // Ask the detail provider to ping us when we're ready Timber.d("MediaDetailFragment not yet ready to display details; registering observer"); @@ -208,17 +214,18 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { Timber.d("MediaDetailFragment ready to display delayed details!"); detailProvider.unregisterDataSetObserver(dataObserver); dataObserver = null; - displayMediaDetails(detailProvider.getMediaAtPosition(index)); + media=detailProvider.getMediaAtPosition(index); + displayMediaDetails(); } }; detailProvider.registerDataSetObserver(dataObserver); } else { Timber.d("MediaDetailFragment ready to display details"); - displayMediaDetails(media); + displayMediaDetails(); } } - private void displayMediaDetails(final Media media) { + private void displayMediaDetails() { //Always load image from Internet to allow viewing the desc, license, and cats image.setMedia(media); @@ -255,7 +262,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (success) { extractor.fill(media); setTextFields(media); - setOnClickListeners(media); } else { Timber.d("Failed to load photo details."); } @@ -309,70 +315,77 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { checkDeletion(media); } - private void setOnClickListeners(final Media media) { - if (licenseLink(media) != null) { - license.setOnClickListener(v -> openWebBrowser(licenseLink(media))); + @OnClick(R.id.mediaDetailLicense) + public void onMediaDetailLicenceClicked(){ + if (!TextUtils.isEmpty(licenseLink(media))) { + openWebBrowser(licenseLink(media)); } else { Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); toast.show(); } + } + + @OnClick(R.id.mediaDetailCoordinates) + public void onMediaDetailCoordinatesClicked(){ if (media.getCoordinates() != null) { - coordinates.setOnClickListener(v -> openMap(media.getCoordinates())); + openMap(media.getCoordinates()); } - if (delete.getVisibility() == View.VISIBLE) { - enableDeleteButton(true); + } - delete.setOnClickListener(v -> { + @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) { + // enableDeleteButton(true); makes sense ? + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setMessage("Why should this file be deleted?"); + final EditText input = new EditText(getActivity()); + alert.setView(input); + input.requestFocus(); + alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String reason = input.getText().toString(); + DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason); + deleteTask.execute(); + enableDeleteButton(false); + } + }); + alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + } + }); + AlertDialog d = alert.create(); + input.addTextChangedListener(new TextWatcher() { + private void handleText() { + final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE); + if (input.getText().length() == 0) { + okButton.setEnabled(false); + } else { + okButton.setEnabled(true); + } + } - AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); - alert.setMessage("Why should this file be deleted?"); - final EditText input = new EditText(getActivity()); - alert.setView(input); - input.requestFocus(); - alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - String reason = input.getText().toString(); - DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason); - deleteTask.execute(); - enableDeleteButton(false); - } - }); - alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - } - }); - AlertDialog d = alert.create(); - input.addTextChangedListener(new TextWatcher() { - private void handleText() { - final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE); - if (input.getText().length() == 0) { - okButton.setEnabled(false); - } else { - okButton.setEnabled(true); - } - } + @Override + public void afterTextChanged(Editable arg0) { + handleText(); + } - @Override - public void afterTextChanged(Editable arg0) { - handleText(); - } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + d.show(); + d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - }); - d.show(); - d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - }); - } - if (nominatedforDeletion.getVisibility() == View.VISIBLE){ - seeMore.setOnClickListener(v -> { - openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); - }); + @OnClick(R.id.seeMore) + public void onSeeMoreClicked(){ + if(nominatedForDeletion.getVisibility()==View.VISIBLE) { + openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); } } @@ -477,11 +490,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private void checkDeletion(Media media){ if (media.getRequestedDeletion()){ delete.setVisibility(View.GONE); - nominatedforDeletion.setVisibility(View.VISIBLE); + nominatedForDeletion.setVisibility(View.VISIBLE); } else{ delete.setVisibility(View.VISIBLE); - nominatedforDeletion.setVisibility(View.GONE); + enableDeleteButton(true);//I believe this is to enable delete button and set its text color resemblance accordingly + nominatedForDeletion.setVisibility(View.GONE); } } 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 c0564c603..62d1261cf 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 @@ -26,6 +26,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; import javax.inject.Inject; import javax.inject.Named; @@ -53,7 +55,8 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple @Named("default_preferences") SharedPreferences prefs; - private ViewPager pager; + @BindView(R.id.mediaDetailsPager) + ViewPager pager; private Boolean editable; private boolean isFeaturedImage; @@ -72,7 +75,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false); - pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager); + ButterKnife.bind(this,view); pager.addOnPageChangeListener(this); final MediaDetailAdapter adapter = new MediaDetailAdapter(getChildFragmentManager()); From f8cb469b48c2f33d1c8cbcb5542dcf5424ac66ae Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 8 May 2018 19:33:21 +1000 Subject: [PATCH 006/134] Create new UploadUtils class --- app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java new file mode 100644 index 000000000..f6b82ba1d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java @@ -0,0 +1,4 @@ +package fr.free.nrw.commons.upload; + +public class UploadUtils { +} From 7f58b21fa0419fe48685b499d8ceec3c0253cde0 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 8 May 2018 19:52:49 +1000 Subject: [PATCH 007/134] Add Javadocs to ShareActivity.java --- .../free/nrw/commons/upload/ShareActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 6a59c8e30..69e68c245 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 @@ -148,9 +148,9 @@ public class ShareActivity private FloatingActionButton mainFab; private boolean isFABOpen = false; - /** * Called when user taps the submit button. + * Requests Storage permission, if needed. */ @Override public void uploadActionInitiated(String title, String description) { @@ -159,8 +159,6 @@ public class ShareActivity this.description = description; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // Check for Storage permission that is required for upload. - // Do not allow user to proceed without permission, otherwise will crash if (needsToRequestStoragePermission()) { requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERM_ON_SUBMIT_STORAGE); @@ -172,16 +170,22 @@ public class ShareActivity } } + /** + * Checks whether storage permissions need to be requested. + * Permissions are needed if the file is not owned by this application, (e.g. shared from the Gallery) + * @return true if file is not owned by this application and permission hasn't been granted beforehand + */ @RequiresApi(16) private boolean needsToRequestStoragePermission() { - // We need to ask storage permission when - // the file is not owned by this application, (e.g. shared from the Gallery) - // and permission is not obtained. return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri) && (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED); } + /** + * Called after permission checks are done. + * Gets file metadata for category suggestions, displays toast, caches categories found, calls uploadController + */ private void uploadBegins() { getFileMetadata(locationPermitted); From b1f777c674fdb773d310761464cc75c5688f55d5 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 8 May 2018 19:54:13 +1000 Subject: [PATCH 008/134] Add TODOs --- app/src/main/AndroidManifest.xml | 1 + .../java/fr/free/nrw/commons/upload/MultipleShareActivity.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17f6770d2..e617b385e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,6 +52,7 @@ + Date: Tue, 8 May 2018 20:21:10 +1000 Subject: [PATCH 009/134] Add TODOs and Javadocs --- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 7 ++++++- .../main/java/fr/free/nrw/commons/upload/UploadUtils.java | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) 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 69e68c245..1a73c6f43 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 @@ -204,6 +204,9 @@ public class ShareActivity }); } + /** + * Starts CategorizationFragment after uploadBegins. + */ private void showPostUpload() { if (categorizationFragment == null) { categorizationFragment = new CategorizationFragment(); @@ -787,6 +790,7 @@ public class ShareActivity return super.onOptionsItemSelected(item); } + //TODO: Move this to a new class. /* * Get SHA1 of file from input stream */ @@ -826,6 +830,7 @@ public class ShareActivity } } + //TODO: Move this to a new class. Save references to the findViewByIds and pass them to the new method /* * function to provide pinch zoom */ @@ -874,7 +879,7 @@ public class ShareActivity expandedImageView.setImageBitmap(scaled); - + // Calculate the starting and ending bounds for the zoomed-in image. // This step involves lots of math. Yay, math. final Rect startBounds = new Rect(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java index f6b82ba1d..0c0703ca6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java @@ -1,4 +1,7 @@ package fr.free.nrw.commons.upload; public class UploadUtils { + + + } From f4cef8da1b83376bbb693a27dc846bb9f6aeb070 Mon Sep 17 00:00:00 2001 From: misaochan Date: Wed, 9 May 2018 02:29:52 +1000 Subject: [PATCH 010/134] Add TODOs --- .../nrw/commons/upload/ShareActivity.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) 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 1a73c6f43..ffc965d90 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 @@ -216,6 +216,10 @@ public class ShareActivity .commit(); } + /** + * Send categories to modifications queue after they are selected + * @param categories categories selected + */ @Override public void onCategoriesSave(List categories) { if (categories.size() > 0) { @@ -253,10 +257,6 @@ public class ShareActivity finish(); } - protected boolean isNearbyUpload() { - return isNearbyUpload; - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -309,8 +309,6 @@ public class ShareActivity } }); - - zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @@ -345,6 +343,7 @@ public class ShareActivity } } + //TODO: We should only use snackbar for location permissions, since storage permissions are MANDATORY // Check storage permissions if marshmallow or newer if (useNewPermissions && (!storagePermitted || !locationPermitted)) { if (!storagePermitted && !locationPermitted) { @@ -404,7 +403,8 @@ public class ShareActivity } }); } - /* + + /** * Function to display the zoom and map FAB */ private void showFABMenu(){ @@ -419,8 +419,8 @@ public class ShareActivity zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); } - /* - * function to close the zoom and map FAB + /** + * Function to close the zoom and map FAB */ private void closeFABMenu(){ isFABOpen=false; @@ -429,7 +429,6 @@ public class ShareActivity zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { - } @Override @@ -438,21 +437,25 @@ public class ShareActivity maps_fragment.setVisibility(View.GONE); zoomInButton.setVisibility(View.GONE); } - } @Override public void onAnimationCancel(Animator animator) { - } @Override public void onAnimationRepeat(Animator animator) { - } }); } + /** + * Checks if upload was initiated via Nearby + * @return true if upload was initiated via Nearby + */ + protected boolean isNearbyUpload() { + return isNearbyUpload; + } @Override public void onRequestPermissionsResult(int requestCode, From 2d1f166ac77a21a0f2cf96c203f2359fe4927213 Mon Sep 17 00:00:00 2001 From: Ashish Date: Wed, 9 May 2018 17:57:59 +0530 Subject: [PATCH 011/134] Implemented butterknife in MultipleUploadListFragment [issue #1491] --- .../commons/upload/MultipleUploadListFragment.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java index 4390bcef4..089f1ac46 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java @@ -26,6 +26,8 @@ import android.widget.GridView; import android.widget.RelativeLayout; import android.widget.TextView; +import butterknife.BindView; +import butterknife.ButterKnife; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.view.SimpleDraweeView; @@ -41,9 +43,13 @@ public class MultipleUploadListFragment extends Fragment { void OnMultipleUploadInitiated(); } - private GridView photosGrid; + @BindView(R.id.multipleShareBackground) + GridView photosGrid; + + @BindView(R.id.multipleBaseTitle) + EditText baseTitle; + private PhotoDisplayAdapter photosAdapter; - private EditText baseTitle; private TitleTextWatcher textWatcher = new TitleTextWatcher(); private Point photoSize; @@ -166,9 +172,7 @@ public class MultipleUploadListFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_multiple_uploads_list, container, false); - photosGrid = view.findViewById(R.id.multipleShareBackground); - baseTitle = view.findViewById(R.id.multipleBaseTitle); - + ButterKnife.bind(this,view); photosAdapter = new PhotoDisplayAdapter(); photosGrid.setAdapter(photosAdapter); photosGrid.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity()); From 8f76dd0b61e0769d2e7831f7a7272967c80fa84e Mon Sep 17 00:00:00 2001 From: Ashish Date: Thu, 10 May 2018 13:31:51 +0530 Subject: [PATCH 012/134] Implemented butterknife in ShareActivity [issue #1491] --- .../nrw/commons/upload/ShareActivity.java | 236 +++++++++--------- 1 file changed, 115 insertions(+), 121 deletions(-) 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 6a59c8e30..cfcec1da5 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 @@ -8,7 +8,6 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -32,16 +31,16 @@ import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.BitmapCompat; -import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.MenuItem; import android.view.View; -import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; -import android.view.inputmethod.InputMethodManager; +import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; +import butterknife.BindView; +import butterknife.OnClick; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.view.SimpleDraweeView; import com.github.chrisbanes.photoview.PhotoView; @@ -69,22 +68,18 @@ import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.category.CategorizationFragment; import fr.free.nrw.commons.category.OnCategoriesSaveHandler; import fr.free.nrw.commons.contributions.Contribution; -import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.modifications.CategoryModifier; import fr.free.nrw.commons.modifications.ModificationsContentProvider; 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.utils.ImageUtils; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; -import android.support.design.widget.FloatingActionButton; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; -import static java.lang.Long.min; /** * Activity for the title/desc screen after image is selected. Also starts processing image @@ -95,6 +90,22 @@ public class ShareActivity implements SingleUploadFragment.OnUploadActionInitiated, OnCategoriesSaveHandler,SimilarImageDialogFragment.onResponse { + @BindView(R.id.container) + FrameLayout flContainer; + @BindView(R.id.backgroundImage) + SimpleDraweeView backgroundImageView; + @BindView(R.id.media_map) + FloatingActionButton mapsFragment; //Lets stick to camelCase + @BindView(R.id.media_upload_zoom_in) + FloatingActionButton zoomInButton; + @BindView(R.id.media_upload_zoom_out) + FloatingActionButton zoomOutButton; + @BindView(R.id.main_fab) + FloatingActionButton mainFab; + @BindView(R.id.expanded_image) + PhotoView expandedImageView; + + private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; @@ -120,9 +131,6 @@ public class ShareActivity private Uri mediaUri; private Contribution contribution; - private SimpleDraweeView backgroundImageView; - private FloatingActionButton maps_fragment; - private boolean cacheFound; private GPSExtractor imageObj; @@ -143,11 +151,14 @@ public class ShareActivity private Animator CurrentAnimator; private long ShortAnimationDuration; - private FloatingActionButton zoomInButton; - private FloatingActionButton zoomOutButton; - private FloatingActionButton mainFab; private boolean isFABOpen = false; + //Had to make them class variables, to extract out the click listeners, also I see no harm in this + final Rect startBounds = new Rect(); + final Rect finalBounds = new Rect(); + final Point globalOffset = new Point(); + private float startScaleFinal; + /** * Called when user taps the submit button. @@ -257,7 +268,6 @@ public class ShareActivity setContentView(R.layout.activity_share); ButterKnife.bind(this); initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder .newInstance(getResources()) .setPlaceholderImage(VectorDrawableCompat.create(getResources(), @@ -286,37 +296,6 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } - - mainFab = (FloatingActionButton) findViewById(R.id.main_fab); - /* - * called when upper arrow floating button - */ - mainFab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(!isFABOpen){ - showFABMenu(); - }else{ - closeFABMenu(); - } - } - }); - - - - zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); - try { - zoomInButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - zoomImageFromThumb(backgroundImageView, mediaUri); - } - }); - } catch (Exception e){ - Log.i("exception", e.toString()); - } - zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); - if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); } @@ -378,25 +357,13 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); - maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); - maps_fragment.setVisibility(View.VISIBLE); + mapsFragment.setVisibility(View.VISIBLE); if( imageObj == null || imageObj.imageCoordsExists != true){ - maps_fragment.setVisibility(View.INVISIBLE); + mapsFragment.setVisibility(View.INVISIBLE); } - - maps_fragment.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if( imageObj != null && imageObj.imageCoordsExists == true) { - Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); - Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); - mapIntent.setPackage("com.google.android.apps.maps"); - startActivity(mapIntent); - } - } - }); } + /* * Function to display the zoom and map FAB */ @@ -404,11 +371,11 @@ public class ShareActivity isFABOpen=true; if( imageObj != null && imageObj.imageCoordsExists == true) - maps_fragment.setVisibility(View.VISIBLE); + mapsFragment.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); mainFab.animate().rotationBy(180); - maps_fragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); + mapsFragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); } @@ -418,7 +385,7 @@ public class ShareActivity private void closeFABMenu(){ isFABOpen=false; mainFab.animate().rotationBy(-180); - maps_fragment.animate().translationY(0); + mapsFragment.animate().translationY(0); zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { @@ -428,7 +395,7 @@ public class ShareActivity @Override public void onAnimationEnd(Animator animator) { if(!isFABOpen){ - maps_fragment.setVisibility(View.GONE); + mapsFragment.setVisibility(View.GONE); zoomInButton.setVisibility(View.GONE); } @@ -700,8 +667,9 @@ public class ShareActivity return; } + //I might not be supposed to change it, but still, I saw it @Override - public void onPostiveResponse() { + public void onPositiveResponse() { imageObj = tempImageObj; decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data Timber.d("EXIF from tempImageObj"); @@ -865,26 +833,19 @@ public class ShareActivity scaled = bitmap; } // Load the high-resolution "zoomed-in" image. - PhotoView expandedImageView = (PhotoView) findViewById( - R.id.expanded_image); expandedImageView.setImageBitmap(scaled); - + // Calculate the starting and ending bounds for the zoomed-in image. // This step involves lots of math. Yay, math. - final Rect startBounds = new Rect(); - final Rect finalBounds = new Rect(); - final Point globalOffset = new Point(); - // The start bounds are the global visible rectangle of the thumbnail, // and the final bounds are the global visible rectangle of the container // view. Also set the container view's offset as the origin for the // bounds, since that's the origin for the positioning animation // properties (X, Y). thumbView.getGlobalVisibleRect(startBounds); - findViewById(R.id.container) - .getGlobalVisibleRect(finalBounds, globalOffset); + flContainer.getGlobalVisibleRect(finalBounds, globalOffset); startBounds.offset(-globalOffset.x, -globalOffset.y); finalBounds.offset(-globalOffset.x, -globalOffset.y); @@ -955,53 +916,86 @@ public class ShareActivity // Upon clicking the zoomed-in image, it should zoom back down // to the original bounds and show the thumbnail instead of // the expanded image. - final float startScaleFinal = startScale; - zoomOutButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (CurrentAnimator != null) { - CurrentAnimator.cancel(); - } - zoomOutButton.setVisibility(View.GONE); - mainFab.setVisibility(View.VISIBLE); + startScaleFinal = startScale; - // Animate the four positioning/sizing properties in parallel, - // back to their original values. - AnimatorSet set = new AnimatorSet(); - set.play(ObjectAnimator - .ofFloat(expandedImageView, View.X, startBounds.left)) - .with(ObjectAnimator - .ofFloat(expandedImageView, - View.Y,startBounds.top)) - .with(ObjectAnimator - .ofFloat(expandedImageView, - View.SCALE_X, startScaleFinal)) - .with(ObjectAnimator - .ofFloat(expandedImageView, - View.SCALE_Y, startScaleFinal)); - set.setDuration(ShortAnimationDuration); - set.setInterpolator(new DecelerateInterpolator()); - set.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - thumbView.setAlpha(1f); - expandedImageView.setVisibility(View.GONE); - CurrentAnimator = null; - } - - @Override - public void onAnimationCancel(Animator animation) { - thumbView.setAlpha(1f); - expandedImageView.setVisibility(View.GONE); - CurrentAnimator = null; - } - }); - set.start(); - CurrentAnimator = set; - - } - - }); } + /* + * called when upper arrow floating button + */ + @OnClick(R.id.main_fab) + public void onMainFabClicked() { + if (!isFABOpen) { + showFABMenu(); + } else { + closeFABMenu(); + } + } + + @OnClick(R.id.media_upload_zoom_in) + public void onZoomInFabClicked() { + //This try catch block was originally holding the entire click listener on the fab button, I did not wanted to risk exceptions + try { + zoomImageFromThumb(backgroundImageView, mediaUri); + } catch (Exception e) { + Log.i("exception", e.toString()); + } + } + + @OnClick(R.id.media_upload_zoom_out) + public void onZoomOutFabClicked() { + if (CurrentAnimator != null) { + CurrentAnimator.cancel(); + } + zoomOutButton.setVisibility(View.GONE); + mainFab.setVisibility(View.VISIBLE); + + // Animate the four positioning/sizing properties in parallel, + // back to their original values. + AnimatorSet set = new AnimatorSet(); + set.play(ObjectAnimator + .ofFloat(expandedImageView, View.X, startBounds.left)) + .with(ObjectAnimator + .ofFloat(expandedImageView, + View.Y, startBounds.top)) + .with(ObjectAnimator + .ofFloat(expandedImageView, + View.SCALE_X, startScaleFinal)) + .with(ObjectAnimator + .ofFloat(expandedImageView, + View.SCALE_Y, startScaleFinal)); + set.setDuration(ShortAnimationDuration); + set.setInterpolator(new DecelerateInterpolator()); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + //background image view is thumbView + backgroundImageView.setAlpha(1f); + expandedImageView.setVisibility(View.GONE); + CurrentAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + //background image view is thumbView + backgroundImageView.setAlpha(1f); + expandedImageView.setVisibility(View.GONE); + CurrentAnimator = null; + } + }); + set.start(); + CurrentAnimator = set; + } + + @OnClick(R.id.media_map) + public void onFabShowMapsClicked() { + if (imageObj != null && imageObj.imageCoordsExists == true) { + Uri gmmIntentUri = Uri + .parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj + .getDecLongitude()); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); + mapIntent.setPackage("com.google.android.apps.maps"); + startActivity(mapIntent); + } + } } From 66b275b19d52a32c99ea8cab95cd51258044706d Mon Sep 17 00:00:00 2001 From: Ashish Date: Thu, 10 May 2018 14:16:08 +0530 Subject: [PATCH 013/134] Implemented butterknife in SimilarImageFragment [issue #1491] --- .../upload/SimilarImageDialogFragment.java | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/SimilarImageDialogFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/SimilarImageDialogFragment.java index a8f336927..59b8a1223 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/SimilarImageDialogFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/SimilarImageDialogFragment.java @@ -13,6 +13,9 @@ import android.view.ViewGroup; import android.view.Window; import android.widget.Button; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.imagepipeline.listener.RequestListener; @@ -29,29 +32,33 @@ import fr.free.nrw.commons.R; */ public class SimilarImageDialogFragment extends DialogFragment { + + @BindView(R.id.orginalImage) SimpleDraweeView originalImage; + @BindView(R.id.possibleImage) SimpleDraweeView possibleImage; + @BindView(R.id.postive_button) Button positiveButton; + @BindView(R.id.negative_button) Button negativeButton; onResponse mOnResponse;//Implemented interface from shareActivity Boolean gotResponse = false; + public SimilarImageDialogFragment() { } public interface onResponse{ - public void onPostiveResponse(); + public void onPositiveResponse(); + public void onNegativeResponse(); } + @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_similar_image_dialog, container, false); + ButterKnife.bind(this,view); Set requestListeners = new HashSet<>(); requestListeners.add(new RequestLoggingListener()); - originalImage =(SimpleDraweeView) view.findViewById(R.id.orginalImage); - possibleImage =(SimpleDraweeView) view.findViewById(R.id.possibleImage); - positiveButton = (Button) view.findViewById(R.id.postive_button); - negativeButton = (Button) view.findViewById(R.id.negative_button); - originalImage.setHierarchy(GenericDraweeHierarchyBuilder .newInstance(getResources()) .setPlaceholderImage(VectorDrawableCompat.create(getResources(), @@ -70,22 +77,6 @@ public class SimilarImageDialogFragment extends DialogFragment { originalImage.setImageURI(Uri.fromFile(new File(getArguments().getString("originalImagePath")))); possibleImage.setImageURI(Uri.fromFile(new File(getArguments().getString("possibleImagePath")))); - negativeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mOnResponse.onNegativeResponse(); - gotResponse = true; - dismiss(); - } - }); - positiveButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mOnResponse.onPostiveResponse(); - gotResponse = true; - dismiss(); - } - }); return view; } @@ -105,8 +96,23 @@ public class SimilarImageDialogFragment extends DialogFragment { @Override public void onDismiss(DialogInterface dialog) { // I user dismisses dialog by pressing outside the dialog. - if(!gotResponse) + if (!gotResponse) { mOnResponse.onNegativeResponse(); + } super.onDismiss(dialog); } + + @OnClick(R.id.negative_button) + public void onNegativeButtonClicked() { + mOnResponse.onNegativeResponse(); + gotResponse = true; + dismiss(); + } + + @OnClick(R.id.postive_button) + public void onPositiveButtonClicked() { + mOnResponse.onPositiveResponse(); + gotResponse = true; + dismiss(); + } } From b6e4fb2d68673463840f1f7d02471d83dd1d66e9 Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Sat, 12 May 2018 16:49:43 +0530 Subject: [PATCH 014/134] Bug fix #1504 (#1506) * Bug fix #1504 * Filtered messages with ConnectException [issue #1504] * A generalised message for exceptions in Nearby Activity [issue #1504] --- .../nrw/commons/nearby/NearbyActivity.java | 29 ++++++++++++++++--- .../nrw/commons/nearby/NearbyController.java | 3 +- .../free/nrw/commons/nearby/NearbyPlaces.java | 10 +------ app/src/main/res/values/strings.xml | 1 + 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 3bf8beacf..04886fda4 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -16,6 +16,7 @@ import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; +import android.text.TextUtils; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -23,9 +24,14 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.ProgressBar; +import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.reactivex.functions.Consumer; +import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; import java.util.List; import javax.inject.Inject; @@ -427,8 +433,14 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp .loadAttractionsFromLocation(curLatLng)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::populatePlaces); - } else if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { + .subscribe(this::populatePlaces, + throwable -> { + Timber.d(throwable); + showErrorMessage(getString(R.string.error_fetching_nearby_places)); + progressBar.setVisibility(View.GONE); + }); + } else if (locationChangeType + .equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { Gson gson = new GsonBuilder() .registerTypeAdapter(Uri.class, new UriSerializer()) .create(); @@ -451,7 +463,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp if (placeList.size() == 0) { ViewUtil.showSnackbar(findViewById(R.id.container), R.string.no_nearby); } - + bundle.putString("PlaceList", gsonPlaceList); //bundle.putString("CurLatLng", gsonCurLatLng); bundle.putString("BoundaryCoord", gsonBoundaryCoordinates); @@ -580,7 +592,12 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp .loadAttractionsFromLocation(curLatLng)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::populatePlaces); + .subscribe(this::populatePlaces, + throwable -> { + Timber.d(throwable); + showErrorMessage(getString(R.string.error_fetching_nearby_places)); + progressBar.setVisibility(View.GONE); + }); nearbyMapFragment.setBundleForUpdtes(bundle); nearbyMapFragment.updateMapSignificantly(); updateListFragment(); @@ -646,4 +663,8 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp public void prepareViewsForSheetPosition(int bottomSheetState) { // TODO } + + private void showErrorMessage(String message) { + ViewUtil.showLongToast(NearbyActivity.this, message); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java index 015d22135..bd042b4d7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java @@ -7,6 +7,7 @@ import android.support.graphics.drawable.VectorDrawableCompat; import com.mapbox.mapboxsdk.annotations.IconFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -44,7 +45,7 @@ public class NearbyController { * @return NearbyPlacesInfo a variable holds Place list without distance information * and boundary coordinates of current Place List */ - public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) { + public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) throws IOException { Timber.d("Loading attractions near %s", curLatLng); NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index a2f4b2352..d05d81251 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -40,10 +40,9 @@ public class NearbyPlaces { } } - List getFromWikidataQuery(LatLng curLatLng, String lang) { + List getFromWikidataQuery(LatLng curLatLng, String lang) throws IOException { List places = Collections.emptyList(); - try { // increase the radius gradually to find a satisfactory number of nearby places while (radius <= MAX_RADIUS) { places = getFromWikidataQuery(curLatLng, lang, radius); @@ -54,13 +53,6 @@ public class NearbyPlaces { radius *= RADIUS_MULTIPLIER; } } - } catch (IOException e) { - Timber.d(e.toString()); - // errors tend to be caused by too many results (and time out) - // try a small radius next time - Timber.d("back to initial radius: %f", radius); - radius = INITIAL_RADIUS; - } // make sure we will be able to send at least one request next time if (radius > MAX_RADIUS) { radius = MAX_RADIUS; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dcea51bcc..6e30baa10 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -282,5 +282,6 @@ Share App Coordinates were not specified during image selection + Error fetching nearby places. From 93b0db9ecdacbb03ccf362f7262c9015fdecfcbd Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 14 May 2018 08:13:34 +0200 Subject: [PATCH 015/134] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-bn/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-iw/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 1 + app/src/main/res/values-pms/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-qq/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 9 +++++++++ app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + 15 files changed, 23 insertions(+) diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index e7167393c..7c76ebaea 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -262,4 +262,5 @@ বুঝেছি! কোন চিত্র পাওয়া যায়নি! আপলোড করেছেন: %1$s + কাছাকাছি স্থানগুলি আনতে ত্রুটি। diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5f751153c..9f47ce42e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -278,4 +278,5 @@ Hochgeladen von: %1$s App teilen Während der Bildauswahl wurden keine Koordinaten angegeben + Fehler beim Abrufen der Orte in der Nähe. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ee50aa3d7..e372df836 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -282,4 +282,5 @@ Ανέβηκε από: %1$s Κοινοποίηση εφαρμογής Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας + Σφάλμα κατά την εύρεση κοντινών μερών. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 69cf2f8b7..9e5dae650 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -275,4 +275,5 @@ Cargada por: %1$s Compartir aplicación No se especificaron las coordenadas al seleccionar la imagen + Error al recuperar los lugares cercanos. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6dd08a30d..bfb14baf1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -289,4 +289,5 @@ Importé par:%1$s Partager les applications Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image + Erreur durant l\'exploration du voisinage. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index f0cdf1f3d..9ba67e4ee 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -282,4 +282,5 @@ הועלתה על־ידי: %1$s שיתוף היישום לא צוינו קואורדינטות בעת בחירת התמונה + שגיאה באחזור המקומות בסביבתך. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e80999d54..883665c85 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -275,4 +275,5 @@ 올린이: %1$s 앱 공유 그림 선택 중에 좌표가 지정되지 않았습니다 + 주변 장소를 가져오는데 오류가 있습니다. diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 492e1bc0c..a344fcad2 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -272,4 +272,5 @@ Подигач: %1$s Сподели прилог Не беа укажани координати при изборот на сликата + Грешка при добивањето на околните места. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 53ce91cc5..8b2bd292c 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -272,4 +272,5 @@ Carià da: %1$s Partagé j\'aplicassion Le coordinà a son nen ëstàite spessificà durant la selession ëd la plancia + Eror durant l\'esplorassion dj\'anviron. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 23b5e4abf..849c4e6b1 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -284,4 +284,5 @@ Carregada por: %1$s Compartilhar o aplicativo Não foram especificadas coordenadas durante a seleção da imagem + Erro ao buscar lugares próximos. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 3da1296b7..e59582fa0 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -283,4 +283,5 @@ Carregada por: %1$s Partilhar aplicação Não foram especificadas coordenadas durante a seleção da imagem + Erro ao localizar locais próximos. diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index 37c2978bf..d84ce08ab 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -99,6 +99,7 @@ Message explaining what kind of images not to submit. Message asking user if they understand what kinds of images to upload. Button text for confirming the user understands what kinds of images to upload.\n{{Identical|Yes}} + \'\'This message is empty, and it\'s probably invalid. See bug report: https://github.com/commons-app/apps-android-commons/issues/1333 .\'\' Label for categories list in media detail panel.\n{{Identical|Category}} Placeholder for categories list in media detail panel, while loading from network.\n{{Identical|Loading}} Placeholder for categories list in media detail panel, if no categories found.\n{{Identical|None selected}} diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 95548b662..dadd40917 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -271,4 +271,13 @@ İlerle Vazgeç Tekrar Deneyin + Anladım! + Vikipedi maddelerine eklemek için fotoğrafa ihtiyaç duyan size yakın yerler + Bu tuşa dokunmak bu yerlerin bir listesini getirir + Galerinizden veya kameranızla herhangi bir yer için resim yükleyebilirsiniz. + Resim bulunamadı! + Resimler yüklenirken hata oluştu. + Yükleyen: %1$s + Uygulamayı Paylaş + Koordinatlar görüntü seçimi sırasında belirlenmedi diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 80b595f5d..4581d45ce 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -280,4 +280,5 @@ 由:%1$s 上傳 分享應用程式 當選擇圖片時未指定座標 + 索取附近地點時出錯。 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 29a1ae898..2079eb925 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -278,4 +278,5 @@ 由%1$s上传 分享应用 图片选择时,坐标并未指定 + 检索附近地点时出错。 From d891b8f31032fcde2e274732b69a450b3dbb0a38 Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Tue, 15 May 2018 12:55:37 +0530 Subject: [PATCH 016/134] Fix security exception crash while accessing network location provider (#1498) * Fix security exception crash while accessing network location provider * Added java docs --- .../location/LocationServiceManager.java | 33 ++++++++++++---- .../nrw/commons/nearby/NearbyActivity.java | 39 +++++++++++++++++-- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java index 73ded852f..49c422633 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.location; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; @@ -10,9 +11,10 @@ import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.util.Log; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import timber.log.Timber; @@ -29,6 +31,7 @@ public class LocationServiceManager implements LocationListener { private Location lastLocation; private final List locationListeners = new CopyOnWriteArrayList<>(); private boolean isLocationManagerRegistered = false; + private Set locationExplanationDisplayed = new HashSet<>(); /** * Constructs a new instance of LocationServiceManager. @@ -51,7 +54,6 @@ public class LocationServiceManager implements LocationListener { /** * Returns whether the location permission is granted. - * * @return true if the location permission is granted */ public boolean isLocationPermissionGranted() { @@ -73,10 +75,23 @@ public class LocationServiceManager implements LocationListener { LOCATION_REQUEST); } + /** + * The permission explanation dialog box is now displayed just once for a particular activity. We are subscribing + * to updates from multiple providers so its important to show the dialog just once. Otherwise it will be displayed + * once for every provider, which in our case currently is 2. + * @param activity + * @return + */ public boolean isPermissionExplanationRequired(Activity activity) { - return !activity.isFinishing() && - ActivityCompat.shouldShowRequestPermissionRationale(activity, - Manifest.permission.ACCESS_FINE_LOCATION); + if (activity.isFinishing()) { + return false; + } + boolean showRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION); + if (showRequestPermissionRationale && !locationExplanationDisplayed.contains(activity)) { + locationExplanationDisplayed.add(activity); + return true; + } + return false; } /** @@ -84,8 +99,9 @@ public class LocationServiceManager implements LocationListener { * (e.g. when Location permission just granted) * @return last known LatLng */ + @SuppressLint("MissingPermission") public LatLng getLKL() { - if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + if (isLocationPermissionGranted()) { Location lastKL = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (lastKL == null) { lastKL = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); @@ -107,9 +123,10 @@ public class LocationServiceManager implements LocationListener { * Registers a LocationManager to listen for current location. */ public void registerLocationManager() { - if (!isLocationManagerRegistered) + if (!isLocationManagerRegistered) { isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) && requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); + } } /** @@ -142,7 +159,7 @@ public class LocationServiceManager implements LocationListener { * @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly * LOCATION_SLIGHTLY_CHANGED if location changed slightly */ - protected LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { + private LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { if (currentBestLocation == null) { // A new location is always better than no location diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 04886fda4..35e15b0d9 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -322,7 +322,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp protected void onStart() { super.onStart(); locationManager.addLocationListener(this); - locationManager.registerLocationManager(); + registerLocationUpdates(); } @Override @@ -400,7 +400,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp return; } - locationManager.registerLocationManager(); + registerLocationUpdates(); LatLng lastLocation = locationManager.getLastLocation(); if (curLatLng != null && curLatLng.equals(lastLocation)) { //refresh view only if location has changed @@ -450,6 +450,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp } } + /** + * This method first checks if the location permissions has been granted and then register the location manager for updates. + */ + private void registerLocationUpdates() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (locationManager.isLocationPermissionGranted()) { + locationManager.registerLocationManager(); + } else { + // Should we show an explanation? + if (locationManager.isPermissionExplanationRequired(this)) { + new AlertDialog.Builder(this) + .setMessage(getString(R.string.location_permission_rationale_nearby)) + .setPositiveButton("OK", (dialog, which) -> { + requestLocationPermissions(); + dialog.dismiss(); + }) + .setNegativeButton("Cancel", (dialog, id) -> { + showLocationPermissionDeniedErrorDialog(); + dialog.cancel(); + }) + .create() + .show(); + + } else { + // No explanation needed, we can request the permission. + requestLocationPermissions(); + } + } + } else { + locationManager.registerLocationManager(); + } + } + private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) { List placeList = nearbyPlacesInfo.placeList; LatLng[] boundaryCoordinates = nearbyPlacesInfo.boundaryCoordinates; @@ -530,7 +563,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp locationManager.removeLocationListener(this); } else { lockNearbyView = false; - locationManager.registerLocationManager(); + registerLocationUpdates(); locationManager.addLocationListener(this); } } From 625f58259889378790e12c9d13dc94908e0b0dee Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 19:52:21 +1000 Subject: [PATCH 017/134] Remove MultipleShareActivity from manifest and send SEND_MULTIPLE intent to ShareActivity as well --- app/src/main/AndroidManifest.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e617b385e..aaba6abc8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,18 +46,6 @@ android:label="@string/app_name"> - - - - - - - - - From 66245d25a5c6b1a9b94ec0093da71fe147e716e8 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 19:56:47 +1000 Subject: [PATCH 018/134] Remove check for Storage permissions and snackbar in onCreate() --- .../nrw/commons/upload/ShareActivity.java | 37 +++---------------- 1 file changed, 5 insertions(+), 32 deletions(-) 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 ffc965d90..27cb3d4f4 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 @@ -334,10 +334,6 @@ public class ShareActivity useNewPermissions = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { useNewPermissions = true; - - if (!needsToRequestStoragePermission()) { - storagePermitted = true; - } if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { locationPermitted = true; } @@ -345,35 +341,14 @@ public class ShareActivity //TODO: We should only use snackbar for location permissions, since storage permissions are MANDATORY // Check storage permissions if marshmallow or newer - if (useNewPermissions && (!storagePermitted || !locationPermitted)) { - if (!storagePermitted && !locationPermitted) { - String permissionRationales = - getResources().getString(R.string.read_storage_permission_rationale) + "\n" - + getResources().getString(R.string.location_permission_rationale); - snackbar = requestPermissionUsingSnackBar( - permissionRationales, - new String[]{ - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION); - View snackbarView = snackbar.getView(); - TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); - textView.setMaxLines(3); - } else if (!storagePermitted) { - requestPermissionUsingSnackBar( - getString(R.string.read_storage_permission_rationale), - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, - REQUEST_PERM_ON_CREATE_STORAGE); - } else if (!locationPermitted) { - requestPermissionUsingSnackBar( - getString(R.string.location_permission_rationale), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_LOCATION); - } + if (!locationPermitted) { + requestPermissionUsingSnackBar( + getString(R.string.location_permission_rationale), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERM_ON_CREATE_LOCATION); } performPreUploadProcessingOfFile(); - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); if (shareView == null && categorizationFragment == null) { @@ -389,8 +364,6 @@ public class ShareActivity if( imageObj == null || imageObj.imageCoordsExists != true){ maps_fragment.setVisibility(View.INVISIBLE); } - - maps_fragment.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { From c0c74f68d2211c6bf8939d9e61115c2f66282bd1 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 19:57:22 +1000 Subject: [PATCH 019/134] Clear lint warnings --- .../main/java/fr/free/nrw/commons/upload/ShareActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 27cb3d4f4..b21f510b1 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 @@ -361,13 +361,13 @@ public class ShareActivity uploadController.prepareService(); maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); maps_fragment.setVisibility(View.VISIBLE); - if( imageObj == null || imageObj.imageCoordsExists != true){ + if( imageObj == null || imageObj.imageCoordsExists){ maps_fragment.setVisibility(View.INVISIBLE); } maps_fragment.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if( imageObj != null && imageObj.imageCoordsExists == true) { + if( imageObj != null && imageObj.imageCoordsExists) { Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); From aa731659645d6592a9f2ad109824befe1e6295f6 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:03:46 +1000 Subject: [PATCH 020/134] Create new receiveIntent() method to tidy onCreate --- .../nrw/commons/upload/ShareActivity.java | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) 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 b21f510b1..918fe68c7 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 @@ -257,23 +257,10 @@ public class ShareActivity finish(); } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_share); - ButterKnife.bind(this); - initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); - backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder - .newInstance(getResources()) - .setPlaceholderImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_image_black_24dp, getTheme())) - .setFailureImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_error_outline_black_24dp, getTheme())) - .build()); - - //Receive intent from ContributionController.java when user selects picture to upload + /** + * Receive intent from ContributionController.java when user selects picture to upload + */ + private void receiveIntent() { Intent intent = getIntent(); if (Intent.ACTION_SEND.equals(intent.getAction())) { @@ -293,6 +280,25 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_share); + ButterKnife.bind(this); + initBack(); + backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); + backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setPlaceholderImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_image_black_24dp, getTheme())) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .build()); + + receiveIntent(); mainFab = (FloatingActionButton) findViewById(R.id.main_fab); /* @@ -339,8 +345,7 @@ public class ShareActivity } } - //TODO: We should only use snackbar for location permissions, since storage permissions are MANDATORY - // Check storage permissions if marshmallow or newer + // Check location permissions if M or newer for category suggestions, request via snackbar if not present if (!locationPermitted) { requestPermissionUsingSnackBar( getString(R.string.location_permission_rationale), From 11d3517b70f402bf314add72a03bbc6e4804a271 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:07:34 +1000 Subject: [PATCH 021/134] Create initViewsAndListeners() method to tidy onCreate --- .../nrw/commons/upload/ShareActivity.java | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) 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 918fe68c7..3da0ef8fe 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 @@ -282,28 +282,12 @@ public class ShareActivity } } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_share); - ButterKnife.bind(this); - initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); - backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder - .newInstance(getResources()) - .setPlaceholderImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_image_black_24dp, getTheme())) - .setFailureImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_error_outline_black_24dp, getTheme())) - .build()); - - receiveIntent(); - + /** + * Initialize views and setup listeners here for FAB to prevent cluttering onCreate + */ + private void initViewsAndListeners() { mainFab = (FloatingActionButton) findViewById(R.id.main_fab); - /* - * called when upper arrow floating button - */ + //called when upper arrow floating button mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -327,6 +311,26 @@ public class ShareActivity Log.i("exception", e.toString()); } zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_share); + ButterKnife.bind(this); + initBack(); + backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); + backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setPlaceholderImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_image_black_24dp, getTheme())) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .build()); + + receiveIntent(); + initViewsAndListeners(); if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); From 3bd421424685070fcd061f3e730900476f390a18 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:09:22 +1000 Subject: [PATCH 022/134] More FAB tidying --- .../nrw/commons/upload/ShareActivity.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) 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 3da0ef8fe..a9c5c1260 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 @@ -291,9 +291,9 @@ public class ShareActivity mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if(!isFABOpen){ + if(!isFABOpen) { showFABMenu(); - }else{ + } else { closeFABMenu(); } } @@ -307,10 +307,27 @@ public class ShareActivity zoomImageFromThumb(backgroundImageView, mediaUri); } }); - } catch (Exception e){ + } catch (Exception e) { Log.i("exception", e.toString()); } zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); + + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + maps_fragment.setVisibility(View.VISIBLE); + if( imageObj == null || imageObj.imageCoordsExists){ + maps_fragment.setVisibility(View.INVISIBLE); + } + maps_fragment.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( imageObj != null && imageObj.imageCoordsExists) { + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); + mapIntent.setPackage("com.google.android.apps.maps"); + startActivity(mapIntent); + } + } + }); } @Override @@ -368,22 +385,6 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); - maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); - maps_fragment.setVisibility(View.VISIBLE); - if( imageObj == null || imageObj.imageCoordsExists){ - maps_fragment.setVisibility(View.INVISIBLE); - } - maps_fragment.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if( imageObj != null && imageObj.imageCoordsExists) { - Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); - Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); - mapIntent.setPackage("com.google.android.apps.maps"); - startActivity(mapIntent); - } - } - }); } /** From 478c4900dfd5017c8ce6a397de5be55574e49375 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:13:03 +1000 Subject: [PATCH 023/134] Tidy initViewsAndListeners() --- .../fr/free/nrw/commons/upload/ShareActivity.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 a9c5c1260..9849387b8 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 @@ -286,8 +286,12 @@ public class ShareActivity * Initialize views and setup listeners here for FAB to prevent cluttering onCreate */ private void initViewsAndListeners() { + //Main FAB splits into Zoom and Map mainFab = (FloatingActionButton) findViewById(R.id.main_fab); - //called when upper arrow floating button + zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); + zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -299,7 +303,6 @@ public class ShareActivity } }); - zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @Override @@ -308,11 +311,9 @@ public class ShareActivity } }); } catch (Exception e) { - Log.i("exception", e.toString()); + Timber.e(e); } - zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); - maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); maps_fragment.setVisibility(View.VISIBLE); if( imageObj == null || imageObj.imageCoordsExists){ maps_fragment.setVisibility(View.INVISIBLE); From c22d4ce07136006f15b545beb4af13e75daa44c0 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:13:59 +1000 Subject: [PATCH 024/134] Shift helper methods to more logical place --- .../nrw/commons/upload/ShareActivity.java | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) 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 9849387b8..51527b680 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 @@ -257,6 +257,63 @@ public class ShareActivity finish(); } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_share); + ButterKnife.bind(this); + initBack(); + backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); + backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setPlaceholderImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_image_black_24dp, getTheme())) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .build()); + + receiveIntent(); + initViewsAndListeners(); + + if (savedInstanceState != null) { + contribution = savedInstanceState.getParcelable("contribution"); + } + + requestAuthToken(); + + Timber.d("Uri: %s", mediaUri.toString()); + Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); + + useNewPermissions = false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + useNewPermissions = true; + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + locationPermitted = true; + } + } + + // Check location permissions if M or newer for category suggestions, request via snackbar if not present + if (!locationPermitted) { + requestPermissionUsingSnackBar( + getString(R.string.location_permission_rationale), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERM_ON_CREATE_LOCATION); + } + performPreUploadProcessingOfFile(); + + SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); + categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); + if (shareView == null && categorizationFragment == null) { + shareView = new SingleUploadFragment(); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.single_upload_fragment_container, shareView, "shareView") + .commitAllowingStateLoss(); + } + uploadController.prepareService(); + } + /** * Receive intent from ContributionController.java when user selects picture to upload */ @@ -330,64 +387,7 @@ public class ShareActivity } }); } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_share); - ButterKnife.bind(this); - initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); - backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder - .newInstance(getResources()) - .setPlaceholderImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_image_black_24dp, getTheme())) - .setFailureImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_error_outline_black_24dp, getTheme())) - .build()); - - receiveIntent(); - initViewsAndListeners(); - - if (savedInstanceState != null) { - contribution = savedInstanceState.getParcelable("contribution"); - } - - requestAuthToken(); - - Timber.d("Uri: %s", mediaUri.toString()); - Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); - - useNewPermissions = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - useNewPermissions = true; - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - locationPermitted = true; - } - } - - // Check location permissions if M or newer for category suggestions, request via snackbar if not present - if (!locationPermitted) { - requestPermissionUsingSnackBar( - getString(R.string.location_permission_rationale), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_LOCATION); - } - performPreUploadProcessingOfFile(); - - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); - categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); - if (shareView == null && categorizationFragment == null) { - shareView = new SingleUploadFragment(); - getSupportFragmentManager() - .beginTransaction() - .add(R.id.single_upload_fragment_container, shareView, "shareView") - .commitAllowingStateLoss(); - } - uploadController.prepareService(); - } - + /** * Function to display the zoom and map FAB */ From e62022ab55aa2370b22dae728e6155907bff0790 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:25:42 +1000 Subject: [PATCH 025/134] Tidy up onRequestPermissionsResult() --- .../nrw/commons/upload/ShareActivity.java | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) 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 51527b680..be4d65e27 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 @@ -387,11 +387,11 @@ public class ShareActivity } }); } - + /** * Function to display the zoom and map FAB */ - private void showFABMenu(){ + private void showFABMenu() { isFABOpen=true; if( imageObj != null && imageObj.imageCoordsExists == true) @@ -445,42 +445,18 @@ public class ShareActivity public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { - case REQUEST_PERM_ON_CREATE_STORAGE: { - if (grantResults.length >= 1 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - backgroundImageView.setImageURI(mediaUri); - storagePermitted = true; - performPreUploadProcessingOfFile(); - } - return; - } case REQUEST_PERM_ON_CREATE_LOCATION: { - if (grantResults.length >= 1 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - locationPermitted = true; - performPreUploadProcessingOfFile(); - } - return; - } - case REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION: { - if (grantResults.length >= 2 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - backgroundImageView.setImageURI(mediaUri); - storagePermitted = true; - performPreUploadProcessingOfFile(); - } - if (grantResults.length >= 2 - && grantResults[1] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { locationPermitted = true; performPreUploadProcessingOfFile(); } return; } + // Storage (from submit button) - this needs to be separate from (1) because only the // submit button should bring user to next screen case REQUEST_PERM_ON_SUBMIT_STORAGE: { - if (grantResults.length >= 1 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //It is OK to call this at both (1) and (4) because if perm had been granted at //snackbar, user should not be prompted at submit button performPreUploadProcessingOfFile(); @@ -489,7 +465,6 @@ public class ShareActivity uploadBegins(); snackbar.dismiss(); } - return; } } } From b1c3e8a0ac270f7b5dafb2ca49649c30b7626bda Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:41:15 +1000 Subject: [PATCH 026/134] Tidying up code --- .../fr/free/nrw/commons/upload/ShareActivity.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 be4d65e27..567cd27e9 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 @@ -394,7 +394,7 @@ public class ShareActivity private void showFABMenu() { isFABOpen=true; - if( imageObj != null && imageObj.imageCoordsExists == true) + if( imageObj != null && imageObj.imageCoordsExists) maps_fragment.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); @@ -482,12 +482,10 @@ public class ShareActivity ExistingFileAsync fileAsyncTask = new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { Timber.d("%s duplicate check: %s", mediaUri.toString(), result); - duplicateCheckPassed = (result == DUPLICATE_PROCEED - || result == NO_DUPLICATE); - /* - TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means - we are processing images that are already on server???... - */ + duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); + + //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means + //we are processing images that are already on server???... if (duplicateCheckPassed) { //image can be uploaded, so now check if its a useless picture or not @@ -500,7 +498,6 @@ public class ShareActivity Timber.d(e, "IO Exception: "); } } - getFileMetadata(locationPermitted); } else { Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", From ea5f3a6ea9e2d09b33b81cab1a492fc3a1f46611 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:43:21 +1000 Subject: [PATCH 027/134] Add Javadocs --- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 567cd27e9..198792314 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 @@ -441,9 +441,14 @@ public class ShareActivity return isNearbyUpload; } + /** + * Handles BOTH snackbar permission request (for location) and submit button permission request (for storage) + * @param requestCode type of request + * @param permissions permissions requested + * @param grantResults grant results + */ @Override - public void onRequestPermissionsResult(int requestCode, - @NonNull String[] permissions, @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_PERM_ON_CREATE_LOCATION: { if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { From 80a97c503714916b432cd382daf977030ef867c3 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 17 May 2018 08:24:27 +0200 Subject: [PATCH 028/134] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ast/strings.xml | 14 ++++++++++++++ app/src/main/res/values-diq/strings.xml | 8 ++++++-- app/src/main/res/values-hu/strings.xml | 12 ++++++++++-- app/src/main/res/values-is/strings.xml | 10 ++++++++++ app/src/main/res/values-ru/strings.xml | 19 ++++++++----------- app/src/main/res/values-sv/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 7 +++++++ 8 files changed, 57 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 532a4897e..947bc441a 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -84,6 +84,7 @@ Categoríes Configuración Date d\'alta + Imáxenes destacaes Tocante a La app de Wikimedia Commons ye software de códigu abiertu, creáu y calteníu por becaos y voluntarios de la comunidá de Wikimedia. La Fundación Wikimedia nun participa na creación, desendolcu nin caltenimientu de la app. Crea una nueva <a href=\"https://github.com/commons-app/apps-android-commons/issues\">incidencia en GitHub</a> pa informar de problemes y suxerencies. @@ -169,6 +170,8 @@ Títulu del mediu Descripción Equí va la descripción del mediu. Esto pué ser llargo enforma, y necesitará espardese per delles llinies. Sicasí, esperamos que se vea bien. + Autor + El nome d\'usuariu del autor de la imaxe destacada va equí. Data d\'unviu Llicencia Coordenaes @@ -211,6 +214,7 @@ Salir Tutorial Avisos + Destacada Los sitios cercanos nun pueden amosase ensin los permisos d\'allugamientu nun s\'atoparon descripciones Páxina del ficheru en Commons @@ -259,4 +263,14 @@ Siguir Encaboxar Retentar + Entendílo + Estos son sitios cercanos a ti que precisen imaxes para ilustrar los sos artículos de Wikipedia + Tocando esti botón amuésase la llista d\'esos llugares + Puedes xubir una imaxe pa cualquier sitiu dende la galería o la cámara + Nun s\'alcontró nenguna imaxe + Asocedió un error al cargar les imáxenes. + Xubida por: %1$s + Compartir app + Nun s\'especificaron les coordenaes al escoyer la imaxe + Error al llograr los llugares cercanos. diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 1f1d69ea3..53bd88bac 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -13,6 +13,7 @@ Bıngeh Lokasyon Commons + Eyari Namey karberi Parola @@ -84,7 +85,8 @@ Qeyd be Heq te cı Qandê yew <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-cıkewtış</a>ê neweyi rê rapor û teklifan bıaferne. - <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Politikay nımıtışi</a> + <u>Politikaya nımıtışi</u> + <u>İştırakkerdoği</u> Heq te cı Peyd rışten bırış (E-posta ra) E-posta eyar nêbi @@ -92,7 +94,7 @@ Anciya bıcerrebne Bıtexelne Ron - Lisans + Lisanso hesebiyaye Attribution-ShareAlike 3.0 Attribution 3.0 CC0 @@ -127,6 +129,8 @@ E Sername + Şınasnayış + Nuştekar Lisans Koordinati Korbıze diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 81e655b95..d9580dca1 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -162,8 +162,8 @@ Nincs leírás Ismeretlen licenc Frissítés - Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül. - Szükséges engedély: Külső tárhely írása. Az alkalmazás nem működik enélkül. + Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül. + Szükséges engedély: Külső tárhely írása. Az alkalmazás nem tudja használni a kamerát enélkül. Lehetséges engedély: Jelenlegi hely megszerzése, a kategóriajavaslatok lehetőségéért. OK Közeli helyek @@ -242,6 +242,7 @@ A hely nem változott. A hely nem érhető el. Közeli helyek listájának megtekintéséhez engedély szükséges + SZÓCIKK OLVASÁSA Üdvözlünk a Wikimedia Commonson, %1$s! Örülünk, hogy itt vagy. %1$s üzenetet hagyott a vitalapodon Köszönjük a szerkesztésedet! @@ -258,5 +259,12 @@ Folytatás Mégse Újra + 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 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-is/strings.xml b/app/src/main/res/values-is/strings.xml index ba5bdfe42..f1b020965 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -263,4 +263,14 @@ Halda áfram Hætta við Reyna aftur + Náði því! + Þetta eru þeir staðir í næsta nágrenni við þig sem vantar myndir til að skýra með Wikipedia-greinar + Ef ýtt er á þennan hnapp birtist listi yfir þessa staði + Þú getur sent inn mynd úr myndasafninu þínu eða myndavélinni + Engir myndir fundust! + Villa kom upp við að hlaða inn myndum. + Sent inn af: %1$s + Deila forriti + Hnit voru ekki tilgreind við val myndar + Villa við að sækja nálæga staði. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ce71af94b..0d5a0ee3e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -46,11 +46,7 @@ Завершение загрузки %1$s Загрузка %1$s не удалась Нажмите для просмотра - - %1$d файл загружается - %1$d файла загружается - %1$d файлов загружается - + %1$d {{PLURAL:%1$d|one=файл загружается|few=файла загружается|файлов загружается}} Мои недавние загрузки В очереди Ошибка загрузки. @@ -115,7 +111,7 @@ Почтовый клиент не установлен Недавно использованные категории Ожидание первой синхронизации… - Вы ещё не загрузили ни одной фотографии. + Вы ещё не загрузили ни одного изображения. Повторить Отмена Это изображение будет лицензировано под %1$s @@ -123,7 +119,7 @@ Скачать Лицензия по умолчанию Использовать предыдущие название/описание - Автоматически получить текущее местоположение + Анализ местоположения Получить текущее местоположение, чтобы были предложены категории, если изображение не содержит геотегов Ночной режим Использовать тёмную тему @@ -148,7 +144,7 @@ CC BY 4.0 CC Zero Викисклад содержит бо́льшую часть изображений, которые используются в Википедии. - Ваши изображения помогают образованию людей во всём мире! + Ваши изображения могут помочь образованию людей во всём мире! Пожалуйста, загрузите фотографии, которые были сняты или созданы исключительно вами: Природные объекты (например, цветы, животные, горы)\n• Полезные предметы (например, велосипеды, вокзалы)\n• Известные люди (например, ваш мэр, спортсмены-олимпийцы, которых вы встретили) Природные объекты (например, цветы, животные, горы) @@ -166,7 +162,7 @@ Категории: Sydney Opera House from the west, Sydney Opera House remote views Загрузите свои изображения. Помогите Википедии оживить статьи! Изображения в Википедии хранятся на Викискладе. - Ваши изображения помогают образованию людей во всём мире. + Ваши изображения могут помочь образованию людей во всём мире. Избегайте материалов, защищённых авторским правом, например, найденных в Интернете, изображений плакатов, книжных обложек и т.п. Вам это понятно? Да! @@ -211,7 +207,7 @@ Facebook-страница Commons Исходные коды Commons на гитхабе Фоновое изображение - Ошибка медиаизображения + Ошибка медиафайла Изображение не найдено Загрузить изображение Гора Зао @@ -246,7 +242,7 @@ Пожалуйста, подробно опишите загружаемый файл: где он был снят? что на нём изображено? каков его контекст? Пожалуйста опишите изображённых персон или объекты. Добавьте информацию, о которой нельзя легко догадаться, например, время суток, когда снимался файл. Если снято что-то необычное, постарайтесь пояснить, что именно в этом необычного. Это изображение слишком тёмное. Вы уверены, что хотите его загрузить? Викисклад подходит только для фотографий, имеющих энциклопедическую ценность. Это изображение размыто. Вы уверены, что хотите его загрузить? Викисклад подходит только для фотографий, имеющих энциклопедическую ценность. - Дать разрешение + Разрешить Использовать внешнее хранилище Сохранять изображения, сделанные с помощью встроенной камеры на устройстве Войдите в свою учётную запись @@ -294,4 +290,5 @@ Загружено участником %1$s Поделиться приложением Во время выбора изображения не были указаны координаты + Ошибка получения мест поблизости diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 17a324a97..1d2fc868e 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -280,4 +280,5 @@ Uppladdad av: %1$s Dela app Koordinater specificerades inte vid bildvalet + Fel uppstod när platser i närheten hämtades. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index dadd40917..c9dd60b41 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -280,4 +280,5 @@ Yükleyen: %1$s Uygulamayı Paylaş Koordinatlar görüntü seçimi sırasında belirlenmedi + Yakındaki yerler alınırken hata oluştu. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a4ed20320..1aba51e02 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -279,4 +279,11 @@ Виконується Скасувати Повторити + Зрозуміло + Натискання цієї кнопки згенерує список таких місць + Зображень не знайдено! + Сталася помилка при завантаженні зображень. + Завантажено: %1$s + Поділитися програмою + Помилка отримання місць поблизу. From c4f55d2fe8a2f0e658a4b443c7aa2fbb70a94ec9 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 7 May 2018 07:54:16 +0200 Subject: [PATCH 029/134] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-hu/strings.xml | 8 ++++++++ app/src/main/res/values-iw/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 1 + app/src/main/res/values-pms/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-sv/strings.xml | 2 ++ app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + 13 files changed, 21 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f265e7ebd..36e013976 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -269,4 +269,5 @@ Fortfahren Abbrechen Erneut versuchen + App teilen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 58aa93860..0dae5c65e 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -273,4 +273,5 @@ Συνέχεια Ακύρωση Ξαναπροσπαθήστε + Κοινοποίηση εφαρμογής diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 8494a7a36..442fc6bc5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -279,4 +279,5 @@ Continuer Annuler Réessayer + Partager les applications diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5524afb0e..81e655b95 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -13,6 +13,7 @@ * ViDam --> + Megjelenés Általános Visszajelzés Helyszín @@ -145,6 +146,7 @@ Híres emberek (a polgármestered, olimpikonok, akikkel találkoztál) Kérjük, NE tölts fel: - Szelfiket vagy képeket a barátaidról\n- Internetröl letöltött képeket\n- Kereskedelmi alkalmazások képernyőképeit + Az Internetről letöltött képek Példa feltöltés: - Cím: Sydney-i Operaház\n- Leírás: A Sydney-i Operaház az öböl túlpartjáról\n- Kategóriák: Sydney Opera House from the west, Sydney Opera House remote views Cím: Sydney-i Operaház @@ -191,6 +193,7 @@ Commons Logo Commons weboldal Commons Facebook-oldal + Commons Github forráskód Háttérkép Nem található kép Kép feltöltése @@ -223,6 +226,8 @@ Hiba a képek gyorsítótárazásakor Egy egyedi, leíró cím a fájlnak, ami fájlnévként fog szolgálni. Egyszerű nyelvezetet használhatsz szóközökkel. Ne tedd bele a kiterjesztést. Kérlek a lehető legteljesebb módon írd le a fájlt: hol készült, mit ábrázol, mi a kontextus? Kérlek add meg az objektumokat vagy személyeket a képen, valamint a nehezen kitalálható információkat (például a kép készítésének dátumát, ha az egy tájkép). Amennyiben a média valami szokatlant ábrázol, kérlek fejtsd ki, hogy mi teszi szokatlanná. + Ez a fénykép túl sötét, biztos fel akarod tölteni? A Wikimédia Commons csak enciklopédikus értékkel bíró képeket tart meg. + Ez a fénykép homályos, biztos fel akarod tölteni? A Wikimédia Commons csak enciklopédikus értékkel bíró képeket tart meg. Engedély adása Külső tárhely használata Az alkalmazáson belüli kamerával készült képek mentése az eszközre @@ -238,6 +243,7 @@ A hely nem érhető el. Közeli helyek listájának megtekintéséhez engedély szükséges Üdvözlünk a Wikimedia Commonson, %1$s! Örülünk, hogy itt vagy. + %1$s üzenetet hagyott a vitalapodon Köszönjük a szerkesztésedet! WIKIDATA WIKIPÉDIA @@ -249,6 +255,8 @@ Internet elérhető Nincs értesítés Nyelvek + Folytatás Mégse Újra + Alkalmazás megosztása diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 48750daf9..762a3be31 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -273,4 +273,5 @@ המשך ביטול לנסות שוב + שיתוף היישום diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 11faee9c6..d0e1c21ed 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -266,4 +266,5 @@ 진행 취소 다시 시도 + 앱 공유 diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index dd1e6f03e..4e44dad60 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -263,4 +263,5 @@ Продолжи Откажи Пробај пак + Сподели прилог diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 0cd748031..30115f1ad 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -263,4 +263,5 @@ Andé anans Anulé Prové torna + Partagé j\'aplicassion diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 4bc478527..6722ae4bb 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -275,4 +275,5 @@ Avançar Cancelar Tentar novamente + Compartilhar o aplicativo diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 5e4862ecb..b0717f3dc 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -159,6 +159,7 @@ Evite materiais protegidos por direitos de autor que tenham sido encontrados na Internet, bem como imagens de cartazes, capas de livros, etc. Acha que conseguiu? Sim! + Categorias A carregar… Nenhuma selecionada diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index fa5174ced..24ef60b61 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -156,6 +156,7 @@ Undvik upphovsrättsskyddat material som du hittar på Internet, samt bilder av affischer, bokomslag, etc. Tror du att du förstår? Ja! + Kategorier Läser in… Ingen markerad @@ -270,4 +271,5 @@ Fortsätt Avbryt Försök igen + Dela app diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 4966d9c08..a2878cddf 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -271,4 +271,5 @@ 已進行 取消 重試 + 分享應用程式 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 83650b408..d3d049ea2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -269,4 +269,5 @@ 已处理 取消 重试 + 分享应用 From 30d7b5d35c7734730933405ed82bf2744076179a Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Mon, 7 May 2018 13:39:23 +0530 Subject: [PATCH 030/134] Integrate API for displaying featured images (#1456) * Integrate API for displaying featured images * Add pagination and refactor code so that it can be reused for category images * Add license info to the images * Fix author view * Remove unused values * Fix minor issues with featured images * Fix null license url issue * Remove some log lines * Fix back navigation issue * fix tests * fix test inits * Gracefully handling various error situations * Added java docs --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 5 +- .../category/CategoryImageController.java | 29 +++ .../commons/category/CategoryImageUtils.java | 225 +++++++++++++++++ .../category/CategoryImagesActivity.java | 160 ++++++++++++ .../category/CategoryImagesListFragment.java | 227 ++++++++++++++++++ .../nrw/commons/category/GridViewAdapter.java | 88 +++++++ .../nrw/commons/category/QueryContinue.java | 24 ++ .../nrw/commons/di/ActivityBuilderModule.java | 4 +- .../commons/di/CommonsApplicationModule.java | 30 ++- .../nrw/commons/di/FragmentBuilderModule.java | 4 +- .../nrw/commons/featured/FeaturedImage.java | 44 ---- .../featured/FeaturedImagesActivity.java | 114 --------- .../featured/FeaturedImagesListFragment.java | 52 ---- .../commons/featured/MockGridViewAdapter.java | 50 ---- .../commons/media/MediaDetailFragment.java | 31 ++- .../mwapi/ApacheHttpClientMediaWikiApi.java | 98 +++++++- .../free/nrw/commons/mwapi/MediaWikiApi.java | 3 + .../commons/theme/NavigationBaseActivity.java | 7 +- .../free/nrw/commons/utils/ContinueUtils.java | 15 ++ ...mages.xml => activity_category_images.xml} | 11 +- ...mages.xml => fragment_category_images.xml} | 15 +- ..._images.xml => layout_category_images.xml} | 10 +- app/src/main/res/values/strings.xml | 4 + .../nrw/commons/TestCommonsApplication.kt | 5 +- .../mwapi/ApacheHttpClientMediaWikiApiTest.kt | 5 +- 26 files changed, 953 insertions(+), 309 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java rename app/src/main/res/layout/{activity_featured_images.xml => activity_category_images.xml} (69%) rename app/src/main/res/layout/{fragment_featured_images.xml => fragment_category_images.xml} (70%) rename app/src/main/res/layout/{layout_featured_images.xml => layout_category_images.xml} (88%) diff --git a/app/build.gradle b/app/build.gradle index 535f58143..9c6f62fd4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,6 +49,8 @@ dependencies { implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0' implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0' + implementation 'org.jsoup:jsoup:1.11.3' + implementation 'com.facebook.fresco:fresco:1.5.0' implementation 'com.facebook.stetho:stetho:1.5.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6aab09b55..17f6770d2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -92,8 +92,9 @@ android:label="@string/navigation_item_notification" /> + android:name=".category.CategoryImagesActivity" + android:label="@string/title_activity_featured_images" + android:parentActivityName=".contributions.ContributionsActivity" /> diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java new file mode 100644 index 000000000..3495d710c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java @@ -0,0 +1,29 @@ +package fr.free.nrw.commons.category; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.mwapi.MediaWikiApi; + +@Singleton +public class CategoryImageController { + + private MediaWikiApi mediaWikiApi; + + @Inject + public CategoryImageController(MediaWikiApi mediaWikiApi) { + this.mediaWikiApi = mediaWikiApi; + } + + /** + * Takes a category name as input and calls the API to get a list of images for that category + * @param categoryName + * @return + */ + public List getCategoryImages(String categoryName) { + return mediaWikiApi.getCategoryImages(categoryName); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..18749847e --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -0,0 +1,225 @@ +package fr.free.nrw.commons.category; + +import org.jsoup.Jsoup; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.annotation.Nullable; + +import fr.free.nrw.commons.Media; +import timber.log.Timber; + +public class CategoryImageUtils { + + /** + * The method iterates over the child nodes to return a list of Media objects + * @param childNodes + * @return + */ + 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)); + } + + return categoryImages; + } + + /** + * Creates a new Media object from the XML response as received by the API + * @param node + * @return + */ + private static Media getMediaFromPage(Node node) { + Media media = new Media(null, + getImageUrl(node), + getFileName(node), + getDescription(node), + getDataLength(node), + getDateCreated(node), + getDateCreated(node), + getCreator(node) + ); + + media.setLicense(getLicense(node)); + + return media; + } + + /** + * Extracts the filename of the uploaded image + * @param document + * @return + */ + private static String getFileName(Node document) { + Element element = (Element) document; + return element.getAttribute("title"); + } + + /** + * Extracts the image description for that particular upload + * @param document + * @return + */ + private static String getDescription(Node document) { + return getMetaDataValue(document, "ImageDescription"); + } + + /** + * Extracts license information from the image meta data + * @param document + * @return + */ + private static String getLicense(Node document) { + return getMetaDataValue(document, "License"); + } + + /** + * Returns the parsed value of artist from the response + * The artist information is returned as a HTML string from the API. Jsoup library parses the HTML string + * to extract just the text value + * @param document + * @return + */ + private static String getCreator(Node document) { + String artist = getMetaDataValue(document, "Artist"); + if (artist != null) { + return Jsoup.parse(artist).text(); + } + return null; + } + + /** + * Returns the parsed date of creation of the image + * @param document + * @return + */ + private static Date getDateCreated(Node document) { + String dateTime = getMetaDataValue(document, "DateTime"); + if (dateTime != null && !dateTime.equals("")) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + return format.parse(dateTime); + } catch (ParseException e) { + Timber.d("Error occurred while parsing date %s", dateTime); + return new Date(); + } + } + return new Date(); + } + + /** + * @param document + * @return Returns the url attribute from the imageInfo node + */ + private static String getImageUrl(Node document) { + Element element = (Element) getImageInfo(document); + if (element != null) { + return element.getAttribute("url"); + } + return null; + } + + /** + * Takes the node document and gives out the attribute length from the node document + * @param document + * @return + */ + private static long getDataLength(Node document) { + Element element = (Element) document; + if (element != null) { + String length = element.getAttribute("length"); + if (length != null && !length.equals("")) { + return Long.parseLong(length); + } + } + return 0L; + } + + /** + * Generic method to get the value of any meta as returned by the getMetaData function + * @param document node document as returned by API + * @param metaName the name of meta node to be returned + * @return + */ + private static String getMetaDataValue(Node document, String metaName) { + Element metaData = getMetaData(document, metaName); + if (metaData != null) { + return metaData.getAttribute("value"); + } + return null; + } + + /** + * Generic method to return an element taking the node document and metaName as input + * @param document node document as returned by API + * @param metaName the name of meta node to be returned + * @return + */ + @Nullable + private static Element getMetaData(Node document, String metaName) { + Node extraMetaData = getExtraMetaData(document); + if (extraMetaData != null) { + Node node = getNode(extraMetaData, metaName); + if (node != null) { + return (Element) node; + } + } + return null; + } + + /** + * Extracts extmetadata from the response XML + * @param document + * @return + */ + @Nullable + private static Node getExtraMetaData(Node document) { + Node imageInfo = getImageInfo(document); + if (imageInfo != null) { + return getNode(imageInfo, "extmetadata"); + } + return null; + } + + /** + * Extracts the ii node from the imageinfo node + * @param document + * @return + */ + @Nullable + private static Node getImageInfo(Node document) { + Node imageInfo = getNode(document, "imageinfo"); + if (imageInfo != null) { + return getNode(imageInfo, "ii"); + } + return null; + } + + /** + * Takes a parent node as input and returns a child node if present + * @param node parent node + * @param nodeName child node name + * @return + */ + @Nullable + public static Node getNode(Node node, String nodeName) { + NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node nodeItem = childNodes.item(i); + Element item = (Element) nodeItem; + if (item.getTagName().equals(nodeName)) { + return nodeItem; + } + } + return null; + } +} 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 new file mode 100644 index 000000000..1f385b258 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -0,0 +1,160 @@ +package fr.free.nrw.commons.category; + +import android.content.Context; +import android.content.Intent; +import android.database.DataSetObserver; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.AdapterView; + +import butterknife.ButterKnife; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.AuthenticatedActivity; +import fr.free.nrw.commons.media.MediaDetailPagerFragment; +import timber.log.Timber; + +/** + * 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. + */ + +public class CategoryImagesActivity + extends AuthenticatedActivity + implements FragmentManager.OnBackStackChangedListener, + MediaDetailPagerFragment.MediaDetailProvider, + AdapterView.OnItemClickListener{ + + + private FragmentManager supportFragmentManager; + private CategoryImagesListFragment categoryImagesListFragment; + private MediaDetailPagerFragment mediaDetails; + + @Override + protected void onAuthCookieAcquired(String authCookie) { + + } + + @Override + protected void onAuthFailure() { + + } + + @Override + 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(); + setCategoryImagesFragment(); + supportFragmentManager.addOnBackStackChangedListener(this); + if (savedInstanceState != null) { + mediaDetails = (MediaDetailPagerFragment) supportFragmentManager + .findFragmentById(R.id.fragmentContainer); + + } + requestAuthToken(); + initDrawer(); + setPageTitle(); + } + + /** + * Gets the categoryName from the intent and initializes the fragment for showing images of that category + */ + private void setCategoryImagesFragment() { + categoryImagesListFragment = new CategoryImagesListFragment(); + String 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 + .add(R.id.fragmentContainer, categoryImagesListFragment) + .commit(); + } + } + + /** + * Gets the passed title from the intents and displays it as the page title + */ + private void setPageTitle() { + if (getIntent() != null && getIntent().getStringExtra("title") != null) { + setTitle(getIntent().getStringExtra("title")); + } + } + + @Override + public void onBackStackChanged() { + } + + @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 + .beginTransaction() + .replace(R.id.fragmentContainer, mediaDetails) + .addToBackStack(null) + .commit(); + supportFragmentManager.executePendingTransactions(); + } + mediaDetails.showImage(i); + } + + /** + * 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, CategoryImagesActivity.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/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java new file mode 100644 index 000000000..3b6734edd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -0,0 +1,227 @@ +package fr.free.nrw.commons.category; + +import android.annotation.SuppressLint; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.GridView; +import android.widget.ListAdapter; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; + +import butterknife.BindView; +import butterknife.ButterKnife; +import dagger.android.support.DaggerFragment; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +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 images for a particular category with load more on scrolling incorporated + */ +public class CategoryImagesListFragment extends DaggerFragment { + + private static int TIMEOUT_SECONDS = 15; + + private GridViewAdapter gridAdapter; + + @BindView(R.id.statusMessage) + TextView statusTextView; + @BindView(R.id.loadingImagesProgressBar) ProgressBar progressBar; + @BindView(R.id.categoryImagesList) GridView gridView; + + private boolean hasMoreImages = true; + private boolean isLoading; + private String categoryName = null; + + @Inject CategoryImageController controller; + @Inject @Named("category_prefs") SharedPreferences categoryPreferences; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_category_images, container, false); + ButterKnife.bind(this, v); + return v; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + gridView.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity()); + initViews(); + } + + /** + * Initializes the UI elements for the fragment + * Setup the grid view to and scroll listener for it + */ + private void initViews() { + String categoryName = getArguments().getString("categoryName"); + if (getArguments() != null && categoryName != null) { + this.categoryName = categoryName; + resetQueryContinueValues(categoryName); + initList(); + setScrollListener(); + } + } + + /** + * Query continue values determine the last page that was loaded for the particular keyword + * This method resets those values, so that the results can be queried from the first page itself + * @param keyword + */ + private void resetQueryContinueValues(String keyword) { + SharedPreferences.Editor editor = categoryPreferences.edit(); + editor.remove(keyword); + editor.apply(); + } + + /** + * Checks for internet connection and then initializes the grid view with first 10 images of that category + */ + @SuppressLint("CheckResult") + private void initList() { + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + + isLoading = true; + progressBar.setVisibility(VISIBLE); + Observable.fromCallable(() -> controller.getCategoryImages(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + /** + * Handles the UI updates for no internet scenario + */ + private void handleNoInternet() { + progressBar.setVisibility(GONE); + if (gridAdapter == null || gridAdapter.isEmpty()) { + statusTextView.setVisibility(VISIBLE); + statusTextView.setText(getString(R.string.no_internet)); + } else { + ViewUtil.showSnackbar(gridView, R.string.no_internet); + } + } + + /** + * Logs and handles API error scenario + * @param throwable + */ + private void handleError(Throwable throwable) { + Timber.e(throwable, "Error occurred while loading featured images"); + initErrorView(); + } + + /** + * 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 { + statusTextView.setVisibility(GONE); + } + } + + /** + * Initializes the adapter with a list of Media objects + * @param mediaList + */ + private void setAdapter(List mediaList) { + gridAdapter = new GridViewAdapter(this.getContext(), R.layout.layout_category_images, mediaList); + gridView.setAdapter(gridAdapter); + } + + /** + * Sets the scroll listener for the grid view so that more images are fetched when the user scrolls down + * Checks if the category has more images before loading + * Also checks whether images are currently being fetched before triggering another request + */ + private void setScrollListener() { + gridView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + 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)) { + isLoading = true; + fetchMoreImages(); + } + } + }); + } + + /** + * Fetches more images for the category and adds it to the grid view adapter + */ + @SuppressLint("CheckResult") + private void fetchMoreImages() { + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + + progressBar.setVisibility(VISIBLE); + Observable.fromCallable(() -> controller.getCategoryImages(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + /** + * Handles the success scenario + * On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter + * @param collection + */ + private void handleSuccess(List collection) { + if(collection == null || collection.isEmpty()) { + initErrorView(); + hasMoreImages = false; + return; + } + + if(gridAdapter == null) { + setAdapter(collection); + } else { + gridAdapter.addItems(collection); + } + + progressBar.setVisibility(GONE); + isLoading = false; + statusTextView.setVisibility(GONE); + } + + 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 new file mode 100644 index 000000000..c8e6066f6 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -0,0 +1,88 @@ +package fr.free.nrw.commons.category; + +import android.app.Activity; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.MediaWikiImageView; +import fr.free.nrw.commons.R; + +/** + * This is created to only display UI implementation. Needs to be changed in real implementation + */ + +public class GridViewAdapter extends ArrayAdapter { + private Context context; + private List data; + + public GridViewAdapter(Context context, int layoutResourceId, List data) { + super(context, layoutResourceId, data); + this.context = context; + this.data = data; + } + + /** + * Adds more item to the list + * Its triggered on scrolling down in the list + * @param images + */ + public void addItems(List images) { + if (data == null) { + data = new ArrayList<>(); + } + data.addAll(images); + notifyDataSetChanged(); + } + + @Override + public boolean isEmpty() { + return data == null || data.isEmpty(); + } + + /** + * Sets up the UI for the category image item + * @param position + * @param convertView + * @param parent + * @return + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + if (convertView == null) { + LayoutInflater inflater = ((Activity) context).getLayoutInflater(); + convertView = inflater.inflate(R.layout.layout_category_images, null); + } + + Media item = data.get(position); + 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()); + setAuthorView(item, author); + imageView.setMedia(item); + return convertView; + } + + /** + * Shows author information if its present + * @param item + * @param author + */ + private void setAuthorView(Media item, TextView author) { + if (item.getCreator() != null && !item.getCreator().equals("")) { + String uploadedByTemplate = context.getString(R.string.image_uploaded_by); + author.setText(String.format(uploadedByTemplate, item.getCreator())); + } else { + author.setVisibility(View.GONE); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java b/app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java new file mode 100644 index 000000000..e12d5a778 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java @@ -0,0 +1,24 @@ +package fr.free.nrw.commons.category; + +/** + * 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 + */ +public class QueryContinue { + private String continueParam; + private String gcmContinueParam; + + public QueryContinue(String continueParam, String gcmContinueParam) { + this.continueParam = continueParam; + this.gcmContinueParam = gcmContinueParam; + } + + public String getGcmContinueParam() { + return gcmContinueParam; + } + + public String getContinueParam() { + return continueParam; + } +} + 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 f88f3b34a..51aa85903 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 @@ -7,7 +7,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.contributions.ContributionsActivity; -import fr.free.nrw.commons.featured.FeaturedImagesActivity; +import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.settings.SettingsActivity; @@ -49,5 +49,5 @@ public abstract class ActivityBuilderModule { abstract NotificationActivity bindNotificationActivity(); @ContributesAndroidInjector - abstract FeaturedImagesActivity bindFeaturedImagesActivity(); + abstract CategoryImagesActivity bindFeaturedImagesActivity(); } 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 e8b915c7e..55281be7e 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 @@ -6,6 +6,8 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.util.LruCache; +import com.google.gson.Gson; + import javax.inject.Named; import javax.inject.Singleton; @@ -85,6 +87,17 @@ public class CommonsApplicationModule { return context.getSharedPreferences("prefs", MODE_PRIVATE); } + /** + * + * @param context + * @return returns categoryPrefs + */ + @Provides + @Named("category_prefs") + public SharedPreferences providesCategorySharedPreferences(Context context) { + return context.getSharedPreferences("categoryPrefs", MODE_PRIVATE); + } + @Provides @Named("direct_nearby_upload_prefs") public SharedPreferences providesDirectNearbyUploadPreferences(Context context) { @@ -106,8 +119,11 @@ public class CommonsApplicationModule { @Provides @Singleton - public MediaWikiApi provideMediaWikiApi(Context context, @Named("default_preferences") SharedPreferences sharedPreferences) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, sharedPreferences); + 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 @@ -116,6 +132,16 @@ public class CommonsApplicationModule { 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 Gson(); + } + @Provides @Singleton public CacheController provideCacheController() { 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 c5cdcb5a7..dfed64871 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 @@ -4,7 +4,7 @@ import dagger.Module; import dagger.android.ContributesAndroidInjector; import fr.free.nrw.commons.category.CategorizationFragment; import fr.free.nrw.commons.contributions.ContributionsListFragment; -import fr.free.nrw.commons.featured.FeaturedImagesListFragment; +import fr.free.nrw.commons.category.CategoryImagesListFragment; import fr.free.nrw.commons.media.MediaDetailFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.nearby.NearbyListFragment; @@ -49,6 +49,6 @@ public abstract class FragmentBuilderModule { abstract SingleUploadFragment bindSingleUploadFragment(); @ContributesAndroidInjector - abstract FeaturedImagesListFragment bindFeaturedImagesListFragment(); + abstract CategoryImagesListFragment bindFeaturedImagesListFragment(); } diff --git a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java b/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java deleted file mode 100644 index 853fba29e..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java +++ /dev/null @@ -1,44 +0,0 @@ -package fr.free.nrw.commons.featured; - - -import fr.free.nrw.commons.Media; - -/** - * Object to hold FeaturedImage - */ - -public class FeaturedImage { - private Media image; - private String author; - private String fileName; - - public FeaturedImage(Media image, String author, String fileName) { - this.image = image; - this.author = author; - this.fileName = fileName; - } - - public Media getImage() { - return image; - } - - public void setImage(Media image) { - this.image = image; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java deleted file mode 100644 index a2dc7b00b..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java +++ /dev/null @@ -1,114 +0,0 @@ -package fr.free.nrw.commons.featured; - -import android.database.DataSetObserver; -import android.os.Bundle; -import android.support.v4.app.FragmentManager; -import android.view.View; -import android.widget.AdapterView; - -import butterknife.ButterKnife; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.auth.AuthenticatedActivity; -import fr.free.nrw.commons.media.MediaDetailPagerFragment; - -/** - * This activity displays pic of the days of last xx days - */ - -public class FeaturedImagesActivity - extends AuthenticatedActivity - implements FragmentManager.OnBackStackChangedListener, - MediaDetailPagerFragment.MediaDetailProvider, - AdapterView.OnItemClickListener{ - - private FeaturedImagesListFragment featuredImagesListFragment; - private MediaDetailPagerFragment mediaDetails; - - @Override - protected void onAuthCookieAcquired(String authCookie) { - - } - - @Override - protected void onAuthFailure() { - - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_featured_images); - ButterKnife.bind(this); - - // Activity can call methods in the fragment by acquiring a - // reference to the Fragment from FragmentManager, using findFragmentById() - FragmentManager supportFragmentManager = getSupportFragmentManager(); - featuredImagesListFragment = (FeaturedImagesListFragment)supportFragmentManager - .findFragmentById(R.id.featuedListFragment); - - supportFragmentManager.addOnBackStackChangedListener(this); - if (savedInstanceState != null) { - mediaDetails = (MediaDetailPagerFragment)supportFragmentManager - .findFragmentById(R.id.featuredFragmentContainer); - - } - requestAuthToken(); - initDrawer(); - setTitle(getString(R.string.title_activity_featured_images)); - } - - @Override - public void onBackStackChanged() { - - } - - @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 - .beginTransaction() - .replace(R.id.featuredFragmentContainer, mediaDetails) - .addToBackStack(null) - .commit(); - supportFragmentManager.executePendingTransactions(); - } - mediaDetails.showImage(i); - } - - @Override - public Media getMediaAtPosition(int i) { - if (featuredImagesListFragment.getAdapter() == null) { - // not yet ready to return data - return null; - } else { - return ((FeaturedImage)featuredImagesListFragment.getAdapter().getItem(i)).getImage(); - } - } - - @Override - public int getTotalMediaCount() { - if (featuredImagesListFragment.getAdapter() == null) { - return 0; - } - return featuredImagesListFragment.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/featured/FeaturedImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java deleted file mode 100644 index 19e33b0ee..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java +++ /dev/null @@ -1,52 +0,0 @@ -package fr.free.nrw.commons.featured; - -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.GridView; -import android.widget.ListAdapter; - -import java.util.ArrayList; - -import butterknife.ButterKnife; -import dagger.android.support.DaggerFragment; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; - -public class FeaturedImagesListFragment extends DaggerFragment { - private GridView gridView; - private MockGridViewAdapter gridAdapter; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_featured_images, container, false); - ButterKnife.bind(this, v); - return v; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - gridView = getView().findViewById(R.id.featuredImagesList); - gridView.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity()); - gridAdapter = new MockGridViewAdapter(this.getContext(), R.layout.layout_featured_images, getMockFeaturedImages()); - gridView.setAdapter(gridAdapter); - - } - - private ArrayList getMockFeaturedImages(){ - ArrayList featuredImages = new ArrayList<>(); - for (int i=0; i<10; i++){ - featuredImages.add(new FeaturedImage(new Media("test.jpg"), "username: test", "test file name")); - } - return featuredImages; - } - - public ListAdapter getAdapter() { - return gridView.getAdapter(); - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java deleted file mode 100644 index 7aa2a8892..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package fr.free.nrw.commons.featured; - -import android.app.Activity; -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -import java.util.ArrayList; - -import fr.free.nrw.commons.MediaWikiImageView; -import fr.free.nrw.commons.R; - -/** - * This is created to only display UI implementation. Needs to be changed in real implementation - */ - -public class MockGridViewAdapter extends ArrayAdapter { - private Context context; - private int layoutResourceId; - private ArrayList data = new ArrayList(); - - public MockGridViewAdapter(Context context, int layoutResourceId, ArrayList data) { - super(context, layoutResourceId, data); - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - if (convertView == null) { - LayoutInflater inflater = ((Activity) context).getLayoutInflater(); - convertView = inflater.inflate(R.layout.layout_featured_images, null); - } - - FeaturedImage item = data.get(position); - MediaWikiImageView imageView = convertView.findViewById(R.id.featuredImageView); - TextView fileName = convertView.findViewById(R.id.featuredImageTitle); - TextView author = convertView.findViewById(R.id.featuredImageAuthor); - fileName.setText("Test file name"); - author.setText("Uploaded by: Test user name"); - imageView.setMedia(item.getImage()); - return convertView; - } - -} \ No newline at end of file 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 037f92e56..c3a977ab5 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.mwapi.MediaWikiApi; import fr.free.nrw.commons.ui.widget.CompatTextView; import timber.log.Timber; +import static android.view.View.*; import static android.widget.Toast.LENGTH_SHORT; public class MediaDetailFragment extends CommonsDaggerSupportFragment { @@ -154,9 +155,9 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { authorLayout = (LinearLayout) view.findViewById(R.id.authorLinearLayout); if (isFeaturedMedia){ - authorLayout.setVisibility(View.VISIBLE); + authorLayout.setVisibility(VISIBLE); } else { - authorLayout.setVisibility(View.GONE); + authorLayout.setVisibility(GONE); } licenseList = new LicenseList(getActivity()); @@ -306,6 +307,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { } rebuildCatList(); + if(media.getCreator() == null || media.getCreator().equals("")) { + authorLayout.setVisibility(GONE); + } else { + author.setText(media.getCreator()); + } + checkDeletion(media); } @@ -313,13 +320,17 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (licenseLink(media) != null) { license.setOnClickListener(v -> openWebBrowser(licenseLink(media))); } else { - Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); - toast.show(); + if(isFeaturedMedia) { + Timber.d("Unable to fetch license URL for %s", media.getLicense()); + } else { + Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); + toast.show(); + } } if (media.getCoordinates() != null) { coordinates.setOnClickListener(v -> openMap(media.getCoordinates())); } - if (delete.getVisibility() == View.VISIBLE) { + if (delete.getVisibility() == VISIBLE) { enableDeleteButton(true); delete.setOnClickListener(v -> { @@ -369,7 +380,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); }); } - if (nominatedforDeletion.getVisibility() == View.VISIBLE){ + if (nominatedforDeletion.getVisibility() == VISIBLE){ seeMore.setOnClickListener(v -> { openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); }); @@ -476,12 +487,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private void checkDeletion(Media media){ if (media.getRequestedDeletion()){ - delete.setVisibility(View.GONE); - nominatedforDeletion.setVisibility(View.VISIBLE); + delete.setVisibility(GONE); + nominatedforDeletion.setVisibility(VISIBLE); } else{ - delete.setVisibility(View.VISIBLE); - nominatedforDeletion.setVisibility(View.GONE); + delete.setVisibility(VISIBLE); + nominatedforDeletion.setVisibility(GONE); } } 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 78051abd8..6629d0933 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 @@ -9,6 +9,8 @@ import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.Log; +import com.google.gson.Gson; + import org.apache.http.HttpResponse; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; @@ -38,7 +40,10 @@ import java.util.Locale; import java.util.concurrent.Callable; import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.Media; import fr.free.nrw.commons.PageTitle; +import fr.free.nrw.commons.category.CategoryImageUtils; +import fr.free.nrw.commons.category.QueryContinue; import fr.free.nrw.commons.notification.Notification; import fr.free.nrw.commons.notification.NotificationUtils; import in.yuvi.http.fluent.Http; @@ -46,6 +51,8 @@ import io.reactivex.Observable; import io.reactivex.Single; import timber.log.Timber; +import static fr.free.nrw.commons.utils.ContinueUtils.getQueryContinue; + /** * @author Addshore */ @@ -56,9 +63,15 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { private AbstractHttpClient httpClient; private MWApi api; private Context context; - private SharedPreferences sharedPreferences; + private SharedPreferences defaultPreferences; + private SharedPreferences categoryPreferences; + private Gson gson; - public ApacheHttpClientMediaWikiApi(Context context, String apiURL, SharedPreferences sharedPreferences) { + public ApacheHttpClientMediaWikiApi(Context context, + String apiURL, + SharedPreferences defaultPreferences, + SharedPreferences categoryPreferences, + Gson gson) { this.context = context; BasicHttpParams params = new BasicHttpParams(); SchemeRegistry schemeRegistry = new SchemeRegistry(); @@ -69,7 +82,9 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent()); httpClient = new DefaultHttpClient(cm, params); api = new MWApi(apiURL, httpClient); - this.sharedPreferences = sharedPreferences; + this.defaultPreferences = defaultPreferences; + this.categoryPreferences = categoryPreferences; + this.gson = gson; } @Override @@ -160,7 +175,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } private void setAuthCookieOnLogin(boolean isLoggedIn) { - SharedPreferences.Editor editor = sharedPreferences.edit(); + SharedPreferences.Editor editor = defaultPreferences.edit(); if (isLoggedIn) { editor.putBoolean("isUserLoggedIn", true); editor.putString("getAuthCookie", api.getAuthCookie()); @@ -448,6 +463,81 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { return NotificationUtils.getNotificationsFromList(context, 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. + * Uses the query continue values for fetching paginated responses + * @param categoryName Category name as defined on commons + * @return + */ + @Override + @NonNull + public List getCategoryImages(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("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.getMediaList(childNodes); + } + + /** + * 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 + * After fetching images a page of image for a particular category, shared prefs are updated with the latest QueryContinue Values + * @param keyword + * @param queryContinue + */ + private void setQueryContinueValues(String keyword, QueryContinue queryContinue) { + SharedPreferences.Editor editor = categoryPreferences.edit(); + editor.putString(keyword, gson.toJson(queryContinue)); + editor.apply(); + } + + /** + * Before making a paginated API call, this method is called to get the latest query continue values to be used + * @param keyword + * @return + */ + @Nullable + private QueryContinue getQueryContinueValues(String keyword) { + String queryContinueString = categoryPreferences.getString(keyword, null); + return gson.fromJson(queryContinueString, QueryContinue.class); + } + @Override public boolean existingFile(String fileSha1) throws IOException { return api.action("query") 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 fd213455d..c0bd2fd87 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 @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import fr.free.nrw.commons.Media; import fr.free.nrw.commons.notification.Notification; import io.reactivex.Observable; import io.reactivex.Single; @@ -34,6 +35,8 @@ public interface MediaWikiApi { boolean logEvents(LogBuilder[] logBuilders); + List getCategoryImages(String categoryName); + @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 acd9b7646..4a7322b57 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 @@ -23,12 +23,11 @@ import fr.free.nrw.commons.AboutActivity; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.contributions.ContributionsActivity; -import fr.free.nrw.commons.featured.FeaturedImagesActivity; +import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.settings.SettingsActivity; @@ -37,6 +36,8 @@ import timber.log.Timber; public abstract class NavigationBaseActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener { + private static final String FEATURED_IMAGES_CATEGORY = "Category:Featured_pictures_on_Wikimedia_Commons"; + @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.navigation_view) @@ -157,7 +158,7 @@ public abstract class NavigationBaseActivity extends BaseActivity return true; case R.id.action_featured_images: drawerLayout.closeDrawer(navigationView); - startActivityWithFlags(this, FeaturedImagesActivity.class, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_featured_images), FEATURED_IMAGES_CATEGORY); return true; default: Timber.e("Unknown option [%s] selected from the navigation menu", itemId); diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java new file mode 100644 index 000000000..b05c8bc45 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java @@ -0,0 +1,15 @@ +package fr.free.nrw.commons.utils; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import fr.free.nrw.commons.category.QueryContinue; + +public class ContinueUtils { + + public static QueryContinue getQueryContinue(Node document) { + Element continueElement = (Element) document; + return new QueryContinue(continueElement.getAttribute("continue"), + continueElement.getAttribute("gcmcontinue")); + } +} diff --git a/app/src/main/res/layout/activity_featured_images.xml b/app/src/main/res/layout/activity_category_images.xml similarity index 69% rename from app/src/main/res/layout/activity_featured_images.xml rename to app/src/main/res/layout/activity_category_images.xml index 755fe4983..c329e4458 100644 --- a/app/src/main/res/layout/activity_featured_images.xml +++ b/app/src/main/res/layout/activity_category_images.xml @@ -1,6 +1,5 @@ - - diff --git a/app/src/main/res/layout/fragment_featured_images.xml b/app/src/main/res/layout/fragment_category_images.xml similarity index 70% rename from app/src/main/res/layout/fragment_featured_images.xml rename to app/src/main/res/layout/fragment_category_images.xml index ca45f44c3..001f0a780 100644 --- a/app/src/main/res/layout/fragment_featured_images.xml +++ b/app/src/main/res/layout/fragment_category_images.xml @@ -1,20 +1,21 @@ - + android:background="?attr/mainBackground"> @@ -33,7 +33,7 @@ android:padding="@dimen/small_gap" > Proceed Cancel Retry + + No images found! + Error occurred while loading images. + Uploaded by: %1$s Share App 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 73760dd40..b1de29143 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -3,6 +3,7 @@ package fr.free.nrw.commons 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 @@ -36,6 +37,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu 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() @@ -45,6 +47,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu val mockDbOpenHelper: DBOpenHelper = mock() val nearbyPlaces: NearbyPlaces = mock() val lruCache: LruCache = mock() + val gson: Gson = Gson() override fun providesAccountUtil(context: Context): AccountUtil = accountUtil @@ -58,7 +61,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager - override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences): MediaWikiApi = mediaWikiApi + override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences, categorySharedPreferences: SharedPreferences, gson: Gson): MediaWikiApi = mediaWikiApi override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt index c51d354c2..686a90ef2 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt @@ -3,6 +3,7 @@ package fr.free.nrw.commons.mwapi import android.content.SharedPreferences import android.os.Build import android.preference.PreferenceManager +import com.google.gson.Gson import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.TestCommonsApplication import okhttp3.mockwebserver.MockResponse @@ -26,12 +27,14 @@ class ApacheHttpClientMediaWikiApiTest { private lateinit var testObject: ApacheHttpClientMediaWikiApi private lateinit var server: MockWebServer private lateinit var sharedPreferences: SharedPreferences + private lateinit var categoryPreferences: SharedPreferences @Before fun setUp() { server = MockWebServer() sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) - testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences) + categoryPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) + testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences, categoryPreferences, Gson()) testObject.setWikiMediaToolforgeUrl("http://" + server.hostName + ":" + server.port + "/") } From f28cc6fc8ce9c900ba92c94f2d821d18110dedb2 Mon Sep 17 00:00:00 2001 From: Josephine Lim Date: Mon, 7 May 2018 19:27:14 +1000 Subject: [PATCH 031/134] Update pull_request_template.md (#1476) * Update pull_request_template.md * Remove Javadocs mention * Added required/optional notes --- PULL_REQUEST_TEMPLATE.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 34078f07e..9d7150008 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,15 @@ -## Description +## Description (required) -Fixes #{GitHub issue number} +Fixes #{GitHub issue number and title} {Describe the changes made and why they were made.} -## Tests performed +## Tests performed (required) Tested on {API level & name of device/emulator}, with {build variant, e.g. ProdDebug}. -{Please test your PR at least once before submitting.} - -## Screenshots showing what changed +## Screenshots showing what changed (optional) {Only for user interface changes, otherwise remove this section. See [how to take a screenshot](https://android.stackexchange.com/questions/1759/how-to-take-a-screenshot-with-an-android-device)} + +_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._ From 539c03bf047f039ae501d00262cdef3b8b028c6f Mon Sep 17 00:00:00 2001 From: Man Parvesh Singh Randhawa Date: Mon, 7 May 2018 17:32:11 +0800 Subject: [PATCH 032/134] resolves #1464 : MediaDataExtractor is making inefficient (redundant) server calls (#1496) --- app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java index 2d79a6c4f..affb57528 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java @@ -61,8 +61,8 @@ public class MediaDataExtractor { } try{ - Timber.d("Nominated for deletion: " + mediaWikiApi.pageExists("Commons:Deletion_requests/"+filename)); - deletionStatus = mediaWikiApi.pageExists("Commons:Deletion_requests/"+filename); + deletionStatus = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename); + Timber.d("Nominated for deletion: " + deletionStatus); } catch (Exception e){ Timber.d(e.getMessage()); From aca3f0f832370e1859892c4c89604595fcd84ccd Mon Sep 17 00:00:00 2001 From: Tanvi Dadu Date: Mon, 7 May 2018 15:27:59 +0530 Subject: [PATCH 033/134] Open map of place where picture was taken (#1360) * Intent to map added * Merge conflicts resolved * Added the functionality to hide FAB incase of null coordinate * Merge Conflict resolved * Improve pr quality * Improve Quality * Added nested FAB animations * Nested FAB implemented * Improve Quality * Added up arrow * Javadocs Added --- .../nrw/commons/upload/ShareActivity.java | 105 +++++++++++++++++- .../ic_keyboard_arrow_up_black_24dp.xml | 5 + app/src/main/res/layout/activity_share.xml | 27 ++++- app/src/main/res/values/dimens.xml | 2 + app/src/main/res/values/strings.xml | 2 + 5 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml 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 77f76fbed..6a59c8e30 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 @@ -81,8 +81,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; - - +import android.support.design.widget.FloatingActionButton; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; import static java.lang.Long.min; @@ -122,6 +121,7 @@ public class ShareActivity private Uri mediaUri; private Contribution contribution; private SimpleDraweeView backgroundImageView; + private FloatingActionButton maps_fragment; private boolean cacheFound; @@ -145,6 +145,8 @@ public class ShareActivity private long ShortAnimationDuration; private FloatingActionButton zoomInButton; private FloatingActionButton zoomOutButton; + private FloatingActionButton mainFab; + private boolean isFABOpen = false; /** @@ -284,6 +286,24 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } + + mainFab = (FloatingActionButton) findViewById(R.id.main_fab); + /* + * called when upper arrow floating button + */ + mainFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(!isFABOpen){ + showFABMenu(); + }else{ + closeFABMenu(); + } + } + }); + + + zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @@ -358,7 +378,74 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + maps_fragment.setVisibility(View.VISIBLE); + if( imageObj == null || imageObj.imageCoordsExists != true){ + maps_fragment.setVisibility(View.INVISIBLE); + } + + + maps_fragment.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( imageObj != null && imageObj.imageCoordsExists == true) { + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); + mapIntent.setPackage("com.google.android.apps.maps"); + startActivity(mapIntent); + } + } + }); } + /* + * Function to display the zoom and map FAB + */ + private void showFABMenu(){ + isFABOpen=true; + + if( imageObj != null && imageObj.imageCoordsExists == true) + maps_fragment.setVisibility(View.VISIBLE); + zoomInButton.setVisibility(View.VISIBLE); + + mainFab.animate().rotationBy(180); + maps_fragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); + zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); + } + + /* + * function to close the zoom and map FAB + */ + private void closeFABMenu(){ + isFABOpen=false; + mainFab.animate().rotationBy(-180); + maps_fragment.animate().translationY(0); + zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + + } + + @Override + public void onAnimationEnd(Animator animator) { + if(!isFABOpen){ + maps_fragment.setVisibility(View.GONE); + zoomInButton.setVisibility(View.GONE); + } + + } + + @Override + public void onAnimationCancel(Animator animator) { + + } + + @Override + public void onAnimationRepeat(Animator animator) { + + } + }); + } + @Override public void onRequestPermissionsResult(int requestCode, @@ -461,6 +548,9 @@ public class ShareActivity detectUnwantedPicturesAsync.execute(); } + /* + * to display permission snackbar in share activity + */ private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) { @@ -693,7 +783,9 @@ public class ShareActivity return super.onOptionsItemSelected(item); } - // Get SHA1 of file from input stream + /* + * Get SHA1 of file from input stream + */ private String getSHA1(InputStream is) { MessageDigest digest; @@ -730,6 +822,9 @@ public class ShareActivity } } + /* + * function to provide pinch zoom + */ private void zoomImageFromThumb(final View thumbView, Uri imageuri ) { // If there's an animation in progress, cancel it // immediately and proceed with this one. @@ -737,6 +832,8 @@ public class ShareActivity CurrentAnimator.cancel(); } ViewUtil.hideKeyboard(ShareActivity.this.findViewById(R.id.titleEdit | R.id.descEdit)); + closeFABMenu(); + mainFab.setVisibility(View.GONE); InputStream input = null; Bitmap scaled = null; try { @@ -866,7 +963,7 @@ public class ShareActivity CurrentAnimator.cancel(); } zoomOutButton.setVisibility(View.GONE); - zoomInButton.setVisibility(View.VISIBLE); + mainFab.setVisibility(View.VISIBLE); // Animate the four positioning/sizing properties in parallel, // back to their original values. diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml new file mode 100644 index 000000000..bc010396b --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml index ca8097495..b6e523239 100644 --- a/app/src/main/res/layout/activity_share.xml +++ b/app/src/main/res/layout/activity_share.xml @@ -41,8 +41,6 @@ - - + + + + + + + 20sp 16sp 14sp + 15dp + 25dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3e83fefd1..00bfafdad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -275,4 +275,6 @@ Error occurred while loading images. Uploaded by: %1$s Share App + Coordinates were not specified during image selection + From 8eed630ee57036dcd07ced2fba98dee8c9a5e857 Mon Sep 17 00:00:00 2001 From: neslihanturan Date: Mon, 7 May 2018 15:05:20 +0300 Subject: [PATCH 034/134] Add nearby tutorial (#1467) * Add dependency for MaterialShowcase * Add actionview class to get a reference to material showcase * Create a NearbyMaterialShowcaseSequence class * Apply sequence steps * Add first three steps of nearby showcase * Add sequence id constants to make sure they will be displayed only once * Add last step of sequence to explain plus fab * Create an object to prevent customize all sequences every time * Fix typo * Code cleanup * Add strings to strings.xml * Code cleanup * Revert irrelevant change * Revert irrelevant change * Remove showcaseview for recenter button * Use single showcaseView instead of sequence * Add single showcase view insted of sequence to be able to edit text style * Make sure it will be displayed only once * Cleanup * Update strings * Change dismiss text style --- app/build.gradle | 2 + .../nrw/commons/nearby/NearbyActivity.java | 91 ++++++++++++++++++- .../nrw/commons/nearby/NearbyMapFragment.java | 32 ++++++- .../NearbyMaterialShowcaseSequence.java | 18 ++++ .../fr/free/nrw/commons/utils/ViewUtil.java | 4 + app/src/main/res/values/strings.xml | 6 ++ 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java diff --git a/app/build.gradle b/app/build.gradle index 9c6f62fd4..5d37f8f54 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,8 @@ dependencies { transitive=true } + implementation "com.github.deano2390:MaterialShowcaseView:1.2.0" + implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION" implementation "com.android.support:appcompat-v7:$SUPPORT_LIB_VERSION" implementation "com.android.support:design:$SUPPORT_LIB_VERSION" diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 2a423de93..3bf8beacf 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -4,14 +4,18 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.Typeface; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; + import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -25,6 +29,7 @@ import com.google.gson.GsonBuilder; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import butterknife.BindView; import butterknife.ButterKnife; @@ -41,6 +46,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; +import uk.co.deanwild.materialshowcaseview.IShowcaseListener; +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener { @@ -56,12 +63,15 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp LinearLayout bottomSheetDetails; @BindView(R.id.transparentView) View transparentView; + @BindView(R.id.fab_recenter) + View fabRecenter; @Inject LocationServiceManager locationManager; @Inject NearbyController nearbyController; - + @Inject + @Named("application_preferences") SharedPreferences applicationPrefs; private LatLng curLatLng; private Bundle bundle; private Disposable placesDisposable; @@ -72,11 +82,18 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp private NearbyListFragment nearbyListFragment; private static final String TAG_RETAINED_MAP_FRAGMENT = NearbyMapFragment.class.getSimpleName(); private static final String TAG_RETAINED_LIST_FRAGMENT = NearbyListFragment.class.getSimpleName(); + private View listButton; // Reference to list button to use in tutorial private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; private BroadcastReceiver broadcastReceiver; + + private boolean isListShowcaseAdded = false; + private boolean isMapShowCaseAdded = false; + private LatLng lastKnownLocation; + private MaterialShowcaseView secondSingleShowCaseView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -126,6 +143,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_nearby, menu); + new Handler().post(() -> { + + listButton = findViewById(R.id.action_display_list); + + secondSingleShowCaseView = new MaterialShowcaseView.Builder(this) + .setTarget(listButton) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_list_icon)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_1) // provide a unique ID used to ensure it is only shown once + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .setListener(new IShowcaseListener() { + @Override + public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) { + + } + + // If dismissed, we can inform fragment to start showcase sequence there + @Override + public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) { + nearbyMapFragment.onNearbyMaterialShowcaseDismissed(); + } + }) + .build(); + + isListShowcaseAdded = true; + + if (isMapShowCaseAdded) { // If map showcase is also ready, start ShowcaseSequence + // Probably this case is not possible. Just added to be careful + setMapViewTutorialShowCase(); + } + }); + return super.onCreateOptionsMenu(menu); } @@ -420,6 +470,45 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp updateMapFragment(false); updateListFragment(); } + + isMapShowCaseAdded = true; + } + + public void setMapViewTutorialShowCase() { + /* + *This showcase view will be the first step of our nearbyMaterialShowcaseSequence. The reason we use a + * single item instead of adding another step to nearbyMaterialShowcaseSequence is that we are not able to + * call withoutShape() method on steps. For mapView we need an showcase view without + * any circle on it, it should cover the whole page. + * */ + MaterialShowcaseView firstSingleShowCaseView = new MaterialShowcaseView.Builder(this) + .setTarget(nearbyMapFragment.mapView) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_whole_nearby_activity)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_2) // provide a unique ID used to ensure it is only shown once + .withoutShape() // no shape on map view since there are no view to focus on + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .setListener(new IShowcaseListener() { + @Override + public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) { + + } + + @Override + public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) { + /* Add other nearbyMaterialShowcaseSequence here, it will make the user feel as they are a + * nearbyMaterialShowcaseSequence whole together. + * */ + secondSingleShowCaseView.show(NearbyActivity.this); + } + }) + .build(); + + if (applicationPrefs.getBoolean("firstRunNearby", true)) { + applicationPrefs.edit().putBoolean("firstRunNearby", false).apply(); + firstSingleShowCaseView.show(this); + } } private void lockNearbyView(boolean lock) { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java index 431132436..69041d286 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java @@ -7,6 +7,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Color; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; @@ -58,13 +59,14 @@ import fr.free.nrw.commons.contributions.ContributionController; import fr.free.nrw.commons.utils.UriDeserializer; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; import static android.app.Activity.RESULT_OK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class NearbyMapFragment extends DaggerFragment { - private MapView mapView; + public MapView mapView; private List baseMarkerOptions; private fr.free.nrw.commons.location.LatLng curLatLng; public fr.free.nrw.commons.location.LatLng[] boundaryCoordinates; @@ -111,6 +113,10 @@ public class NearbyMapFragment extends DaggerFragment { private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.06; private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.04; + private boolean isSecondMaterialShowcaseDismissed; + private boolean isMapReady; + private MaterialShowcaseView thirdSingleShowCaseView; + private Bundle bundleForUpdtes;// Carry information from activity about changed nearby places and current location @Inject @@ -163,7 +169,6 @@ public class NearbyMapFragment extends DaggerFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Timber.d("onCreateView called"); if (curLatLng != null) { Timber.d("curLatLng found, setting up map view..."); @@ -476,6 +481,7 @@ public class NearbyMapFragment extends DaggerFragment { mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(MapboxMap mapboxMap) { + ((NearbyActivity)getActivity()).setMapViewTutorialShowCase(); NearbyMapFragment.this.mapboxMap = mapboxMap; updateMapSignificantly(); } @@ -519,6 +525,7 @@ public class NearbyMapFragment extends DaggerFragment { private void addNearbyMarkerstoMapBoxMap() { mapboxMap.addMarkers(baseMarkerOptions); + mapboxMap.setOnInfoWindowCloseListener(marker -> { if (marker == selected) { bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); @@ -534,6 +541,7 @@ public class NearbyMapFragment extends DaggerFragment { }); mapboxMap.setOnMarkerClickListener(marker -> { + if (marker instanceof NearbyMarker) { this.selected = marker; NearbyMarker nearbyMarker = (NearbyMarker) marker; @@ -541,6 +549,7 @@ public class NearbyMapFragment extends DaggerFragment { passInfoToSheet(place); bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } return false; }); @@ -634,7 +643,19 @@ public class NearbyMapFragment extends DaggerFragment { addAnchorToSmallFABs(fabGallery, getActivity().findViewById(R.id.empty_view).getId()); addAnchorToSmallFABs(fabCamera, getActivity().findViewById(R.id.empty_view1).getId()); + thirdSingleShowCaseView = new MaterialShowcaseView.Builder(this.getActivity()) + .setTarget(fabPlus) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_plus_fab)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_3) // provide a unique ID used to ensure it is only shown once + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .build(); + isMapReady = true; + if (isSecondMaterialShowcaseDismissed) { + thirdSingleShowCaseView.show(getActivity()); + } } @@ -791,6 +812,13 @@ public class NearbyMapFragment extends DaggerFragment { this.bundleForUpdtes = bundleForUpdtes; } + public void onNearbyMaterialShowcaseDismissed() { + isSecondMaterialShowcaseDismissed = true; + if (isMapReady) { + thirdSingleShowCaseView.show(getActivity()); + } + } + @Override public void onStart() { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java new file mode 100644 index 000000000..c6e46611d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java @@ -0,0 +1,18 @@ +package fr.free.nrw.commons.nearby; + +import android.app.Activity; + +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseSequence; +import uk.co.deanwild.materialshowcaseview.ShowcaseConfig; + + +public class NearbyMaterialShowcaseSequence extends MaterialShowcaseSequence { + + public NearbyMaterialShowcaseSequence(Activity activity, String sequenceID) { + super(activity, sequenceID); + ShowcaseConfig config = new ShowcaseConfig(); + config.setDelay(500); // half second between each showcase view + this.setConfig(config); + this.singleUse(sequenceID); // Display tutorial only once + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java index 1c45e8178..0c22a40a2 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java @@ -10,6 +10,10 @@ import android.widget.Toast; public class ViewUtil { + public static final String SHOWCASE_VIEW_ID_1 = "SHOWCASE_VIEW_ID_1"; + public static final String SHOWCASE_VIEW_ID_2 = "SHOWCASE_VIEW_ID_2"; + public static final String SHOWCASE_VIEW_ID_3 = "SHOWCASE_VIEW_ID_3"; + public static void showSnackbar(View view, int messageResourceId) { Snackbar.make(view, messageResourceId, Snackbar.LENGTH_SHORT).show(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00bfafdad..dcea51bcc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -271,9 +271,15 @@ 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 + No images found! Error occurred while loading images. Uploaded by: %1$s + Share App Coordinates were not specified during image selection From 1ae28e37c32da3aa553d1c36655210fd30f1f98d Mon Sep 17 00:00:00 2001 From: Kaartic Sivaraam Date: Tue, 8 May 2018 06:34:09 +0000 Subject: [PATCH 035/134] CONTRIBUTING: fix formatting of the gist of the guidelines (#1453) * CONTRIBUTING: fix formatting of the gist of the guidelines First level headings for a gist seems to be overkill. So, replace first level headings with an ordered-list which sounds more meaningful. * CONTRIBUTING: specify clearly that 'blame' is a feature of "Git" The contributing file specifies about the ability to know who wrote something without the need of @author javadoc tags but incorrectly attributes the feature to GitHub. Correctly attribute the feature to where it belongs, Git, and specify the name of the feature to help users easily take advantage of it. --- CONTRIBUTING.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a8e7af19..caa02a103 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,23 +5,30 @@ If you're not sure where to start head on to [this wiki page](https://github.com Here's a gist of the guidelines, -# Make separate commits for logically separate changes +1. Make separate commits for logically separate changes -# Describe your changes well in the commit message +1. Describe your changes well in the commit message -The first line of the commit message should be a short description of what has + The first line of the commit message should be a short description of what has changed. It is also good to prefix the first line with "area: " where the "area" is a filename or identifier for the general area of the code being modified. The body should provide a meaningful commit message. -# Write Javadocs +1. Write Javadocs -We require contributors to include Javadocs for all new methods and classes submitted via PRs (after 1 May 2018). This is aimed at making it easier for new contributors to dive into our codebase, especially those who are new to Android development. A few things to note: + We require contributors to include Javadocs for all new methods and classes + submitted via PRs (after 1 May 2018). This is aimed at making it easier for + new contributors to dive into our codebase, especially those who are new to + Android development. A few things to note: -- This should not replace the need for code that is easily-readable in and of itself -- Please make sure that your Javadocs are reasonably descriptive, not just a copy of the method name -- Please do not use `@author` tags - we aim for collective code ownership, and if needed, GitHub allows us to see who wrote something without needing to add these tags + - This should not replace the need for code that is easily-readable in + and of itself + - Please make sure that your Javadocs are reasonably descriptive, not just + a copy of the method name + - Please do not use `@author` tags - we aim for collective code ownership, + and if needed, Git allows us to see who wrote something without needing + to add these tags (`git blame`) -# Write tests for your code (if possible) +1. Write tests for your code (if possible) -# Make sure the Wiki pages don't become stale by updating them (if needed) +1. Make sure the Wiki pages don't become stale by updating them (if needed) From cd212b7daa96960c5aa70fbd7b0088a40897b345 Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Tue, 8 May 2018 12:21:13 +0530 Subject: [PATCH 036/134] Feature/switch to butterknife (#1494) * Implemented butterknife in MediaDetailFragment [issue #1491] * Implemented butterknife in MediaDetailPagerFragment [[issue #1491]] * post merge upstream master wip [[issue #1491]] --- .../commons/media/MediaDetailFragment.java | 200 ++++++++++-------- .../media/MediaDetailPagerFragment.java | 7 +- 2 files changed, 112 insertions(+), 95 deletions(-) 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 c3a977ab5..9614c4f00 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 @@ -9,6 +9,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.text.Editable; +import android.text.TextUtils; import android.text.TextWatcher; import android.util.TypedValue; import android.view.LayoutInflater; @@ -22,6 +23,9 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -45,7 +49,8 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.ui.widget.CompatTextView; import timber.log.Timber; -import static android.view.View.*; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static android.widget.Toast.LENGTH_SHORT; public class MediaDetailFragment extends CommonsDaggerSupportFragment { @@ -75,23 +80,37 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; - - private MediaWikiImageView image; - private MediaDetailSpacer spacer; private int initialListTop = 0; - private TextView title; - private TextView desc; - private TextView author; - private TextView license; - private TextView coordinates; - private TextView uploadedDate; - private TextView seeMore; - private LinearLayout nominatedforDeletion; - private LinearLayout categoryContainer; - private LinearLayout authorLayout; - private Button delete; - private ScrollView scrollView; + @BindView(R.id.mediaDetailImage) + MediaWikiImageView image; + @BindView(R.id.mediaDetailSpacer) + MediaDetailSpacer spacer; + @BindView(R.id.mediaDetailTitle) + TextView title; + @BindView(R.id.mediaDetailDesc) + TextView desc; + @BindView(R.id.mediaDetailAuthor) + TextView author; + @BindView(R.id.mediaDetailLicense) + TextView license; + @BindView(R.id.mediaDetailCoordinates) + TextView coordinates; + @BindView(R.id.mediaDetailuploadeddate) + TextView uploadedDate; + @BindView(R.id.seeMore) + TextView seeMore; + @BindView(R.id.nominatedDeletionBanner) + LinearLayout nominatedForDeletion; + @BindView(R.id.mediaDetailCategoryContainer) + LinearLayout categoryContainer; + @BindView(R.id.authorLinearLayout) + LinearLayout authorLayout; + @BindView(R.id.nominateDeletion) + Button delete; + @BindView(R.id.mediaDetailScrollView) + ScrollView scrollView; + private ArrayList categoryNames; private boolean categoriesLoaded = false; private boolean categoriesPresent = false; @@ -101,6 +120,9 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private AsyncTask detailFetchTask; private LicenseList licenseList; + //Had to make this class variable, to implement various onClicks, which access the media, also I fell why make separate variables when one can serve the purpose + private Media media; + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -137,22 +159,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { final View view = inflater.inflate(R.layout.fragment_media_detail, container, false); - image = (MediaWikiImageView) view.findViewById(R.id.mediaDetailImage); - scrollView = (ScrollView) view.findViewById(R.id.mediaDetailScrollView); - - // Detail consists of a list view with main pane in header view, plus category list. - spacer = (MediaDetailSpacer) view.findViewById(R.id.mediaDetailSpacer); - title = (TextView) view.findViewById(R.id.mediaDetailTitle); - desc = (TextView) view.findViewById(R.id.mediaDetailDesc); - author = (TextView) view.findViewById(R.id.mediaDetailAuthor); - license = (TextView) view.findViewById(R.id.mediaDetailLicense); - coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates); - uploadedDate = (TextView) view.findViewById(R.id.mediaDetailuploadeddate); - seeMore = (TextView) view.findViewById(R.id.seeMore); - nominatedforDeletion = (LinearLayout) view.findViewById(R.id.nominatedDeletionBanner); - delete = (Button) view.findViewById(R.id.nominateDeletion); - categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer); - authorLayout = (LinearLayout) view.findViewById(R.id.authorLinearLayout); + ButterKnife.bind(this,view); if (isFeaturedMedia){ authorLayout.setVisibility(VISIBLE); @@ -196,7 +203,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { @Override public void onResume() { super.onResume(); - Media media = detailProvider.getMediaAtPosition(index); + media = detailProvider.getMediaAtPosition(index); if (media == null) { // Ask the detail provider to ping us when we're ready Timber.d("MediaDetailFragment not yet ready to display details; registering observer"); @@ -209,17 +216,18 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { Timber.d("MediaDetailFragment ready to display delayed details!"); detailProvider.unregisterDataSetObserver(dataObserver); dataObserver = null; - displayMediaDetails(detailProvider.getMediaAtPosition(index)); + media=detailProvider.getMediaAtPosition(index); + displayMediaDetails(); } }; detailProvider.registerDataSetObserver(dataObserver); } else { Timber.d("MediaDetailFragment ready to display details"); - displayMediaDetails(media); + displayMediaDetails(); } } - private void displayMediaDetails(final Media media) { + private void displayMediaDetails() { //Always load image from Internet to allow viewing the desc, license, and cats image.setMedia(media); @@ -256,7 +264,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (success) { extractor.fill(media); setTextFields(media); - setOnClickListeners(media); } else { Timber.d("Failed to load photo details."); } @@ -316,74 +323,81 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { checkDeletion(media); } - private void setOnClickListeners(final Media media) { - if (licenseLink(media) != null) { - license.setOnClickListener(v -> openWebBrowser(licenseLink(media))); + @OnClick(R.id.mediaDetailLicense) + public void onMediaDetailLicenceClicked(){ + if (!TextUtils.isEmpty(licenseLink(media))) { + openWebBrowser(licenseLink(media)); } else { if(isFeaturedMedia) { - Timber.d("Unable to fetch license URL for %s", media.getLicense()); + Timber.d("Unable to fetch license URL for %s", media.getLicense()); } else { Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); toast.show(); } } + } + + @OnClick(R.id.mediaDetailCoordinates) + public void onMediaDetailCoordinatesClicked(){ if (media.getCoordinates() != null) { - coordinates.setOnClickListener(v -> openMap(media.getCoordinates())); + openMap(media.getCoordinates()); } - if (delete.getVisibility() == VISIBLE) { - enableDeleteButton(true); + } - delete.setOnClickListener(v -> { + @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) { + // enableDeleteButton(true); makes sense ? + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setMessage("Why should this file be deleted?"); + final EditText input = new EditText(getActivity()); + alert.setView(input); + input.requestFocus(); + alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String reason = input.getText().toString(); + DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason); + deleteTask.execute(); + enableDeleteButton(false); + } + }); + alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + } + }); + AlertDialog d = alert.create(); + input.addTextChangedListener(new TextWatcher() { + private void handleText() { + final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE); + if (input.getText().length() == 0) { + okButton.setEnabled(false); + } else { + okButton.setEnabled(true); + } + } - AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); - alert.setMessage("Why should this file be deleted?"); - final EditText input = new EditText(getActivity()); - alert.setView(input); - input.requestFocus(); - alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - String reason = input.getText().toString(); - DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason); - deleteTask.execute(); - enableDeleteButton(false); - } - }); - alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - } - }); - AlertDialog d = alert.create(); - input.addTextChangedListener(new TextWatcher() { - private void handleText() { - final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE); - if (input.getText().length() == 0) { - okButton.setEnabled(false); - } else { - okButton.setEnabled(true); - } - } + @Override + public void afterTextChanged(Editable arg0) { + handleText(); + } - @Override - public void afterTextChanged(Editable arg0) { - handleText(); - } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + d.show(); + d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - }); - d.show(); - d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - }); - } - if (nominatedforDeletion.getVisibility() == VISIBLE){ - seeMore.setOnClickListener(v -> { - openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); - }); + @OnClick(R.id.seeMore) + public void onSeeMoreClicked(){ + if(nominatedForDeletion.getVisibility()== VISIBLE) { + openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); } } @@ -488,11 +502,11 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private void checkDeletion(Media media){ if (media.getRequestedDeletion()){ delete.setVisibility(GONE); - nominatedforDeletion.setVisibility(VISIBLE); + nominatedForDeletion.setVisibility(VISIBLE); } else{ delete.setVisibility(VISIBLE); - nominatedforDeletion.setVisibility(GONE); + nominatedForDeletion.setVisibility(GONE); } } 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 c0564c603..62d1261cf 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 @@ -26,6 +26,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; import javax.inject.Inject; import javax.inject.Named; @@ -53,7 +55,8 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple @Named("default_preferences") SharedPreferences prefs; - private ViewPager pager; + @BindView(R.id.mediaDetailsPager) + ViewPager pager; private Boolean editable; private boolean isFeaturedImage; @@ -72,7 +75,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false); - pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager); + ButterKnife.bind(this,view); pager.addOnPageChangeListener(this); final MediaDetailAdapter adapter = new MediaDetailAdapter(getChildFragmentManager()); From d012572b926afe709f08cefaf341c7fb55926c30 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 10 May 2018 10:11:14 +0200 Subject: [PATCH 037/134] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ar/strings.xml | 1 + app/src/main/res/values-bn/strings.xml | 3 + app/src/main/res/values-de/strings.xml | 8 +++ app/src/main/res/values-diq/strings.xml | 8 +++ app/src/main/res/values-el/strings.xml | 8 +++ app/src/main/res/values-es/strings.xml | 8 +++ app/src/main/res/values-eu/strings.xml | 17 +++-- app/src/main/res/values-fr/strings.xml | 9 +++ app/src/main/res/values-it/strings.xml | 7 +++ app/src/main/res/values-iw/strings.xml | 8 +++ app/src/main/res/values-ja/strings.xml | 73 +++++++++++++++++++--- app/src/main/res/values-ko/strings.xml | 8 +++ app/src/main/res/values-lb/strings.xml | 4 ++ app/src/main/res/values-lv/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 8 +++ app/src/main/res/values-pl/strings.xml | 2 + app/src/main/res/values-pms/strings.xml | 8 +++ app/src/main/res/values-pt-rBR/strings.xml | 8 +++ app/src/main/res/values-pt/strings.xml | 19 ++++-- app/src/main/res/values-ru/strings.xml | 9 +++ app/src/main/res/values-skr/strings.xml | 1 + app/src/main/res/values-sv/strings.xml | 20 ++++-- app/src/main/res/values-zh-rTW/strings.xml | 10 ++- app/src/main/res/values-zh/strings.xml | 8 +++ 24 files changed, 231 insertions(+), 25 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 6d9c94c1d..a9127c1eb 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -145,4 +145,5 @@ مرحبا بكم في ويكيمديا كومنز، %1$s! نحن سعداء لأنك هنا. %1$s رسالة على صفحة الحديث %1$s ذكر لك على %2$s. + شارك التطبيق diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 679c50803..e7167393c 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -259,4 +259,7 @@ ইন্টারনেট উপলব্ধ কোন বিজ্ঞপ্তি পাওয়া যায়নি পুনঃচেষ্টা করুন + বুঝেছি! + কোন চিত্র পাওয়া যায়নি! + আপলোড করেছেন: %1$s diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 36e013976..5f751153c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -269,5 +269,13 @@ Fortfahren Abbrechen Erneut versuchen + Verstanden! + Dies sind die Orte in deiner Nähe, die Bilder zur Illustration ihrer Wikipedia-Artikel benötigen. + Das Antippen dieser Schaltfläche zeigt eine Liste mit diesen Orten + Du kannst ein Bild für einen beliebigen Ort von deiner Galerie oder Kamera hochladen + Keine Bilder gefunden! + Beim Laden der Bilder ist ein Fehler aufgetreten. + Hochgeladen von: %1$s App teilen + Während der Bildauswahl wurden keine Koordinaten angegeben diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 2b66709f0..1f1d69ea3 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -9,11 +9,15 @@ * Mirzali --> + Asayış + Bıngeh + Lokasyon Commons Eyari Namey karberi Parola Cı kewe + Parola, xo vira kerde? Qeyd be Cıkewtış Kerem kerên, bıpawên... @@ -58,6 +62,8 @@ Bar ke Kategoriyan dı cı geyr Star ke + Newe ke + Liste \@string/contributions_subtitle_zero Yew barbiyayış @@ -129,5 +135,7 @@ Keye Bar ke Veciyayış + Bıtexelne Anciya bıcerrebne + Mı fehm kerd! diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0dae5c65e..ee50aa3d7 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -273,5 +273,13 @@ Συνέχεια Ακύρωση Ξαναπροσπαθήστε + Κατάλαβα! + Αυτά είναι τα μέρη κοντά σας που χρειάζονται φωτογραφίες για να εικονογραφηθούν τα λήμματά τους στη Βικιπαίδεια + Πατώντας αυτό το κουμπί φέρνει μια λίστα αυτών των μερών + Μπορείτε να ανεβάσετε μια εικόνα για οποιοδήποτε μέρος από την γκαλερί ή την κάμερά σας + Δεν βρέθηκαν εικόνες! + Συνέβη σφάλμα κατά το ανέβασμα των εικόνων. + Ανέβηκε από: %1$s Κοινοποίηση εφαρμογής + Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c1a45b309..69cf2f8b7 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -267,4 +267,12 @@ Selecciona el idioma en que quieres enviar traducciones Cancelar Reintentar + Entendido + Estos sitios cercanos a ti necesitan imágenes para ilustrar sus artículos de Wikipedia + Puedes cargar una imagen para cualquier sitio desde la galería o la cámara + No se encontró ninguna imagen. + Se produjo un error al cargar las imágenes. + Cargada por: %1$s + Compartir aplicación + No se especificaron las coordenadas al seleccionar la imagen diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index f44262e05..a109ff541 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -8,11 +8,17 @@ * Theklan --> + Itxura + Orokorra + Feedback + Kokapena Commons + Hobespenak Erabiltzaile izena Pasahitza Saioa hasi + Pasahitza ahaztu duzu? Eman izena Saioa hasten Mesedez itxaron… @@ -54,22 +60,23 @@ Kategoriak bilatu Gorde Eguneratu + Zerrenda GPSa gaitu Oraindik ez da ezer igo - - Oraindik igoerarik ez + + \@string/contributions_subtitle_zero igoera 1 %1$d igoera Ez da kategoriak aukritu %1$s izenarekin - Gehitu kategoriak zure argazkiak Wikimedia Commonsen aurkitzen errazagoak izan daitezen. + Gehitu kategoriak zure argazkiak Wikimedia Commonsen aurkitzen errazagoak izan daitezen.\nHasi idazten kategoriak gehitzeko. Kategoriak Hobespenak Eman izena Honi buruz Open Source softwarea <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache v2 Lizentziaren</a> pean egina. Wikimedia Commons eta bere logoa Wikimedia Fundazioaren marka erregistratuak dira eta Wikimedia Fundazioaren baimenarekin erabiltzen dira. Ez gaude Wikimedia Fundaziora afiliatuta. GitHub-eko <a href=\"https://github.com/commons-app/apps-android-commons\">Iturria</a> eta <a href=\"https://commons-app.github.io/\">webgunea</a>. <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-eko gai</a> berria sortu erroreen berri emateko. - <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Pribatutasun politika</a> + <u>Pribatutasun politika</u> Honi buruz Bidali zure iritzia (e-posta bidez) Posta bezerorik ez da instalatu @@ -81,7 +88,7 @@ Irudi hau %1$s lizentziapean egongo da Irudi hau bidaltzen, nire lan propioa dela aitortzen dut, copyrighta duten materiala edo selfiak ez duela, eta beste motatakoak <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Ohikoaren arauak</a> Jaitsi - Lizentzia + Berezko lizentzia Aurreko izenburu/deskribapena erabili Oraingo kokapena automatikoki lortu Gau modua diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 442fc6bc5..6dd08a30d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -5,6 +5,7 @@ * Happy13241 * Jean-Frédéric * KATRINE1992 +* Melissadeba95 * Metroitendo * Nicolas Raoul * Orikrin1998 @@ -279,5 +280,13 @@ Continuer Annuler Réessayer + C’est bon ! + Il y a des lieux autour de vous qui demandent des images pour illustrer leurs articles Wikipédia + En cliquant sur ce bouton vous afficherez une liste de ces endroits + Vous pouvez téléverser une photo de n\'importe quel endroit de votre gallerie ou de votre appareil photo + Aucune images trouvée. + Une erreur s\'est produite pendant le chargement des images. + Importé par:%1$s Partager les applications + Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 306a1e3a4..06bfa701f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -223,4 +223,11 @@ Lingue Annulla Riprova + Capito! + Questi sono i luoghi vicino a te che necessitano di immagini per illustrare le loro voci di Wikipedia + Puoi caricare un\'immagine per ogni luogo dalla tua galleria o fotocamera + Nessuna immagine trovata! + Si è verificato un errore durante il caricamento delle immagini. + Caricato da: %1$s + Condividi applicazione diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 762a3be31..f0cdf1f3d 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -273,5 +273,13 @@ המשך ביטול לנסות שוב + הבנתי! + אלה המקומות בסביבתך שזקוקים לתמונות כדי להמחיש את הערכים שלהם בוויקיפדיה + ניתן ללחוץ על כפתור זה כדי להציג רשימה של המקומות האלה + באפשרותך להעלות תמונה של כל מקום מהגלריה או מהמצלמה שלך + לא נמצאו תמונות! + אירעה שגיאה בטעינת התמונות. + הועלתה על־ידי: %1$s שיתוף היישום + לא צוינו קואורדינטות בעת בחירת התמונה diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e34c98432..ef45a5d3a 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -12,13 +12,18 @@ * Yusuke1109 --> + 表示 + 全般 フィードバック 場所 コモンズ + 設定 利用者名 パスワード + コモンズのベータ版アカウントにログイン ログイン + パスワードを忘れた場合 利用者登録 ログイン中 お待ちください… @@ -49,6 +54,7 @@ 共有 ブラウザーで表示 タイトル + ファイル名をつけてください 説明 ログインできません - ネットワークのエラーです ログインできません - 利用者名を確認してください @@ -64,6 +70,7 @@ カテゴリを検索 保存 更新 + 一覧 お使いのデバイスではGPSが無効になっています。有効にしますか? GPSを有効にする まだ何もアップロードされていません。 @@ -82,6 +89,7 @@ カテゴリ 設定 利用者登録 + 秀逸な画像 このアプリについて ウィキメディア・コモンズ・アプリはウィキメディア・コミュニティの助成金受給者とボランティアによって製作・メンテナンスされているオープンソースソフトウェアです。ウィキメディア財団はこのアプリの製作・開発・メンテナンスに関与していません。 バグとアイディアは <a href=\"https://github.com/commons-app/apps-android-commons/issues\">Github</a> へ。 @@ -96,6 +104,7 @@ 再試行 キャンセル この画像が %1$s ライセンスでアップロードされます。 + この画像の投稿に当たり、私はこれが自分自身の作品であり、著作権のあるコンテンツや自撮りは含まれていないと宣言します。 ダウンロード 既定のライセンス 前回のタイトルと記述を使用 @@ -126,11 +135,19 @@ ウィキメディア・コモンズにはウィキペディアで使用する画像のほぼすべてが保管されています。 あなたの画像は世界中の人々が学習する助けになります! アップロードする画像はあなたご本人が撮影したものかあなたが単独で制作したものに限定します。 - - 自然物 (動植物、山)\n- 道具 (自転車、駅)\n- 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) + 自然物 (動植物、山)\n• 道具 (自転車、駅)\n• 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) + 自然物 (動植物、山) + 道具 (自転車、駅) + 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) アップロードが《禁止》のもの: - あなたの友人の自撮り写真や画像\n- インターネットからダウンロードした画像\n- 著作権のあるアプリのスクリーンショット + 自撮りもしくは友達を撮影した写真 + ウェブからダウンロードした画像 + 独自のアプリケーションのスクリーンショット アップロードの例: - 題名: シドニー・オペラハウス\n- 説明: 湾の向こうから見たシドニー・オペラハウス\n- カテゴリ: 西側から見たシドニー・オペラハウス、遠くから見たシドニー・オペラハウス + 題名: シドニーのオペラハウス + 説明: シドニーのオペラハウス。湾を挟んで撮影。 画像を投稿してください。ウィキペディアの記事に彩りを! ウィキペディアの画像はウィキメディア・コモンズに保管されています。 あなたの画像は世界中の人々が学習する助けになります @@ -143,11 +160,11 @@ 説明はありません。 不明なライセンス 更新 - 必要な権限:外部ストレージを読み込みます。これがなければアプリは機能しません。 - 必要な権限:外部ストレージを作成します。これがなければアプリは機能しません。 - オプションの権限:カテゴリ候補の現在の位置を取得する + 必要な権限:外部ストレージを読み込みます。これがなければアプリはギャラリーを開けません。 + 必要な権限:外部ストレージに入力します。これがないとアプリはカメラにアクセスできません。 + オプションの権限:カテゴリ候補のため現在の位置を取得する 承認 - 周りの場所 + 近くの場所 付近の場所が見つかりません 警告 このファイルが既にコモンズにあります。本当にアップロードしますか? @@ -158,6 +175,7 @@ 記述 ここにメディアの説明が入ります。かなり長文になる場合には数行にわたることがあります。それでも見栄えがよいと願っています。 作者 + 秀逸な画像の作者名を記入します。 アップロード日時 ライセンス 緯度経度 @@ -174,14 +192,19 @@ コモンズの商標 コモンズのウェブサイト コモンズのフェイスブックページ + コモンズのGithubソースコード 背景画像 + メディアイメージが失敗しました 画像がありません 画像をアップロード 蔵王連峰 リャマ レインボーブリッジ チューリップ + 自撮りはアップロードできません + 独自の著作権がある画像 ウィキペディアへようこそ + 著作権について シドニーオペラハウス キャンセル 開く @@ -195,28 +218,62 @@ ログアウト チュートリアル 通知 - 場所の権限がないと、近くの場所を表示できません + 秀逸 + 場所の権限がないため、近くの場所を表示できません 説明がありません + コモンズのファイルページ ウィキデータ項目 ウィキペディアの記事 画像をキャッシュする際のエラー ファイル固有の説明的な表題。ファイル名として使われます。平易な言葉を使い、空白を入れることができます。拡張子は含めないでください。 可能な限りメディアを説明してください:どこで撮られましたか?それは何を示していますか?文脈とは何ですか?物や人を説明してください。容易に推測できない情報、例えば風景の場合の時刻を明らかにする。メディアに珍しいことがある場合は、何が珍しいのかを説明してください。 - 権限を取得 + この画像は暗すぎますがアップロードしますか? ウィキメディア・コモンズは百科事典に適した画像のみ受け付けます。 + ピントが合っていませんが、アップロードしますか? ウィキメディア・コモンズは百科事典に適した画像のみ受け付けます。 + 権限を付与 外部ストレージを使用 アプリ内のカメラで撮影した写真を端末に保存する 自分のアカウントにログイン ログファイルを送信する メールで開発者にログファイルを送信する + URLを開くブラウザーが見つかりません + エラーが発生しました。URL が見つかりません + 削除の提案 + この画像の削除が提案されています。 ブラウザーで表示 場所は変更されていません。 位置が無効です。 + 近くの場所を表示するには権限が必要です + 道順を調べる 記事を読む + ウィキメディアコモンズにようこそ、%1$さん! このサイトへ来てくれてありがとうございます。 + %1$さんからアナタのとーくぺ^字にメッセージが届いています + 編集をしてくれてありがとうございます + %1$さんが%2$であなたに言及しています。 + 表示の切り替え + 道順 + ウィキデータ + ウィキペディア + コモンズ <u>評価する</u> <u>FAQ</u> チュートリアルをスキップする + インターネットに接続していません + インターネットに接続しました + 通知の取得に失敗しました + 通知はありません <u>翻訳</u> 言語 + どの言語に編集するか選択 + 次へ キャンセル - 再試行 + やり直す + 了解 + 近くでウィキペディアの記事に使う写真がない場所はこちら + このボタンをタップするとリストを表示します + 場所の写真をアップロードするには、ギャラリーから選ぶことも撮影することもできます + 画像がありません + 画像の読み込み中にエラーが発生しました + アップロードした人: %1$ + アプリをシェアする + 画像の選択中に位置情報を特定できませんでした diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d0e1c21ed..e80999d54 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -266,5 +266,13 @@ 진행 취소 다시 시도 + 알겠습니다! + 이들은 위키백과 글에 사진을 넣을 필요가 있는 당신 주위의 장소들입니다 + 이 버튼을 탭하면 이 장소들의 목록을 가져옵니다 + 갤러리나 카메라 어느 곳이든 사진을 올릴 수 있습니다 + 그림이 없습니다! + 그림을 불러오는 동안 오류가 발생했습니다. + 올린이: %1$s 앱 공유 + 그림 선택 중에 좌표가 지정되지 않았습니다 diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index 5abca6562..d346b90bd 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -240,4 +240,8 @@ Virufueren Ofbriechen Nach eng Kéier probéieren + Verstanen! + Keng Biller fonnt! + Feeler beim Eropluede vu Biller. + Eropgeluede vum: %1$s diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 1eba570d2..b6c5103e2 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -109,4 +109,5 @@ Valodas Turpināt Atcelt + Sapratu! diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 4e44dad60..492e1bc0c 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -263,5 +263,13 @@ Продолжи Откажи Пробај пак + Јасно! + Ова се места во ваша близинана кои им требаат слики за илустрирање на нивните статии на Википедија + Ако допрете на копчево ќе добиете список на тие места + Можете да подигнете слика за било кое од местата од вашата галерија или камера + Не пронајдов ниедна слика! + Се појави грешка при вчитувањето на сликите. + Подигач: %1$s Сподели прилог + Не беа укажани координати при изборот на сликата diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 77cdb547d..ccc5f374f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -227,4 +227,6 @@ Nie znaleziono powiadomień Języki Anuluj + Nie znaleziono grafik! + Wystąpił błąd podczas ładowania grafik. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 30115f1ad..53ce91cc5 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -263,5 +263,13 @@ Andé anans Anulé Prové torna + Fàit! + A-i é dij pòst davzin a chiel ch\'a l\'han da manca ëd plance për ilustré ij sò artìcoj su Wikipedia + Sgnacand su \'s boton a comparirà na lista ëd si pòst + A peul carié na fòto da \'n pòst qualsëssìa ëd soa galarìa o màchina fòto + Gnun-e plance trovà! + A-i é staje n\'eror durant ël cariament ëd le plance. + Carià da: %1$s Partagé j\'aplicassion + Le coordinà a son nen ëstàite spessificà durant la selession ëd la plancia diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 6722ae4bb..23b5e4abf 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -275,5 +275,13 @@ Avançar Cancelar Tentar novamente + Entendido! + Estes são os lugares perto de você que precisam de fotografias para ilustrar os respetivos artigos na Wikipédia + Tocar neste botão fará surgir uma lista destes lugares + Pode carregar uma fotografia para qualquer dos lugares, da sua galeria ou câmara + Não foi encontrada nenhuma imagem! + Ocorreu um erro durante o carregamento das imagens. + Carregada por: %1$s Compartilhar o aplicativo + Não foram especificadas coordenadas durante a seleção da imagem diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index b0717f3dc..3da1296b7 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -16,12 +16,12 @@ Geral Comentários Localização - Wikimedia Commons + Commons Configurações - Nome de utilizador(a) + Nome de utilizador Palavra-passe - Entre com a sua conta do Commons Beta + Entre com a sua conta da wiki Commons Beta Iniciar sessão Esqueceu-se da palavra-passe? Registar-se @@ -118,7 +118,7 @@ Utilizar tema escuro Atribuição-CompartilhaIgual 4.0 Atribuição 4.0 - Atribuição – Compartilhamento pela mesma Licença + Atribuição–CompartilhaIgual 3.0 Atribuição 3.0 CC0 CC BY-SA 3.0 @@ -136,7 +136,7 @@ CC-BY-SA 4.0 CC BY 4.0 CC Zero - Wikimedia Commons armazena a maioria das imagens que são usadas na Wikipédia. + A wiki Wikimedia Commons aloja a maioria das imagens que são usadas na Wikipédia. As suas imagens ajudam a educar pessoas em todo o mundo! Por favor, carregue apenas imagens tiradas ou criadas exclusivamente por si: Objetos naturais (flores, animais, montanhas)\n• Objetos úteis (bicicletas, estações de comboio)\n• Pessoas famosas (o seu presidente da câmara, atletas olímpicos que conheça) @@ -274,4 +274,13 @@ Avançar Cancelar Tentar novamente + Entendido! + Estes são os lugares perto de si que precisam de fotografias para ilustrar os respetivos artigos na Wikipédia + Tocar neste botão fará surgir uma lista destes lugares + Pode carregar uma fotografia para qualquer dos lugares, da sua galeria ou câmara + Não foi encontrada nenhuma imagem! + Ocorreu um erro durante o carregamento das imagens. + Carregada por: %1$s + Partilhar aplicação + Não foram especificadas coordenadas durante a seleção da imagem diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4dc90489d..ce71af94b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -285,4 +285,13 @@ Выполняется Отмена Повторить + Понятно! + Это места поблизости, статьи о которых нуждаются в иллюстрациях + Нажатие этой кнопки сгенерирует список таких мест + Вы можете загрузить изображение для любого из этих мест, сделав снимок камерой или выбрав уже существующее изображение из галереи + Изображений не найдено! + Произошла ошибка при загрузке изображений. + Загружено участником %1$s + Поделиться приложением + Во время выбора изображения не были указаны координаты diff --git a/app/src/main/res/values-skr/strings.xml b/app/src/main/res/values-skr/strings.xml index 796ae8555..73a5b7e51 100644 --- a/app/src/main/res/values-skr/strings.xml +++ b/app/src/main/res/values-skr/strings.xml @@ -129,4 +129,5 @@ اڳوں تے تھیوو منسوخ ولدا کوشش کرو + گھن گھندا diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 24ef60b61..17a324a97 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -90,9 +90,9 @@ Kategorier Inställningar Registrera - Utvalda bild + Utvalda bilder Om - Wikimedia Commons är en app med öppen källkod som skapas och underhålls av frivilliga från Wikimedias gemenskap. Wikimedia Foundation är inte involverad i skapandet, utvecklingen eller underhållet av appen. + Wikimedia Commons-appen är en app med öppen källkod som skapas och underhålls av frivilliga från Wikimedias gemenskap. Wikimedia Foundation är inte involverad i skapandet, utvecklingen eller underhållet av appen. Skapa ett nytt <a href=\"https://github.com/commons-app/apps-android-commons/issues\">ärende på GitHub</a> för att rapportera buggar och förslag. <u>Integritetspolicy</u> <u>Erkännande</u> @@ -105,7 +105,7 @@ Försök igen Avbryt Denna bild kommer att licensieras under %1$s - Genom att skicka in denna bild intygar jag att detta är mitt eget verk, som inte innehåller upphovsrättsskyddat material eller selfies samt annars följer <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons-policys</a>. + Genom att skicka in denna bild intygar jag att detta är mitt eget verk, som inte innehåller upphovsrättsskyddat material eller selfies samt följer <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons-policys</a>. Ladda ned Standardlicens Använd föregående titel/beskrivning @@ -135,7 +135,7 @@ CC Zero Wikimedia Commons lagrar de flesta bilderna som används på Wikipedia. Dina bilder hjälper till att utbilda\nmänniskor runt hela världen! - Ladda upp bilder som endast du har tagit eller skapat: + Ladda endast upp bilder som du har tagit eller skapat själv: Naturliga föremål (blommor, djur, berg)\n• Användbara föremål (cyklar, tågstationer)\n• Berömda personer (din borgmästare, olympiska atleter du har träffat) Naturliga föremål (blommor, djur, berg) Användbar föremål (cyklar, tågstationer) @@ -163,8 +163,8 @@ Ingen beskrivning Okänd licens Uppdatera - Nödvändig behörighet: Läsa extern lagring. Appen kan inte komma åt ditt galleri utan detta. - Nödvändig behörighet: Skriva till extern lagring. Appen kan inte komma åt din kamera utan detta. + Nödvändig behörighet: Läs extern lagring. Appen kan inte komma åt ditt galleri utan detta. + Nödvändig behörighet: Skriv till extern lagring. Appen kan inte komma åt din kamera utan detta. Valfri behörighet: Hämta aktuell plats för kategoriförslag OK Platser i närheten @@ -271,5 +271,13 @@ Fortsätt Avbryt Försök igen + Uppfattat! + Detta är platserna nära dig som behöver bilder för att illustrera deras Wikipedia-artiklar + Klicka på den här knappen för att få upp en lista med dessa platser + Du kan ladda upp en bild från vilken plats som helst från ditt galleri eller kamera + Inga bilder hittades! + Ett fel uppstod vid inläsning av bilder. + Uppladdad av: %1$s Dela app + Koordinater specificerades inte vid bildvalet diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a2878cddf..80b595f5d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -32,7 +32,7 @@ 未能核對身分! 開始上傳! 已上傳%1$s! - 點選檢視您上傳的項目 + 輕觸來檢視您上傳的項目 開始上傳%1$s 正在上傳%1$s 即將完成上傳 %1$s @@ -271,5 +271,13 @@ 已進行 取消 重試 + 了解! + 這些是在您的附近,並且需要圖片來圖解有關它們的維基百科條目之地點 + 輕觸此按鈕來帶出這些地點的清單 + 您可從您的圖庫或相機,來上傳任何地點的圖片 + 找不到圖片! + 當載入圖片時發生錯誤。 + 由:%1$s 上傳 分享應用程式 + 當選擇圖片時未指定座標 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d3d049ea2..29a1ae898 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -269,5 +269,13 @@ 已处理 取消 重试 + 明白了! + 这些是您附近需要图片以阐明维基百科条目的地方 + 点按此按钮会出现这些地点的列表 + 您可以从您的图库或照相机中上传任意地点的图片 + 找不到图片! + 加载图片时出错。 + 由%1$s上传 分享应用 + 图片选择时,坐标并未指定 From 8bdc4f6b95a33ece82d68136446faa9c02d93a0a Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Sat, 12 May 2018 16:49:43 +0530 Subject: [PATCH 038/134] Bug fix #1504 (#1506) * Bug fix #1504 * Filtered messages with ConnectException [issue #1504] * A generalised message for exceptions in Nearby Activity [issue #1504] --- .../nrw/commons/nearby/NearbyActivity.java | 29 ++++++++++++++++--- .../nrw/commons/nearby/NearbyController.java | 3 +- .../free/nrw/commons/nearby/NearbyPlaces.java | 10 +------ app/src/main/res/values/strings.xml | 1 + 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 3bf8beacf..04886fda4 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -16,6 +16,7 @@ import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; +import android.text.TextUtils; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -23,9 +24,14 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.ProgressBar; +import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.reactivex.functions.Consumer; +import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; import java.util.List; import javax.inject.Inject; @@ -427,8 +433,14 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp .loadAttractionsFromLocation(curLatLng)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::populatePlaces); - } else if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { + .subscribe(this::populatePlaces, + throwable -> { + Timber.d(throwable); + showErrorMessage(getString(R.string.error_fetching_nearby_places)); + progressBar.setVisibility(View.GONE); + }); + } else if (locationChangeType + .equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { Gson gson = new GsonBuilder() .registerTypeAdapter(Uri.class, new UriSerializer()) .create(); @@ -451,7 +463,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp if (placeList.size() == 0) { ViewUtil.showSnackbar(findViewById(R.id.container), R.string.no_nearby); } - + bundle.putString("PlaceList", gsonPlaceList); //bundle.putString("CurLatLng", gsonCurLatLng); bundle.putString("BoundaryCoord", gsonBoundaryCoordinates); @@ -580,7 +592,12 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp .loadAttractionsFromLocation(curLatLng)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::populatePlaces); + .subscribe(this::populatePlaces, + throwable -> { + Timber.d(throwable); + showErrorMessage(getString(R.string.error_fetching_nearby_places)); + progressBar.setVisibility(View.GONE); + }); nearbyMapFragment.setBundleForUpdtes(bundle); nearbyMapFragment.updateMapSignificantly(); updateListFragment(); @@ -646,4 +663,8 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp public void prepareViewsForSheetPosition(int bottomSheetState) { // TODO } + + private void showErrorMessage(String message) { + ViewUtil.showLongToast(NearbyActivity.this, message); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java index 015d22135..bd042b4d7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java @@ -7,6 +7,7 @@ import android.support.graphics.drawable.VectorDrawableCompat; import com.mapbox.mapboxsdk.annotations.IconFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -44,7 +45,7 @@ public class NearbyController { * @return NearbyPlacesInfo a variable holds Place list without distance information * and boundary coordinates of current Place List */ - public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) { + public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) throws IOException { Timber.d("Loading attractions near %s", curLatLng); NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index a2f4b2352..d05d81251 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -40,10 +40,9 @@ public class NearbyPlaces { } } - List getFromWikidataQuery(LatLng curLatLng, String lang) { + List getFromWikidataQuery(LatLng curLatLng, String lang) throws IOException { List places = Collections.emptyList(); - try { // increase the radius gradually to find a satisfactory number of nearby places while (radius <= MAX_RADIUS) { places = getFromWikidataQuery(curLatLng, lang, radius); @@ -54,13 +53,6 @@ public class NearbyPlaces { radius *= RADIUS_MULTIPLIER; } } - } catch (IOException e) { - Timber.d(e.toString()); - // errors tend to be caused by too many results (and time out) - // try a small radius next time - Timber.d("back to initial radius: %f", radius); - radius = INITIAL_RADIUS; - } // make sure we will be able to send at least one request next time if (radius > MAX_RADIUS) { radius = MAX_RADIUS; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dcea51bcc..6e30baa10 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -282,5 +282,6 @@ Share App Coordinates were not specified during image selection + Error fetching nearby places. From 2b867f0f9b22f802e56efe184468307669bda421 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 14 May 2018 08:13:34 +0200 Subject: [PATCH 039/134] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-bn/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-iw/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 1 + app/src/main/res/values-pms/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-qq/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 9 +++++++++ app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + 15 files changed, 23 insertions(+) diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index e7167393c..7c76ebaea 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -262,4 +262,5 @@ বুঝেছি! কোন চিত্র পাওয়া যায়নি! আপলোড করেছেন: %1$s + কাছাকাছি স্থানগুলি আনতে ত্রুটি। diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5f751153c..9f47ce42e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -278,4 +278,5 @@ Hochgeladen von: %1$s App teilen Während der Bildauswahl wurden keine Koordinaten angegeben + Fehler beim Abrufen der Orte in der Nähe. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ee50aa3d7..e372df836 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -282,4 +282,5 @@ Ανέβηκε από: %1$s Κοινοποίηση εφαρμογής Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας + Σφάλμα κατά την εύρεση κοντινών μερών. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 69cf2f8b7..9e5dae650 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -275,4 +275,5 @@ Cargada por: %1$s Compartir aplicación No se especificaron las coordenadas al seleccionar la imagen + Error al recuperar los lugares cercanos. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6dd08a30d..bfb14baf1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -289,4 +289,5 @@ Importé par:%1$s Partager les applications Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image + Erreur durant l\'exploration du voisinage. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index f0cdf1f3d..9ba67e4ee 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -282,4 +282,5 @@ הועלתה על־ידי: %1$s שיתוף היישום לא צוינו קואורדינטות בעת בחירת התמונה + שגיאה באחזור המקומות בסביבתך. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e80999d54..883665c85 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -275,4 +275,5 @@ 올린이: %1$s 앱 공유 그림 선택 중에 좌표가 지정되지 않았습니다 + 주변 장소를 가져오는데 오류가 있습니다. diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 492e1bc0c..a344fcad2 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -272,4 +272,5 @@ Подигач: %1$s Сподели прилог Не беа укажани координати при изборот на сликата + Грешка при добивањето на околните места. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 53ce91cc5..8b2bd292c 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -272,4 +272,5 @@ Carià da: %1$s Partagé j\'aplicassion Le coordinà a son nen ëstàite spessificà durant la selession ëd la plancia + Eror durant l\'esplorassion dj\'anviron. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 23b5e4abf..849c4e6b1 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -284,4 +284,5 @@ Carregada por: %1$s Compartilhar o aplicativo Não foram especificadas coordenadas durante a seleção da imagem + Erro ao buscar lugares próximos. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 3da1296b7..e59582fa0 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -283,4 +283,5 @@ Carregada por: %1$s Partilhar aplicação Não foram especificadas coordenadas durante a seleção da imagem + Erro ao localizar locais próximos. diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index 37c2978bf..d84ce08ab 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -99,6 +99,7 @@ Message explaining what kind of images not to submit. Message asking user if they understand what kinds of images to upload. Button text for confirming the user understands what kinds of images to upload.\n{{Identical|Yes}} + \'\'This message is empty, and it\'s probably invalid. See bug report: https://github.com/commons-app/apps-android-commons/issues/1333 .\'\' Label for categories list in media detail panel.\n{{Identical|Category}} Placeholder for categories list in media detail panel, while loading from network.\n{{Identical|Loading}} Placeholder for categories list in media detail panel, if no categories found.\n{{Identical|None selected}} diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 95548b662..dadd40917 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -271,4 +271,13 @@ İlerle Vazgeç Tekrar Deneyin + Anladım! + Vikipedi maddelerine eklemek için fotoğrafa ihtiyaç duyan size yakın yerler + Bu tuşa dokunmak bu yerlerin bir listesini getirir + Galerinizden veya kameranızla herhangi bir yer için resim yükleyebilirsiniz. + Resim bulunamadı! + Resimler yüklenirken hata oluştu. + Yükleyen: %1$s + Uygulamayı Paylaş + Koordinatlar görüntü seçimi sırasında belirlenmedi diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 80b595f5d..4581d45ce 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -280,4 +280,5 @@ 由:%1$s 上傳 分享應用程式 當選擇圖片時未指定座標 + 索取附近地點時出錯。 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 29a1ae898..2079eb925 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -278,4 +278,5 @@ 由%1$s上传 分享应用 图片选择时,坐标并未指定 + 检索附近地点时出错。 From f99363c06c8d9ea000a90421000c3760a9982892 Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Tue, 15 May 2018 12:55:37 +0530 Subject: [PATCH 040/134] Fix security exception crash while accessing network location provider (#1498) * Fix security exception crash while accessing network location provider * Added java docs --- .../location/LocationServiceManager.java | 33 ++++++++++++---- .../nrw/commons/nearby/NearbyActivity.java | 39 +++++++++++++++++-- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java index 73ded852f..49c422633 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.location; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; @@ -10,9 +11,10 @@ import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.util.Log; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import timber.log.Timber; @@ -29,6 +31,7 @@ public class LocationServiceManager implements LocationListener { private Location lastLocation; private final List locationListeners = new CopyOnWriteArrayList<>(); private boolean isLocationManagerRegistered = false; + private Set locationExplanationDisplayed = new HashSet<>(); /** * Constructs a new instance of LocationServiceManager. @@ -51,7 +54,6 @@ public class LocationServiceManager implements LocationListener { /** * Returns whether the location permission is granted. - * * @return true if the location permission is granted */ public boolean isLocationPermissionGranted() { @@ -73,10 +75,23 @@ public class LocationServiceManager implements LocationListener { LOCATION_REQUEST); } + /** + * The permission explanation dialog box is now displayed just once for a particular activity. We are subscribing + * to updates from multiple providers so its important to show the dialog just once. Otherwise it will be displayed + * once for every provider, which in our case currently is 2. + * @param activity + * @return + */ public boolean isPermissionExplanationRequired(Activity activity) { - return !activity.isFinishing() && - ActivityCompat.shouldShowRequestPermissionRationale(activity, - Manifest.permission.ACCESS_FINE_LOCATION); + if (activity.isFinishing()) { + return false; + } + boolean showRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION); + if (showRequestPermissionRationale && !locationExplanationDisplayed.contains(activity)) { + locationExplanationDisplayed.add(activity); + return true; + } + return false; } /** @@ -84,8 +99,9 @@ public class LocationServiceManager implements LocationListener { * (e.g. when Location permission just granted) * @return last known LatLng */ + @SuppressLint("MissingPermission") public LatLng getLKL() { - if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + if (isLocationPermissionGranted()) { Location lastKL = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (lastKL == null) { lastKL = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); @@ -107,9 +123,10 @@ public class LocationServiceManager implements LocationListener { * Registers a LocationManager to listen for current location. */ public void registerLocationManager() { - if (!isLocationManagerRegistered) + if (!isLocationManagerRegistered) { isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) && requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); + } } /** @@ -142,7 +159,7 @@ public class LocationServiceManager implements LocationListener { * @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly * LOCATION_SLIGHTLY_CHANGED if location changed slightly */ - protected LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { + private LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { if (currentBestLocation == null) { // A new location is always better than no location diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 04886fda4..35e15b0d9 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -322,7 +322,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp protected void onStart() { super.onStart(); locationManager.addLocationListener(this); - locationManager.registerLocationManager(); + registerLocationUpdates(); } @Override @@ -400,7 +400,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp return; } - locationManager.registerLocationManager(); + registerLocationUpdates(); LatLng lastLocation = locationManager.getLastLocation(); if (curLatLng != null && curLatLng.equals(lastLocation)) { //refresh view only if location has changed @@ -450,6 +450,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp } } + /** + * This method first checks if the location permissions has been granted and then register the location manager for updates. + */ + private void registerLocationUpdates() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (locationManager.isLocationPermissionGranted()) { + locationManager.registerLocationManager(); + } else { + // Should we show an explanation? + if (locationManager.isPermissionExplanationRequired(this)) { + new AlertDialog.Builder(this) + .setMessage(getString(R.string.location_permission_rationale_nearby)) + .setPositiveButton("OK", (dialog, which) -> { + requestLocationPermissions(); + dialog.dismiss(); + }) + .setNegativeButton("Cancel", (dialog, id) -> { + showLocationPermissionDeniedErrorDialog(); + dialog.cancel(); + }) + .create() + .show(); + + } else { + // No explanation needed, we can request the permission. + requestLocationPermissions(); + } + } + } else { + locationManager.registerLocationManager(); + } + } + private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) { List placeList = nearbyPlacesInfo.placeList; LatLng[] boundaryCoordinates = nearbyPlacesInfo.boundaryCoordinates; @@ -530,7 +563,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp locationManager.removeLocationListener(this); } else { lockNearbyView = false; - locationManager.registerLocationManager(); + registerLocationUpdates(); locationManager.addLocationListener(this); } } From 677f85a09703da359c33bd9189f339b1e5aed147 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 17 May 2018 08:24:27 +0200 Subject: [PATCH 041/134] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ast/strings.xml | 14 ++++++++++++++ app/src/main/res/values-diq/strings.xml | 8 ++++++-- app/src/main/res/values-hu/strings.xml | 12 ++++++++++-- app/src/main/res/values-is/strings.xml | 10 ++++++++++ app/src/main/res/values-ru/strings.xml | 19 ++++++++----------- app/src/main/res/values-sv/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 7 +++++++ 8 files changed, 57 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 532a4897e..947bc441a 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -84,6 +84,7 @@ Categoríes Configuración Date d\'alta + Imáxenes destacaes Tocante a La app de Wikimedia Commons ye software de códigu abiertu, creáu y calteníu por becaos y voluntarios de la comunidá de Wikimedia. La Fundación Wikimedia nun participa na creación, desendolcu nin caltenimientu de la app. Crea una nueva <a href=\"https://github.com/commons-app/apps-android-commons/issues\">incidencia en GitHub</a> pa informar de problemes y suxerencies. @@ -169,6 +170,8 @@ Títulu del mediu Descripción Equí va la descripción del mediu. Esto pué ser llargo enforma, y necesitará espardese per delles llinies. Sicasí, esperamos que se vea bien. + Autor + El nome d\'usuariu del autor de la imaxe destacada va equí. Data d\'unviu Llicencia Coordenaes @@ -211,6 +214,7 @@ Salir Tutorial Avisos + Destacada Los sitios cercanos nun pueden amosase ensin los permisos d\'allugamientu nun s\'atoparon descripciones Páxina del ficheru en Commons @@ -259,4 +263,14 @@ Siguir Encaboxar Retentar + Entendílo + Estos son sitios cercanos a ti que precisen imaxes para ilustrar los sos artículos de Wikipedia + Tocando esti botón amuésase la llista d\'esos llugares + Puedes xubir una imaxe pa cualquier sitiu dende la galería o la cámara + Nun s\'alcontró nenguna imaxe + Asocedió un error al cargar les imáxenes. + Xubida por: %1$s + Compartir app + Nun s\'especificaron les coordenaes al escoyer la imaxe + Error al llograr los llugares cercanos. diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 1f1d69ea3..53bd88bac 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -13,6 +13,7 @@ Bıngeh Lokasyon Commons + Eyari Namey karberi Parola @@ -84,7 +85,8 @@ Qeyd be Heq te cı Qandê yew <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-cıkewtış</a>ê neweyi rê rapor û teklifan bıaferne. - <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Politikay nımıtışi</a> + <u>Politikaya nımıtışi</u> + <u>İştırakkerdoği</u> Heq te cı Peyd rışten bırış (E-posta ra) E-posta eyar nêbi @@ -92,7 +94,7 @@ Anciya bıcerrebne Bıtexelne Ron - Lisans + Lisanso hesebiyaye Attribution-ShareAlike 3.0 Attribution 3.0 CC0 @@ -127,6 +129,8 @@ E Sername + Şınasnayış + Nuştekar Lisans Koordinati Korbıze diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 81e655b95..d9580dca1 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -162,8 +162,8 @@ Nincs leírás Ismeretlen licenc Frissítés - Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül. - Szükséges engedély: Külső tárhely írása. Az alkalmazás nem működik enélkül. + Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül. + Szükséges engedély: Külső tárhely írása. Az alkalmazás nem tudja használni a kamerát enélkül. Lehetséges engedély: Jelenlegi hely megszerzése, a kategóriajavaslatok lehetőségéért. OK Közeli helyek @@ -242,6 +242,7 @@ A hely nem változott. A hely nem érhető el. Közeli helyek listájának megtekintéséhez engedély szükséges + SZÓCIKK OLVASÁSA Üdvözlünk a Wikimedia Commonson, %1$s! Örülünk, hogy itt vagy. %1$s üzenetet hagyott a vitalapodon Köszönjük a szerkesztésedet! @@ -258,5 +259,12 @@ Folytatás Mégse Újra + 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 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-is/strings.xml b/app/src/main/res/values-is/strings.xml index ba5bdfe42..f1b020965 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -263,4 +263,14 @@ Halda áfram Hætta við Reyna aftur + Náði því! + Þetta eru þeir staðir í næsta nágrenni við þig sem vantar myndir til að skýra með Wikipedia-greinar + Ef ýtt er á þennan hnapp birtist listi yfir þessa staði + Þú getur sent inn mynd úr myndasafninu þínu eða myndavélinni + Engir myndir fundust! + Villa kom upp við að hlaða inn myndum. + Sent inn af: %1$s + Deila forriti + Hnit voru ekki tilgreind við val myndar + Villa við að sækja nálæga staði. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ce71af94b..0d5a0ee3e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -46,11 +46,7 @@ Завершение загрузки %1$s Загрузка %1$s не удалась Нажмите для просмотра - - %1$d файл загружается - %1$d файла загружается - %1$d файлов загружается - + %1$d {{PLURAL:%1$d|one=файл загружается|few=файла загружается|файлов загружается}} Мои недавние загрузки В очереди Ошибка загрузки. @@ -115,7 +111,7 @@ Почтовый клиент не установлен Недавно использованные категории Ожидание первой синхронизации… - Вы ещё не загрузили ни одной фотографии. + Вы ещё не загрузили ни одного изображения. Повторить Отмена Это изображение будет лицензировано под %1$s @@ -123,7 +119,7 @@ Скачать Лицензия по умолчанию Использовать предыдущие название/описание - Автоматически получить текущее местоположение + Анализ местоположения Получить текущее местоположение, чтобы были предложены категории, если изображение не содержит геотегов Ночной режим Использовать тёмную тему @@ -148,7 +144,7 @@ CC BY 4.0 CC Zero Викисклад содержит бо́льшую часть изображений, которые используются в Википедии. - Ваши изображения помогают образованию людей во всём мире! + Ваши изображения могут помочь образованию людей во всём мире! Пожалуйста, загрузите фотографии, которые были сняты или созданы исключительно вами: Природные объекты (например, цветы, животные, горы)\n• Полезные предметы (например, велосипеды, вокзалы)\n• Известные люди (например, ваш мэр, спортсмены-олимпийцы, которых вы встретили) Природные объекты (например, цветы, животные, горы) @@ -166,7 +162,7 @@ Категории: Sydney Opera House from the west, Sydney Opera House remote views Загрузите свои изображения. Помогите Википедии оживить статьи! Изображения в Википедии хранятся на Викискладе. - Ваши изображения помогают образованию людей во всём мире. + Ваши изображения могут помочь образованию людей во всём мире. Избегайте материалов, защищённых авторским правом, например, найденных в Интернете, изображений плакатов, книжных обложек и т.п. Вам это понятно? Да! @@ -211,7 +207,7 @@ Facebook-страница Commons Исходные коды Commons на гитхабе Фоновое изображение - Ошибка медиаизображения + Ошибка медиафайла Изображение не найдено Загрузить изображение Гора Зао @@ -246,7 +242,7 @@ Пожалуйста, подробно опишите загружаемый файл: где он был снят? что на нём изображено? каков его контекст? Пожалуйста опишите изображённых персон или объекты. Добавьте информацию, о которой нельзя легко догадаться, например, время суток, когда снимался файл. Если снято что-то необычное, постарайтесь пояснить, что именно в этом необычного. Это изображение слишком тёмное. Вы уверены, что хотите его загрузить? Викисклад подходит только для фотографий, имеющих энциклопедическую ценность. Это изображение размыто. Вы уверены, что хотите его загрузить? Викисклад подходит только для фотографий, имеющих энциклопедическую ценность. - Дать разрешение + Разрешить Использовать внешнее хранилище Сохранять изображения, сделанные с помощью встроенной камеры на устройстве Войдите в свою учётную запись @@ -294,4 +290,5 @@ Загружено участником %1$s Поделиться приложением Во время выбора изображения не были указаны координаты + Ошибка получения мест поблизости diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 17a324a97..1d2fc868e 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -280,4 +280,5 @@ Uppladdad av: %1$s Dela app Koordinater specificerades inte vid bildvalet + Fel uppstod när platser i närheten hämtades. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index dadd40917..c9dd60b41 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -280,4 +280,5 @@ Yükleyen: %1$s Uygulamayı Paylaş Koordinatlar görüntü seçimi sırasında belirlenmedi + Yakındaki yerler alınırken hata oluştu. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a4ed20320..1aba51e02 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -279,4 +279,11 @@ Виконується Скасувати Повторити + Зрозуміло + Натискання цієї кнопки згенерує список таких місць + Зображень не знайдено! + Сталася помилка при завантаженні зображень. + Завантажено: %1$s + Поділитися програмою + Помилка отримання місць поблизу. From 1520fc01f7b36b55a4f18275c00c9ddb9fc7af1f Mon Sep 17 00:00:00 2001 From: albendz <12453997+albendz@users.noreply.github.com> Date: Sat, 19 May 2018 09:15:43 -0700 Subject: [PATCH 042/134] Issue #1408: Try to get the localized version of the wikipedia article (#1445) * Try to get the localized version of the wikipedia article before defaulting to the English version. Tested with Spanish on physical Android device. Other notes: Difficulties building with gradle due to dexcount plugin: https://github.com/KeepSafe/dexcount-gradle-plugin/issues/234. In testing, disabled the plugin. * Update article fetch to not include unnecessary SERVICE line --- app/src/main/resources/queries/nearby_query.rq | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/resources/queries/nearby_query.rq b/app/src/main/resources/queries/nearby_query.rq index 0453005e8..b11f0985a 100644 --- a/app/src/main/resources/queries/nearby_query.rq +++ b/app/src/main/resources/queries/nearby_query.rq @@ -39,6 +39,11 @@ SELECT # Get emoji OPTIONAL { ?classId wdt:P487 ?emoji0. } OPTIONAL { ?classId wdt:P279*/wdt:P487 ?emoji1. } + + OPTIONAL { + ?wikipediaArticle schema:about ?item ; + schema:isPartOf . + } OPTIONAL { ?wikipediaArticle schema:about ?item ; schema:isPartOf . From 01cb9ccd704f288a7d6f9ab0bcf3adc4fb1b097a Mon Sep 17 00:00:00 2001 From: Elliott Eggleston Date: Sat, 19 May 2018 15:00:06 -0500 Subject: [PATCH 043/134] Wmhack2018 (#1536) * Add new activity to manifest * Create review activity layout base * Add a new menu item to drawer for peer review * Add a top menu with randomizer icon to review activity * Add strings for review button * Add activity to ActivityBuilderModule for injection * Add a new drawer item to start review acitivty * Create base of the Review Activity * Add fragment pager * Add new fragment for injection * Create a fragment pager layout * Wikimedia hackathon 2018 (#1533) * First draft of fn to get random recent image * Use log entries for requests to beta, try to connect refresh button FIXME: runs http request on main thread, breaks * Tweak button connection * Add ReviewController class * Fix fragments * Wmhack2018 (#1534) * tiny fixes * Load pictures into activities * Re-use same class for all review fragments (#1537) And try to add pager indicator * [WIP] category check * [WIP] add on-click actions to ReviewActivity * [WIP] add SendThankTask * Make it beautiful * Use standalone category extraction code in MediaDataExtractor * Add categories to category review page --- app/src/main/AndroidManifest.xml | 4 + .../free/nrw/commons/MediaDataExtractor.java | 18 +- .../free/nrw/commons/delete/DeleteTask.java | 44 ++--- .../nrw/commons/di/ActivityBuilderModule.java | 4 + .../di/CommonsApplicationComponent.java | 8 +- .../nrw/commons/di/FragmentBuilderModule.java | 4 + .../commons/media/MediaDetailFragment.java | 6 + .../media/RecentChangesImageUtils.java | 32 ++++ .../mwapi/ApacheHttpClientMediaWikiApi.java | 87 ++++++++- .../free/nrw/commons/mwapi/MediaWikiApi.java | 7 + .../nrw/commons/review/CheckCategoryTask.java | 130 +++++++++++++ .../nrw/commons/review/ReviewActivity.java | 173 ++++++++++++++++++ .../nrw/commons/review/ReviewController.java | 52 ++++++ .../commons/review/ReviewImageFragment.java | 92 ++++++++++ .../commons/review/ReviewPagerAdapter.java | 51 ++++++ .../nrw/commons/review/SendThankTask.java | 140 ++++++++++++++ .../commons/theme/NavigationBaseActivity.java | 6 + .../commons/utils/MediaDataExtractorUtil.java | 28 +++ .../main/res/drawable/ic_check_black_24dp.xml | 9 + .../res/drawable/ic_refresh_black_24dp.xml | 9 + .../res/drawable/tab_indicator_default.xml | 12 ++ .../res/drawable/tab_indicator_selected.xml | 8 + app/src/main/res/drawable/tab_selector.xml | 8 + app/src/main/res/layout/activity_review.xml | 65 +++++++ .../main/res/layout/fragment_review_image.xml | 89 +++++++++ app/src/main/res/menu/drawer.xml | 5 + .../main/res/menu/review_randomizer_menu.xml | 10 + app/src/main/res/values/strings.xml | 30 +++ 28 files changed, 1086 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/media/RecentChangesImageUtils.java create mode 100644 app/src/main/java/fr/free/nrw/commons/review/CheckCategoryTask.java create mode 100644 app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/review/ReviewController.java create mode 100644 app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java create mode 100644 app/src/main/java/fr/free/nrw/commons/review/ReviewPagerAdapter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/review/SendThankTask.java create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/MediaDataExtractorUtil.java create mode 100644 app/src/main/res/drawable/ic_check_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_refresh_black_24dp.xml create mode 100644 app/src/main/res/drawable/tab_indicator_default.xml create mode 100644 app/src/main/res/drawable/tab_indicator_selected.xml create mode 100644 app/src/main/res/drawable/tab_selector.xml create mode 100644 app/src/main/res/layout/activity_review.xml create mode 100644 app/src/main/res/layout/fragment_review_image.xml create mode 100644 app/src/main/res/menu/review_randomizer_menu.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17f6770d2..a4c944a2d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -96,6 +96,10 @@ android:label="@string/title_activity_featured_images" android:parentActivityName=".contributions.ContributionsActivity" /> + + { notificationBuilder = new NotificationCompat.Builder(context); Toast toast = new Toast(context); toast.setGravity(Gravity.CENTER,0,0); - toast = Toast.makeText(context,"Trying to nominate "+media.getDisplayTitle()+ " for deletion",Toast.LENGTH_SHORT); + toast = Toast.makeText(context,"Trying to nominate "+media.getDisplayTitle()+ " for deletion", Toast.LENGTH_SHORT); toast.show(); } @@ -64,7 +64,7 @@ public class DeleteTask extends AsyncTask { String editToken; String authCookie; - String summary = "Nominating " + media.getFilename() +" for deletion."; + String summary = context.getString(R.string.nominating_file_for_deletion, media.getFilename()); authCookie = sessionManager.getAuthCookie(); mwApi.setAuthCookie(authCookie); @@ -97,19 +97,19 @@ public class DeleteTask extends AsyncTask { publishProgress(1); mwApi.prependEdit(editToken,fileDeleteString+"\n", - media.getFilename(),summary); + media.getFilename(), summary); publishProgress(2); mwApi.edit(editToken,subpageString+"\n", - "Commons:Deletion_requests/"+media.getFilename(),summary); + "Commons:Deletion_requests/"+media.getFilename(), summary); publishProgress(3); mwApi.appendEdit(editToken,logPageString+"\n", - "Commons:Deletion_requests/"+date,summary); + "Commons:Deletion_requests/"+date, summary); publishProgress(4); mwApi.appendEdit(editToken,userPageString+"\n", - "User_Talk:"+sessionManager.getCurrentAccount().name,summary); + "User_Talk:"+sessionManager.getCurrentAccount().name, summary); publishProgress(5); } catch (Exception e) { @@ -123,29 +123,21 @@ public class DeleteTask extends AsyncTask { protected void onProgressUpdate (Integer... values){ super.onProgressUpdate(values); + int[] messages = new int[]{ + R.string.getting_edit_token, + R.string.nominate_for_deletion_edit_file_page, + R.string.nominate_for_deletion_create_deletion_request, + R.string.nominate_for_deletion_edit_deletion_request_log, + R.string.nominate_for_deletion_notify_user, + R.string.nominate_for_deletion_done + }; + String message = ""; - switch (values[0]){ - case 0: - message = "Getting token"; - break; - case 1: - message = "Adding delete message to file"; - break; - case 2: - message = "Creating Delete requests sub-page"; - break; - case 3: - message = "Adding file to Delete requests log"; - break; - case 4: - message = "Notifying User on Talk page"; - break; - case 5: - message = "Done"; - break; + if (0 < values[0] && values[0] < messages.length) { + message = context.getString(messages[values[0]]); } - notificationBuilder.setContentTitle("Nominating "+media.getDisplayTitle()+" for deletion") + notificationBuilder.setContentTitle(context.getString(R.string.nominating_file_for_deletion, media.getFilename())) .setStyle(new NotificationCompat.BigTextStyle() .bigText(message)) .setSmallIcon(R.drawable.ic_launcher) 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 51aa85903..e062dbcc9 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 @@ -10,6 +10,7 @@ import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.notification.NotificationActivity; +import fr.free.nrw.commons.review.ReviewActivity; import fr.free.nrw.commons.settings.SettingsActivity; import fr.free.nrw.commons.upload.MultipleShareActivity; import fr.free.nrw.commons.upload.ShareActivity; @@ -50,4 +51,7 @@ public abstract class ActivityBuilderModule { @ContributesAndroidInjector abstract CategoryImagesActivity bindFeaturedImagesActivity(); + + @ContributesAndroidInjector + abstract ReviewActivity bindReviewActivity(); } 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 91f6d4ccb..99ad9a346 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,11 +9,11 @@ import dagger.android.support.AndroidSupportInjectionModule; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.MediaWikiImageView; import fr.free.nrw.commons.auth.LoginActivity; -import fr.free.nrw.commons.contributions.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.review.CheckCategoryTask; +import fr.free.nrw.commons.review.SendThankTask; import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.nearby.PlaceRenderer; @@ -40,6 +40,10 @@ public interface CommonsApplicationComponent extends AndroidInjector getNotifications() { @@ -616,11 +653,59 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } private Date parseMWDate(String mwDate) { - SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC try { return isoFormat.parse(mwDate); } catch (ParseException e) { throw new RuntimeException(e); } } + + private String formatMWDate(Date date) { + return isoFormat.format(date); + } + + public Media getRecentRandomImage() throws IOException { + Media media = null; + int tries = 0; + Random r = new Random(); + + while (media == null && tries < MAX_RANDOM_TRIES) { + Date now = new Date(); + Date startDate = new Date(now.getTime() - r.nextInt(RANDOM_SECONDS) * 1000L); + ApiResult apiResult = null; + try { + MWApi.RequestBuilder requestBuilder = api.action("query") + .param("list", "recentchanges") + .param("rcstart", formatMWDate(startDate)) + .param("rcnamespace", FILE_NAMESPACE) + .param("rcprop", "title|ids") + .param("rctype", "new|log") + .param("rctoponly", "1"); + + apiResult = requestBuilder.get(); + } catch (IOException e) { + Timber.e("Failed to obtain recent random", e); + } + if (apiResult != null) { + ApiResult recentChangesNode = apiResult.getNode("/api/query/recentchanges"); + if (recentChangesNode != null + && recentChangesNode.getDocument() != null + && recentChangesNode.getDocument().getChildNodes() != null + && recentChangesNode.getDocument().getChildNodes().getLength() > 0) { + NodeList childNodes = recentChangesNode.getDocument().getChildNodes(); + String imageTitle = RecentChangesImageUtils.findImageInRecentChanges(childNodes); + if (imageTitle != null) { + boolean deletionStatus = pageExists("Commons:Deletion_requests/" + imageTitle); + if (!deletionStatus) { + // strip File: prefix + imageTitle = imageTitle.replace("File:", ""); + media = new Media(imageTitle); + } + } + } + } + tries++; + } + return media; + } } 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 c0bd2fd87..4046530d1 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 @@ -75,7 +75,14 @@ public interface MediaWikiApi { @NonNull Single getUploadCount(String userName); + boolean thank(String editToken, String revision) throws IOException; + + String firstRevisionOfFile(String filename) throws IOException; + interface ProgressListener { void onProgress(long transferred, long total); } + + @Nullable + Media getRecentRandomImage() throws IOException; } diff --git a/app/src/main/java/fr/free/nrw/commons/review/CheckCategoryTask.java b/app/src/main/java/fr/free/nrw/commons/review/CheckCategoryTask.java new file mode 100644 index 000000000..d2086e5bd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/CheckCategoryTask.java @@ -0,0 +1,130 @@ +package fr.free.nrw.commons.review; + +import android.app.NotificationManager; +import android.content.Context; +import android.os.AsyncTask; +import android.support.v4.app.NotificationCompat; +import android.view.Gravity; +import android.widget.Toast; + +import javax.inject.Inject; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import timber.log.Timber; + +import static android.support.v4.app.NotificationCompat.DEFAULT_ALL; +import static android.support.v4.app.NotificationCompat.PRIORITY_HIGH; + +// Example code: +// CheckCategoryTask deleteTask = new CheckCategoryTask(getActivity(), media); + +public class CheckCategoryTask extends AsyncTask { + + @Inject + MediaWikiApi mwApi; + @Inject + SessionManager sessionManager; + + public static final int NOTIFICATION_CHECK_CATEGORY = 0x101; + + private NotificationManager notificationManager; + private NotificationCompat.Builder notificationBuilder; + private Context context; + private Media media; + + public CheckCategoryTask(Context context, Media media){ + this.context = context; + this.media = media; + } + + @Override + protected void onPreExecute(){ + ApplicationlessInjection + .getInstance(context.getApplicationContext()) + .getCommonsApplicationComponent() + .inject(this); + + notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationBuilder = new NotificationCompat.Builder(context); + Toast toast = new Toast(context); + toast.setGravity(Gravity.CENTER,0,0); + toast = Toast.makeText(context, context.getString(R.string.check_category_toast, media.getDisplayTitle()), Toast.LENGTH_SHORT); + toast.show(); + } + + @Override + protected Boolean doInBackground(Void ...voids) { + publishProgress(0); + + String editToken; + String authCookie; + String summary = context.getString(R.string.check_category_edit_summary); + + authCookie = sessionManager.getAuthCookie(); + mwApi.setAuthCookie(authCookie); + + try { + editToken = mwApi.getEditToken(); + if (editToken.equals("+\\")) { + return false; + } + publishProgress(1); + + mwApi.appendEdit(editToken, "\n{{subst:chc}}\n", media.getFilename(), summary); + publishProgress(2); + } + catch (Exception e) { + Timber.d(e.getMessage()); + return false; + } + return true; + } + + @Override + protected void onProgressUpdate (Integer... values){ + super.onProgressUpdate(values); + + int[] messages = new int[]{R.string.getting_edit_token, R.string.check_category_adding_template}; + String message = ""; + if (0 < values[0] && values[0] < messages.length) { + message = context.getString(messages[values[0]]); + } + + notificationBuilder.setContentTitle(context.getString(R.string.check_category_notification_title, media.getDisplayTitle())) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText(message)) + .setSmallIcon(R.drawable.ic_launcher) + .setProgress(messages.length, values[0], false) + .setOngoing(true); + notificationManager.notify(NOTIFICATION_CHECK_CATEGORY, notificationBuilder.build()); + } + + @Override + protected void onPostExecute(Boolean result) { + String message = ""; + String title = ""; + + if (result){ + title = context.getString(R.string.check_category_success_title); + message = context.getString(R.string.check_category_success_message, media.getDisplayTitle()); + } + else { + title = context.getString(R.string.check_category_failure_title); + message = context.getString(R.string.check_category_failure_message, media.getDisplayTitle()); + } + + notificationBuilder.setDefaults(DEFAULT_ALL) + .setContentTitle(title) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText(message)) + .setSmallIcon(R.drawable.ic_launcher) + .setProgress(0,0,false) + .setOngoing(false) + .setPriority(PRIORITY_HIGH); + notificationManager.notify(NOTIFICATION_CHECK_CATEGORY, notificationBuilder.build()); + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java new file mode 100644 index 000000000..4d51b97f3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -0,0 +1,173 @@ +package fr.free.nrw.commons.review; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import android.os.Handler; +import android.support.design.widget.NavigationView; +import android.support.v4.view.ViewPager; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.widget.Toolbar; + +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; + +import com.viewpagerindicator.CirclePageIndicator; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.auth.AuthenticatedActivity; +import fr.free.nrw.commons.mwapi.MediaResult; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.MediaDataExtractorUtil; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +/** + * Created by root on 18.05.2018. + */ + +public class ReviewActivity extends AuthenticatedActivity { + + @BindView(R.id.toolbar) + Toolbar toolbar; + @BindView(R.id.navigation_view) + NavigationView navigationView; + @BindView(R.id.drawer_layout) + DrawerLayout drawerLayout; + + @BindView(R.id.reviewPager) + ViewPager pager; + + @Inject MediaWikiApi mwApi; + + private ReviewPagerAdapter reviewPagerAdapter; + + //private ReviewCallback reviewCallback; + private ReviewController reviewController; + + @BindView(R.id.reviewPagerIndicator) + public CirclePageIndicator pagerIndicator; + + @Override + protected void onAuthCookieAcquired(String authCookie) { + + } + + @Override + protected void onAuthFailure() { + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_review); + ButterKnife.bind(this); + initDrawer(); + + reviewController = new ReviewController(); + + + reviewPagerAdapter = new ReviewPagerAdapter(getSupportFragmentManager()); + pager.setAdapter(reviewPagerAdapter); + reviewPagerAdapter.getItem(0); + pagerIndicator.setViewPager(pager); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.review_randomizer_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + if (id == R.id.action_review_randomizer) { + Observable.fromCallable(() -> { + Media result = null; + try { + result = mwApi.getRecentRandomImage(); + + //String thumBaseUrl = Utils.makeThumbBaseUrl(result.getFilename()); + //reviewPagerAdapter.currentThumbBasedUrl = thumBaseUrl; + + //Log.d("review", result.getWikiSource()); + + } catch (IOException e) { + Log.d("review", e.toString()); + } + return result; + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::updateImage); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void updateImage(Media result) { + reviewController.onImageRefreshed(result.getFilename()); //file name is updated + reviewPagerAdapter.updateFilename(); + pager.setCurrentItem(0); + Observable.fromCallable(() -> { + MediaResult media = mwApi.fetchMediaByFilename("File:" + result.getFilename()); + return MediaDataExtractorUtil.extractCategories(media.getWikiSource()); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::updateCategories); + } + + private void updateCategories(ArrayList categories) { + reviewController.onCategoriesRefreshed(categories); + reviewPagerAdapter.updateCategories(); + } + + /** + * References ReviewPagerAdapter to null before the activity is destroyed + */ + @Override + public void onDestroy() { + //adapter.setCallback(null); + super.onDestroy(); + } + + /** + * Consumers should be simply using this method to use this activity. + * @param context + * @param title Page title + */ + public static void startYourself(Context context, String title) { + Intent reviewActivity = new Intent(context, ReviewActivity.class); + context.startActivity(reviewActivity); + } + + interface ReviewCallback { + void onImageRefreshed(String itemTitle); + void onQuestionChanged(); + void onSurveyFinished(); + void onImproperImageReported(); + void onLicenceViolationReported(); + void onWrongCategoryReported(); + void onThankSent(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java new file mode 100644 index 000000000..2bde33ecd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.java @@ -0,0 +1,52 @@ +package fr.free.nrw.commons.review; + +import java.util.ArrayList; + +/** + * Created by root on 19.05.2018. + */ + +public class ReviewController implements ReviewActivity.ReviewCallback { + public static String fileName; + protected static ArrayList categories; + + @Override + public void onImageRefreshed(String fileName) { + ReviewController.fileName = fileName; + ReviewController.categories = new ArrayList<>(); + } + + public void onCategoriesRefreshed(ArrayList categories) { + ReviewController.categories = categories; + } + + @Override + public void onQuestionChanged() { + + } + + @Override + public void onSurveyFinished() { + + } + + @Override + public void onImproperImageReported() { + + } + + @Override + public void onLicenceViolationReported() { + + } + + @Override + public void onWrongCategoryReported() { + + } + + @Override + public void onThankSent() { + + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java new file mode 100644 index 000000000..bab8df6df --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java @@ -0,0 +1,92 @@ +package fr.free.nrw.commons.review; + +import android.app.AlertDialog; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.TextView; + +import com.facebook.drawee.view.SimpleDraweeView; + +import java.util.ArrayList; + +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; + +/** + * Created by root on 19.05.2018. + */ + +public class ReviewImageFragment extends CommonsDaggerSupportFragment { + + public static final int SPAM = 0; + public static final int COPYRIGHT = 1; + public static final int CATEGORY = 2; + + private int position; + private String fileName; + private String catString; + private View catsView; + private SimpleDraweeView simpleDraweeView; + + public void update(int position, String fileName) { + this.position = position; + this.fileName = fileName; + + if (simpleDraweeView!=null) { + simpleDraweeView.setImageURI(Utils.makeThumbBaseUrl(fileName)); + } + } + + public void updateCategories(Iterable categories) { + catString = TextUtils.join(", ", categories); + if (catsView != null) { + ((TextView) catsView).setText(catString); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + position = getArguments().getInt("position"); + View layoutView = inflater.inflate(R.layout.fragment_review_image, container, + false); + View textView = layoutView.findViewById(R.id.reviewQuestion); + catsView = layoutView.findViewById(R.id.reviewCategories); + String question; + switch(position) { + case COPYRIGHT: + question = getString(R.string.review_copyright); + break; + case CATEGORY: + question = getString(R.string.review_category); + catsView.setVisibility(View.VISIBLE); + break; + case SPAM: + question = getString(R.string.review_spam); + break; + default: + question = "How did we get here?"; + } + ((TextView) textView).setText(question); + simpleDraweeView = layoutView.findViewById(R.id.imageView); + + if (fileName != null) { + simpleDraweeView.setImageURI(Utils.makeThumbBaseUrl(fileName)); + } + if (catString != null) { + ((TextView) catsView).setText(catString); + } + return layoutView; + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewPagerAdapter.java new file mode 100644 index 000000000..06d07c7c9 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewPagerAdapter.java @@ -0,0 +1,51 @@ +package fr.free.nrw.commons.review; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; + +/** + * Created by nes on 19.05.2018. + */ + +public class ReviewPagerAdapter extends FragmentStatePagerAdapter { + private int currentPosition; + ReviewImageFragment[] reviewImageFragments; + + + public ReviewPagerAdapter(FragmentManager fm) { + super(fm); + reviewImageFragments = new ReviewImageFragment[] { + new ReviewImageFragment(), + new ReviewImageFragment(), + new ReviewImageFragment() + }; + } + + @Override + public int getCount() { + return 3; + } + + public void updateFilename() { + for (int i = 0; i < getCount(); i++) { + ReviewImageFragment fragment = reviewImageFragments[i]; + fragment.update(i, ReviewController.fileName); + } + } + + public void updateCategories() { + ReviewImageFragment categoryFragment = reviewImageFragments[ReviewImageFragment.CATEGORY]; + categoryFragment.updateCategories(ReviewController.categories); + } + + @Override + public Fragment getItem(int position) { + Bundle bundle = new Bundle(); + bundle.putInt("position", position); + reviewImageFragments[position].setArguments(bundle); + return reviewImageFragments[position]; + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/review/SendThankTask.java b/app/src/main/java/fr/free/nrw/commons/review/SendThankTask.java new file mode 100644 index 000000000..0f723217f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/SendThankTask.java @@ -0,0 +1,140 @@ +package fr.free.nrw.commons.review; + +import android.app.NotificationManager; +import android.content.Context; +import android.os.AsyncTask; +import android.support.v4.app.NotificationCompat; +import android.view.Gravity; +import android.widget.Toast; + +import javax.inject.Inject; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import timber.log.Timber; + +import static android.support.v4.app.NotificationCompat.DEFAULT_ALL; +import static android.support.v4.app.NotificationCompat.PRIORITY_HIGH; + +// example code: +// +// media = new Media("File:Iru.png"); +// Observable.fromCallable(() -> mwApi.firstRevisionOfFile(media.getFilename())) +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(revision -> { +// SendThankTask task = new SendThankTask(getActivity(), media, revision); +// task.execute(); +// }); + +public class SendThankTask extends AsyncTask { + + @Inject + MediaWikiApi mwApi; + @Inject + SessionManager sessionManager; + + public static final int NOTIFICATION_SEND_THANK = 0x102; + + private NotificationManager notificationManager; + private NotificationCompat.Builder notificationBuilder; + private Context context; + private Media media; + private String revision; + + public SendThankTask(Context context, Media media, String revision){ + this.context = context; + this.media = media; + this.revision = revision; + } + + @Override + protected void onPreExecute(){ + ApplicationlessInjection + .getInstance(context.getApplicationContext()) + .getCommonsApplicationComponent() + .inject(this); + + notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationBuilder = new NotificationCompat.Builder(context); + Toast toast = new Toast(context); + toast.setGravity(Gravity.CENTER,0,0); + toast = Toast.makeText(context, context.getString(R.string.send_thank_toast, media.getDisplayTitle()), Toast.LENGTH_SHORT); + toast.show(); + } + + @Override + protected Boolean doInBackground(Void ...voids) { + publishProgress(0); + + String editToken; + String authCookie; + + authCookie = sessionManager.getAuthCookie(); + mwApi.setAuthCookie(authCookie); + + try { + editToken = mwApi.getEditToken(); + if (editToken.equals("+\\")) { + return false; + } + publishProgress(1); + + mwApi.thank(editToken, revision); + + publishProgress(2); + } + catch (Exception e) { + Timber.d(e.getMessage()); + return false; + } + return true; + } + + @Override + protected void onProgressUpdate (Integer... values){ + super.onProgressUpdate(values); + + int[] messages = new int[]{R.string.getting_edit_token, R.string.send_thank_send}; + String message = ""; + if (0 < values[0] && values[0] < messages.length) { + message = context.getString(messages[values[0]]); + } + + notificationBuilder.setContentTitle(context.getString(R.string.send_thank_notification_title)) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText(message)) + .setSmallIcon(R.drawable.ic_launcher) + .setProgress(messages.length, values[0], false) + .setOngoing(true); + notificationManager.notify(NOTIFICATION_SEND_THANK, notificationBuilder.build()); + } + + @Override + protected void onPostExecute(Boolean result) { + String message = ""; + String title = ""; + + if (result){ + title = context.getString(R.string.send_thank_success_title); + message = context.getString(R.string.send_thank_success_message, media.getDisplayTitle()); + } + else { + title = context.getString(R.string.send_thank_failure_title); + message = context.getString(R.string.send_thank_failure_message, media.getDisplayTitle()); + } + + notificationBuilder.setDefaults(DEFAULT_ALL) + .setContentTitle(title) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText(message)) + .setSmallIcon(R.drawable.ic_launcher) + .setProgress(0,0,false) + .setOngoing(false) + .setPriority(PRIORITY_HIGH); + notificationManager.notify(NOTIFICATION_SEND_THANK, notificationBuilder.build()); + } +} \ No newline at end of file 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 4a7322b57..8fc12d068 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 @@ -30,6 +30,7 @@ import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.notification.NotificationActivity; +import fr.free.nrw.commons.review.ReviewActivity; import fr.free.nrw.commons.settings.SettingsActivity; import timber.log.Timber; @@ -160,6 +161,11 @@ public abstract class NavigationBaseActivity extends BaseActivity drawerLayout.closeDrawer(navigationView); CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_featured_images), FEATURED_IMAGES_CATEGORY); return true; + + case R.id.action_review: + drawerLayout.closeDrawer(navigationView); + ReviewActivity.startYourself(this, getString(R.string.title_activity_review)); + return true; default: Timber.e("Unknown option [%s] selected from the navigation menu", itemId); return false; diff --git a/app/src/main/java/fr/free/nrw/commons/utils/MediaDataExtractorUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/MediaDataExtractorUtil.java new file mode 100644 index 000000000..63421a8e4 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/MediaDataExtractorUtil.java @@ -0,0 +1,28 @@ +package fr.free.nrw.commons.utils; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MediaDataExtractorUtil { + + /** + * We could fetch all category links from API, but we actually only want the ones + * directly in the page source so they're editable. In the future this may change. + * + * @param source wikitext source code + */ + public static ArrayList extractCategories(String source) { + ArrayList categories = new ArrayList<>(); + Pattern regex = Pattern.compile("\\[\\[\\s*Category\\s*:([^]]*)\\s*\\]\\]", Pattern.CASE_INSENSITIVE); + Matcher matcher = regex.matcher(source); + while (matcher.find()) { + String cat = matcher.group(1).trim(); + categories.add(cat); + } + + return categories; + } + + +} diff --git a/app/src/main/res/drawable/ic_check_black_24dp.xml b/app/src/main/res/drawable/ic_check_black_24dp.xml new file mode 100644 index 000000000..3c728c59f --- /dev/null +++ b/app/src/main/res/drawable/ic_check_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_refresh_black_24dp.xml b/app/src/main/res/drawable/ic_refresh_black_24dp.xml new file mode 100644 index 000000000..8229a9a64 --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/tab_indicator_default.xml b/app/src/main/res/drawable/tab_indicator_default.xml new file mode 100644 index 000000000..341f4d706 --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator_default.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_indicator_selected.xml b/app/src/main/res/drawable/tab_indicator_selected.xml new file mode 100644 index 000000000..41c1bcf73 --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator_selected.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_selector.xml b/app/src/main/res/drawable/tab_selector.xml new file mode 100644 index 000000000..001747c31 --- /dev/null +++ b/app/src/main/res/drawable/tab_selector.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_review.xml b/app/src/main/res/layout/activity_review.xml new file mode 100644 index 000000000..a0b813f55 --- /dev/null +++ b/app/src/main/res/layout/activity_review.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_review_image.xml b/app/src/main/res/layout/fragment_review_image.xml new file mode 100644 index 000000000..7abf88f01 --- /dev/null +++ b/app/src/main/res/layout/fragment_review_image.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + +