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. }