diff --git a/CHANGELOG.md b/CHANGELOG.md
index 383f725d6..ba83aa025 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Wikimedia Commons for Android
+## v3.0.2
+- Fixed crash when uploading high res image
+- Fixed crash when viewing images in Explore
+
+## v3.0.1
+- Pre-fill desc in Nearby uploads with Wikidata item's label + description
+- Improved ACRA crash reporting
+- Fixed various crashes
+
+## v3.0.0
+- Added Structured Data to upload workflow, users can now add depicts
+- Added Leaderboard in Achievements screen
+- Added to-do system for images with no categories/descriptions or with associated Wikipedia articles that have no pictures
+- Users can now modify and add categories to their uploads from the media details view
+- New UI for main screen
+- Limited connection mode added, users can now pause and resume uploads
+
## v2.13.1
- Added OpenStreetMap attribution
- Fixed various crashes
diff --git a/app/build.gradle b/app/build.gradle
index b6114ca51..5999efb59 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -57,8 +57,8 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp-ws:$OKHTTP_VERSION"
// Logging
- implementation 'ch.acra:acra-dialog:5.3.0'
- implementation 'ch.acra:acra-mail:5.3.0'
+ implementation 'ch.acra:acra-dialog:5.8.1-beta11'
+ implementation 'ch.acra:acra-mail:5.8.1-beta11'
implementation 'org.slf4j:slf4j-api:1.7.25'
api('com.github.tony19:logback-android-classic:1.1.1-6') {
exclude group: 'com.google.android', module: 'android'
@@ -150,8 +150,8 @@ android {
defaultConfig {
//applicationId 'fr.free.nrw.commons'
- versionCode 775
- versionName '2.13.1'
+ versionCode 1022
+ versionName '3.0.3'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion 19
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 81b9f7153..ffc972d89 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -57,6 +57,7 @@
{
+ contributionsSize = list.size();
+ adapter.submitList(list);
+ callback.notifyDataSetChanged();
+ });
rvContributionsList.setAdapter(adapter);
adapter.registerAdapterDataObserver(new AdapterDataObserver() {
@Override
@@ -328,7 +334,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
}
public int getTotalMediaCount() {
- return adapter.getItemCount();
+ return contributionsSize;
}
/**
@@ -355,6 +361,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
public interface Callback {
+ void notifyDataSetChanged();
+
void retryUpload(Contribution contribution);
void showDetail(int position, boolean isWikipediaButtonDisplayed);
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
index 7d5a4570e..24c2a1b16 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
@@ -94,7 +94,9 @@ public class MainActivity extends BaseActivity
@Override
public boolean onSupportNavigateUp() {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) {
- contributionsFragment.backButtonClicked();
+ if (!contributionsFragment.backButtonClicked()) {
+ return false;
+ }
} else {
onBackPressed();
showTabs();
@@ -108,11 +110,18 @@ public class MainActivity extends BaseActivity
setContentView(R.layout.main);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
+ toolbar.setNavigationOnClickListener(view -> {
+ onSupportNavigateUp();
+ });
if (applicationKvStore.getBoolean("login_skipped") == true) {
setTitle(getString(R.string.explore_tab_title_mobile));
setUpLoggedOutPager();
} else {
- setTitle(getString(R.string.contributions_fragment));
+ if(savedInstanceState == null){
+ //starting a fresh fragment.
+ setTitle(getString(R.string.contributions_fragment));
+ loadFragment(ContributionsFragment.newInstance(),false);
+ }
setUpPager();
initMain();
}
@@ -123,30 +132,31 @@ public class MainActivity extends BaseActivity
}
private void setUpPager() {
- loadFragment(ContributionsFragment.newInstance());
tabLayout.setOnNavigationItemSelectedListener(item -> {
if (!item.getTitle().equals("More")) {
// do not change title for more fragment
setTitle(item.getTitle());
}
Fragment fragment = NavTab.of(item.getOrder()).newInstance();
- return loadFragment(fragment);
+ return loadFragment(fragment,true);
});
}
private void setUpLoggedOutPager() {
- loadFragment(ExploreFragment.newInstance());
+ loadFragment(ExploreFragment.newInstance(),false);
tabLayout.setOnNavigationItemSelectedListener(item -> {
if (!item.getTitle().equals("More")) {
// do not change title for more fragment
setTitle(item.getTitle());
}
Fragment fragment = NavTabLoggedOut.of(item.getOrder()).newInstance();
- return loadFragment(fragment);
+ return loadFragment(fragment,true);
});
}
- private boolean loadFragment(Fragment fragment) {
+ private boolean loadFragment(Fragment fragment,boolean showBottom ) {
+ //showBottom so that we do not show the bottom tray again when constructing
+ //from the saved instance state.
if (fragment instanceof ContributionsFragment) {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) { // Do nothing if same tab
return true;
@@ -171,7 +181,7 @@ public class MainActivity extends BaseActivity
}
bookmarkFragment = (BookmarkFragment) fragment;
activeFragment = ActiveFragment.BOOKMARK;
- } else if (fragment == null) {
+ } else if (fragment == null && showBottom) {
if (applicationKvStore.getBoolean("login_skipped") == true) { // If logged out, more sheet is different
MoreBottomSheetLoggedOutFragment bottomSheet = new MoreBottomSheetLoggedOutFragment();
bottomSheet.show(getSupportFragmentManager(),
@@ -238,8 +248,11 @@ public class MainActivity extends BaseActivity
@Override
public void onBackPressed() {
if (contributionsFragment != null && activeFragment == ActiveFragment.CONTRIBUTIONS) {
- // Meas that contribution fragment is visible
- contributionsFragment.backButtonClicked();
+ // Means that contribution fragment is visible
+ if (!contributionsFragment.backButtonClicked()) {//If this one does not wan't to handle
+ // the back press, let the activity do so
+ super.onBackPressed();
+ }
} else if (nearbyParentFragment != null && activeFragment == ActiveFragment.NEARBY) {
// Means that nearby fragment is visible
/* If function nearbyParentFragment.backButtonClick() returns false, it means that the bottomsheet is
diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
index 7921e930f..04e103cd0 100644
--- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
+++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
@@ -13,7 +13,7 @@ import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "commons.db";
- private static final int DATABASE_VERSION = 13;
+ private static final int DATABASE_VERSION = 14;
public static final String CONTRIBUTIONS_TABLE = "contributions";
private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";
diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java
index 0a3f43a3b..e8967d9e8 100644
--- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java
@@ -34,6 +34,10 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
@BindView(R.id.explore_container)
FrameLayout container;
+ public ExploreListRootFragment(){
+ //empty constructor necessary otherwise crashes on recreate
+ }
+
public ExploreListRootFragment(Bundle bundle) {
String title = bundle.getString("categoryName");
listFragment = new CategoriesMediaFragment();
@@ -55,7 +59,9 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- setFragment(listFragment, mediaDetails);
+ if(savedInstanceState == null) {
+ setFragment(listFragment, mediaDetails);
+ }
}
public void setFragment(Fragment fragment, Fragment otherFragment) {
@@ -110,8 +116,9 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
Log.d("deneme8","on media clicked");
container.setVisibility(View.VISIBLE);
((ExploreFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
- mediaDetails = new MediaDetailPagerFragment(false, true, position);
+ mediaDetails = new MediaDetailPagerFragment(false, true);
setFragment(mediaDetails, listFragment);
+ mediaDetails.showImage(position);
}
/**
diff --git a/app/src/main/java/fr/free/nrw/commons/logging/CommonsLogSender.java b/app/src/main/java/fr/free/nrw/commons/logging/CommonsLogSender.java
index 4e8fe2e70..29c2c732e 100644
--- a/app/src/main/java/fr/free/nrw/commons/logging/CommonsLogSender.java
+++ b/app/src/main/java/fr/free/nrw/commons/logging/CommonsLogSender.java
@@ -2,12 +2,16 @@ package fr.free.nrw.commons.logging;
import android.content.Context;
+import android.os.Bundle;
import javax.inject.Inject;
import javax.inject.Singleton;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.DeviceInfoUtil;
+import org.acra.data.CrashReportData;
+import org.acra.sender.ReportSenderException;
+import org.jetbrains.annotations.NotNull;
/**
* Class responsible for sending logs to developers
@@ -87,4 +91,15 @@ public class CommonsLogSender extends LogsSender {
return builder.toString();
}
+
+ @Override
+ public boolean requiresForeground() {
+ return false;
+ }
+
+ @Override
+ public void send(@NotNull Context context, @NotNull CrashReportData crashReportData,
+ @NotNull Bundle bundle) throws ReportSenderException {
+
+ }
}
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 4c7f16c11..55d2bfc71 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
@@ -6,13 +6,15 @@ import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
@@ -90,27 +92,22 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
pager.addOnPageChangeListener(this);
adapter = new MediaDetailAdapter(getChildFragmentManager());
- ((BaseActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ if (getActivity() != null) {
+ final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ pager.setAdapter(adapter);
if (savedInstanceState != null) {
final int pageNumber = savedInstanceState.getInt("current-page");
- // Adapter doesn't seem to be loading immediately.
- // Dear God, please forgive us for our sins
- view.postDelayed(() -> {
- pager.setAdapter(adapter);
- pager.setCurrentItem(pageNumber, false);
-
- if (getActivity() == null) {
- Timber.d("Returning as activity is destroyed!");
- return;
- }
-
- getActivity().supportInvalidateOptionsMenu();
- adapter.notifyDataSetChanged();
- }, 100);
- } else {
- pager.setAdapter(adapter);
+ pager.setCurrentItem(pageNumber, false);
+ getActivity().invalidateOptionsMenu();
}
+ adapter.notifyDataSetChanged();
if (getActivity() instanceof MainActivity) {
((MainActivity)getActivity()).hideTabs();
}
@@ -304,13 +301,31 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
public void showImage(int i, boolean isWikipediaButtonDisplayed) {
this.isWikipediaButtonDisplayed = isWikipediaButtonDisplayed;
- Handler handler = new Handler();
- handler.postDelayed(() -> pager.setCurrentItem(i), 5);
+ setViewPagerCurrentItem(i);
}
public void showImage(int i) {
- Handler handler = new Handler();
- handler.postDelayed(() -> pager.setCurrentItem(i), 5);
+ setViewPagerCurrentItem(i);
+ }
+
+ /**
+ * This function waits for the item to load then sets the item to current item
+ * @param position current item that to be shown
+ */
+ private void setViewPagerCurrentItem(int position) {
+ final Boolean[] currentItemNotShown = {true};
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ while(currentItemNotShown[0]){
+ if(adapter.getCount() > position){
+ pager.setCurrentItem(position, false);
+ currentItemNotShown[0] = false;
+ }
+ }
+ }
+ };
+ new Thread(runnable).start();
}
/**
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java
index 1da57e48e..11f670420 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java
@@ -60,10 +60,26 @@ public class Place implements Parcelable {
if(!StringUtils.isBlank(itemClass)) {
classEntityId = itemClass.replace("http://www.wikidata.org/entity/", "");
}
+ // Set description when not null and not empty
+ String description = (item.getDescription().getValue() != null && !item.getDescription().getValue().isEmpty()) ? item.getDescription().getValue() : "";
+ // When description is "?" but we have a valid label, just use the label. So replace "?" by "" in description
+ description = (description.equals("?")
+ && (item.getLabel().getValue() != null
+ && !item.getLabel().getValue().isEmpty()) ? "" : description);
+ /*
+ * If we have a valid label
+ * - If have a valid label add the description at the end of the string with parenthesis
+ * - If we don't have a valid label, string will include only the description. So add it without paranthesis
+ */
+ description = ((item.getLabel().getValue() != null && !item.getLabel().getValue().isEmpty())
+ ? item.getLabel().getValue()
+ + ((description != null && !description.isEmpty())
+ ? " (" + description + ")" : "")
+ : description);
return new Place(
item.getLabel().getValue(),
Label.fromText(classEntityId), // list
- item.getClassLabel().getValue(), // details
+ description, // description and label of Wikidata item
PlaceUtils.latLngFromPointString(item.getLocation().getValue()),
item.getCommonsCategory().getValue(),
new Sitelinks.Builder()
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt
index eaf32a48b..632eadd47 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt
@@ -51,7 +51,8 @@ fun placeAdapterDelegate(
tvDesc.setText(R.string.no_description_found)
tvDesc.visibility = INVISIBLE
} else {
- tvDesc.text = descriptionText
+ // Remove the label and display only texts inside pharentheses (description) since too long
+ tvDesc.text = descriptionText.substringAfter(tvName.text.toString() + " (").substringBeforeLast(")");
}
distance.text = item.distance
icon.setImageResource(item.label.icon)
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java
index 0689dafc0..d024655ed 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java
@@ -1435,7 +1435,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
title.setText(selectedPlace.name);
distance.setText(selectedPlace.distance);
- description.setText(selectedPlace.getLongDescription());
+ // Remove label since it is double information
+ String descriptionText = selectedPlace.getLongDescription()
+ .replace(selectedPlace.getName() + " (","");
+ descriptionText = (descriptionText.equals(selectedPlace.getLongDescription()) ? descriptionText : descriptionText.replaceFirst(".$",""));
+ // Set the short description after we remove place name from long description
+ description.setText(descriptionText);
fabCamera.setOnClickListener(view -> {
if (fabCamera.isShown()) {
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt
index 7950db906..2557c97d4 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt
@@ -11,7 +11,8 @@ class NearbyResultItem(private val item: ResultTuple?,
@field:SerializedName("classLabel") private val classLabel: ResultTuple?,
@field:SerializedName("commonsCategory") private val commonsCategory: ResultTuple?,
@field:SerializedName("pic") private val pic: ResultTuple?,
- @field:SerializedName("destroyed") private val destroyed: ResultTuple?) {
+ @field:SerializedName("destroyed") private val destroyed: ResultTuple?,
+ @field:SerializedName("description") private val description: ResultTuple?) {
fun getItem(): ResultTuple {
return item ?: ResultTuple()
@@ -57,4 +58,8 @@ class NearbyResultItem(private val item: ResultTuple?,
return destroyed ?: ResultTuple()
}
+ fun getDescription(): ResultTuple {
+ return description ?: ResultTuple()
+ }
+
}
\ No newline at end of file
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
index f82ebd43a..0a828e079 100644
--- a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java
@@ -115,7 +115,7 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment {
//Get existing user name if it is already saved using savedInstanceState else get from reviewController
if (savedInstanceState == null) {
- if (getReviewActivity().reviewController != null) {
+ if (getReviewActivity().reviewController.firstRevision != null) {
user = getReviewActivity().reviewController.firstRevision.getUser();
}
} else {
diff --git a/app/src/main/resources/queries/nearby_query.rq b/app/src/main/resources/queries/nearby_query.rq
index 35af6bece..aeabd903d 100644
--- a/app/src/main/resources/queries/nearby_query.rq
+++ b/app/src/main/resources/queries/nearby_query.rq
@@ -2,6 +2,7 @@ SELECT
(SAMPLE(?location) as ?location)
?item
(SAMPLE(COALESCE(?itemLabelPreferredLanguage, ?itemLabelAnyLanguage)) as ?label)
+ (SAMPLE(COALESCE(?itemDescriptionPreferredLanguage, ?itemDescriptionAnyLanguage, "?")) as ?description)
(SAMPLE(?classId) as ?class)
(SAMPLE(COALESCE(?classLabelPreferredLanguage, ?classLabelAnyLanguage, "?")) as ?classLabel)
(SAMPLE(COALESCE(?icon0, ?icon1)) as ?icon)
@@ -22,6 +23,10 @@ SELECT
OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "${LANG}")}
OPTIONAL {?item rdfs:label ?itemLabelAnyLanguage}
+ # Get the description in the preferred language of the user, or any other language if no description is available in that language.
+ OPTIONAL {?item schema:description ?itemDescriptionPreferredLanguage. FILTER (lang(?itemDescriptionPreferredLanguage) = "${LANG}")}
+ OPTIONAL {?item schema:description ?itemDescriptionAnyLanguage }
+
# Get Commons category (P373)
OPTIONAL { ?item wdt:P373 ?commonsCategory. }