From dcd767351bf45524d8d8c994b47aa0184f615f31 Mon Sep 17 00:00:00 2001 From: Anubhav Date: Tue, 1 May 2018 17:03:59 +0530 Subject: [PATCH 01/16] 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 02/16] 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 03/16] 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 04/16] 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 2d1f166ac77a21a0f2cf96c203f2359fe4927213 Mon Sep 17 00:00:00 2001 From: Ashish Date: Wed, 9 May 2018 17:57:59 +0530 Subject: [PATCH 05/16] 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 06/16] 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 07/16] 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 80a97c503714916b432cd382daf977030ef867c3 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 17 May 2018 08:24:27 +0200 Subject: [PATCH 08/16] 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 09/16] 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 10/16] 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 @@ + + + + + + + + + + + +