Merge 4.0-release into master (#5028)

* Fix string for custom selector

* Fix bug #4950 back arrow still present on top-level activity (#4952)

* Fix bug #4949 by correctly setting previous db version number (#4956)

* Fix bug #4949 by correctly setting previous db version number

* Fix failing tests

* Fix bug #4959 by correctly setting previous db version number and updating the current db version (#4960)

* Fix bug #4957 (#4961)

* Update library to new version that handles older Java VMs

Fixes #4972 I believe.

* Versioning for v4.0.0

* Changelog for v4.0.0

* Fix bug #4984 Added queries for package name for Android API 30+ (#4987)

* Update mapbox sdk version (#4989)

* Versioning for v4.0.1

* Changelog for v4.0.1

* Remove network type information from NetworkUtils (#4996)

* Remove network type information from NetworkUtils

* Ignore dependent tests

* Fix #4992 invert the equals condition to be null safe (#4995)

* Fix java.lang.NullPointerException for username in ContributionBoundaryCallback (#5003)

* Fix failing tests for PR #5003 (#5004)

* Fix java.lang.NullPointerException for username in ContributionBoundaryCallback

* Fix failing tests

* Update Room DB Version (#5011)

* Fix #5001 (#5010)

* Fix DB update issue (#5016)

* [WIP] Fix both timezone problem and saved date problem (#5019)

* Fix both timezone problem and saved date problem

* Fixaccidental test code and add comments

* Add issue link to the comments

* Fix format issue and null checks

* Versioning for v4.0.2

* Changelog for v4.0.2

* Add "Report Violation" menu option (#5025)

* Add "Report Violation" menu option

* Update email template

* Update email address

* Fixed typo

Co-authored-by: Josephine Lim <josephinelim86@gmail.com>

* Versioning for v4.0.3

* Changelog for v4.0.3

Co-authored-by: Josephine Lim <josephinelim86@gmail.com>
Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
Co-authored-by: neslihanturan <tur.neslihan@gmail.com>
This commit is contained in:
Madhur Gupta 2022-08-08 11:21:07 +05:30 committed by GitHub
parent 1de8968fa8
commit 3cdfdcffe1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 406 additions and 72 deletions

View file

@ -1,5 +1,24 @@
# Wikimedia Commons for Android
## v4.0.3
- Added "Report" button for Google UGC policy
## v4.0.2
- Fixed bug with wrong dates taken from EXIF
- Fixed various crashes
## v4.0.1
- Fixed bug with no browser found
- Updated Mapbox SDK to fix hamburger crash
## v4.0.0
- Added map showing nearby Commons pictures
- Added custom SPARQL queries
- Added user profiles
- Added custom picture selector
- Various bugfixes
- Updated target SDK to 30
## v3.1.1
- Optimized Nearby query
- Added Sweden's property for WLM 2021

View file

@ -38,10 +38,10 @@ dependencies {
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
implementation 'com.github.pedrovgs:renderers:3.3.3'
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v8:0.11.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-scalebar-v9:0.4.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-telemetry:6.1.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.2.1'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.12.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-scalebar-v9:0.5.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-telemetry:7.0.0'
implementation 'com.github.deano2390:MaterialShowcaseView:1.2.0'
implementation 'com.dinuscxj:circleprogressbar:1.1.1'
implementation 'com.karumi:dexter:5.0.0'
@ -173,8 +173,8 @@ android {
defaultConfig {
//applicationId 'fr.free.nrw.commons'
versionCode 1025
versionName '3.1.1'
versionCode 1029
versionName '4.0.3'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion 19

View file

@ -18,6 +18,16 @@
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<queries>
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<!-- Google Maps -->
<package android:name="com.google.android.apps.maps" />
</queries>
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->

View file

@ -110,6 +110,10 @@ public class CommonsApplication extends MultiDexApplication {
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App Feedback";
public static final String REPORT_EMAIL = "commons-app-android-private@googlegroups.com";
public static final String REPORT_EMAIL_SUBJECT = "Report a violation";
public static final String NOTIFICATION_CHANNEL_ID_ALL = "CommonsNotificationAll";
public static final String FEEDBACK_EMAIL_TEMPLATE_HEADER = "-- Technical information --";

View file

@ -155,7 +155,7 @@ public class LocationPickerActivity extends BaseActivity implements OnMapReadyCa
addCredits();
getToolbarUI();
if (activity.equals("UploadActivity")) {
if ("UploadActivity".equals(activity)) {
placeSelectedButton.setVisibility(View.GONE);
modifyLocationButton.setVisibility(View.VISIBLE);
showInMapButton.setVisibility(View.VISIBLE);

View file

@ -309,22 +309,18 @@ public class BookmarkItemsDao {
if (from == to) {
return;
}
if (from < 7) {
if (from < 18) {
// doesn't exist yet
from++;
onUpdate(db, from, to);
return;
}
if (from == 7) {
if (from == 18) {
// table added in version 19
onCreate(db);
from++;
onUpdate(db, from, to);
return;
}
if (from == 8) {
from++;
onUpdate(db, from, to);
}
}
}

View file

@ -93,10 +93,12 @@ public class CategoryDao {
// fixme add a limit on the original query instead of falling out of the loop?
while (cursor != null && cursor.moveToNext()
&& cursor.getPosition() < limit) {
if (fromCursor(cursor).getName() != null ) {
items.add(new CategoryItem(fromCursor(cursor).getName(),
fromCursor(cursor).getDescription(), fromCursor(cursor).getThumbnail(),
false));
}
}
} catch (RemoteException e) {
throw new RuntimeException(e);
} finally {
@ -193,6 +195,13 @@ public class CategoryDao {
onUpdate(db, from, to);
return;
}
if (from == 17) {
db.execSQL("ALTER TABLE categories ADD COLUMN description STRING;");
db.execSQL("ALTER TABLE categories ADD COLUMN thumbnail STRING;");
from++;
onUpdate(db, from, to);
return;
}
}
}
}

View file

@ -4,8 +4,8 @@ import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class CategoryItem(val name: String, val description: String,
val thumbnail: String, var isSelected: Boolean) : Parcelable {
data class CategoryItem(val name: String, val description: String?,
val thumbnail: String?, var isSelected: Boolean) : Parcelable {
override fun toString(): String {
return "CategoryItem: '$name'"

View file

@ -38,6 +38,7 @@ data class Contribution constructor(
val localUri: Uri? = null,
var dataLength: Long = 0,
var dateCreated: Date? = null,
var dateCreatedString: String? = null,
var dateModified: Date? = null,
var hasInvalidLocation : Int = 0,
var contentUri: Uri? = null,
@ -70,6 +71,7 @@ data class Contribution constructor(
depictedItems = depictedItems,
wikidataPlace = from(item.place),
contentUri = item.contentUri,
dateCreatedString = item.fileCreatedDateString,
imageSHA1 = imageSHA1
)

View file

@ -21,7 +21,7 @@ class ContributionBoundaryCallback @Inject constructor(
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler
) : BoundaryCallback<Contribution>() {
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
lateinit var userName: String
var userName: String? = null
/**
@ -53,13 +53,13 @@ class ContributionBoundaryCallback @Inject constructor(
/**
* Fetches contributions using the MediaWiki API
*/
fun fetchContributions() {
private fun fetchContributions() {
if (sessionManager.userName != null) {
compositeDisposable.add(
mediaClient.getMediaListForUser(userName!!)
userName?.let { userName ->
mediaClient.getMediaListForUser(userName)
.map { mediaList ->
mediaList.map {
Contribution(media = it, state = Contribution.STATE_COMPLETED)
mediaList.map { media ->
Contribution(media = media, state = Contribution.STATE_COMPLETED)
}
}
.subscribeOn(ioThreadScheduler)
@ -69,11 +69,13 @@ class ContributionBoundaryCallback @Inject constructor(
error.message
)
}
}?.let {
compositeDisposable.add(
it
)
}else {
if (compositeDisposable != null){
compositeDisposable.clear()
}
}else {
compositeDisposable.clear()
}
}

View file

@ -661,7 +661,7 @@ public class ContributionsFragment
}
public boolean backButtonClicked() {
if (null != mediaDetailPagerFragment && mediaDetailPagerFragment.isVisible()) {
if (mediaDetailPagerFragment != null && mediaDetailPagerFragment.isVisible()) {
if (store.getBoolean("displayNearbyCardView", true) && !isUserProfile) {
if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
nearbyNotificationCardView.setVisibility(View.VISIBLE);
@ -680,6 +680,7 @@ public class ContributionsFragment
}
if (getActivity() instanceof MainActivity) {
// Fragment is associated with MainActivity
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
((MainActivity) getActivity()).showTabs();
}
return true;

View file

@ -4,9 +4,7 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao;
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
import fr.free.nrw.commons.category.CategoryDao;
@ -16,7 +14,7 @@ import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao;
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "commons.db";
private static final int DATABASE_VERSION = 19;
private static final int DATABASE_VERSION = 20;
public static final String CONTRIBUTIONS_TABLE = "contributions";
private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";

View file

@ -14,7 +14,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao
* The database for accessing the respective DAOs
*
*/
@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 12, exportSchema = false)
@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 13, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun contributionDao(): ContributionDao

View file

@ -154,8 +154,7 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi
buffer.append("}}, ")
}
}
buffer.deleteCharAt(buffer.length - 1)
buffer.deleteCharAt(buffer.length - 1)
buffer.replace(", $".toRegex(), "")
buffer.append(descriptionEnd)
}
val returningIntent = Intent()

View file

@ -12,6 +12,8 @@ import androidx.exifinterface.media.ExifInterface;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import fr.free.nrw.commons.upload.FileUtils;
@ -124,13 +126,30 @@ public class UploadableFile implements Parcelable {
private DateTimeWithSource getDateTimeFromExif() {
try {
ExifInterface exif = new ExifInterface(file.getAbsolutePath());
@SuppressLint("RestrictedApi") Long dateTime = exif.getDateTime();
// TAG_DATETIME returns the last edited date, we need TAG_DATETIME_ORIGINAL for creation date
// See issue https://github.com/commons-app/apps-android-commons/issues/1971
String dateTimeSubString = exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL);
if (dateTimeSubString!=null) { //getAttribute may return null
String year = dateTimeSubString.substring(0,4);
String month = dateTimeSubString.substring(5,7);
String day = dateTimeSubString.substring(8,10);
// This date is stored as a string (not as a date), the rason is we don't want to include timezones
String dateCreatedString = String.format("%04d-%02d-%02d", Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day));
if (dateCreatedString.length() == 10) { //yyyy-MM-dd format of date is expected
@SuppressLint("RestrictedApi") Long dateTime = exif.getDateTimeOriginal();
if(dateTime != null){
Date date = new Date(dateTime);
return new DateTimeWithSource(date, DateTimeWithSource.EXIF_SOURCE);
return new DateTimeWithSource(date, dateCreatedString, DateTimeWithSource.EXIF_SOURCE);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
return null;
}
@ -149,6 +168,7 @@ public class UploadableFile implements Parcelable {
public static final String EXIF_SOURCE = "exif";
private final long epochDate;
private String dateString; // this does not includes timezone information
private final String source;
public DateTimeWithSource(long epochDate, String source) {
@ -161,10 +181,20 @@ public class UploadableFile implements Parcelable {
this.source = source;
}
public DateTimeWithSource(Date date, String dateString, String source) {
this.epochDate = date.getTime();
this.dateString = dateString;
this.source = source;
}
public long getEpochDate() {
return epochDate;
}
public String getDateString() {
return dateString;
}
public String getSource() {
return source;
}

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.media;
import static fr.free.nrw.commons.Utils.handleWebUrl;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -12,7 +13,9 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
@ -22,6 +25,7 @@ import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.snackbar.Snackbar;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
@ -33,7 +37,6 @@ import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.utils.DownloadUtils;
import fr.free.nrw.commons.utils.ImageUtils;
import fr.free.nrw.commons.utils.NetworkUtils;
@ -211,11 +214,83 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
ProfileActivity.startYourself(getActivity(), m.getUser(),
!Objects.equals(sessionManager.getUserName(), m.getUser()));
}
return true;
case R.id.menu_view_report:
showReportDialog(m);
default:
return super.onOptionsItemSelected(item);
}
}
private void showReportDialog(final Media media) {
if (media == null) {
return;
}
final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
final String[] values = requireContext().getResources()
.getStringArray(R.array.report_violation_options);
builder.setTitle(R.string.report_violation);
builder.setItems(R.array.report_violation_options, (dialog, which) -> {
sendReportEmail(media, values[which]);
});
builder.show();
}
private void sendReportEmail(final Media media, final String type) {
final String technicalInfo = getTechInfo(media, type);
final Intent feedbackIntent = new Intent(Intent.ACTION_SENDTO);
feedbackIntent.setType("message/rfc822");
feedbackIntent.setData(Uri.parse("mailto:"));
feedbackIntent.putExtra(Intent.EXTRA_EMAIL,
new String[]{CommonsApplication.REPORT_EMAIL});
feedbackIntent.putExtra(Intent.EXTRA_SUBJECT,
CommonsApplication.REPORT_EMAIL_SUBJECT);
feedbackIntent.putExtra(Intent.EXTRA_TEXT, technicalInfo);
try {
startActivity(feedbackIntent);
} catch (final ActivityNotFoundException e) {
Toast.makeText(getActivity(), R.string.no_email_client, Toast.LENGTH_SHORT).show();
}
}
private String getTechInfo(final Media media, final String type) {
final StringBuilder builder = new StringBuilder();
builder.append("Report type: ")
.append(type)
.append("\n\n");
builder.append("Image that you want to report: ")
.append(media.getImageUrl())
.append("\n\n");
builder.append("User that you want to report: ")
.append(media.getAuthor())
.append("\n\n");
if (sessionManager.getUserName() != null) {
builder.append("Your username: ")
.append(sessionManager.getUserName())
.append("\n\n");
}
builder.append("Violation reason: ")
.append("\n");
builder.append("----------------------------------------------")
.append("\n")
.append("(please write reason here)")
.append("\n")
.append("----------------------------------------------")
.append("\n\n")
.append("Thank you for your report! Our team will investigate as soon as possible.")
.append("\n")
.append("Please note that images also have a `Nominate for deletion` button.");
return builder.toString();
}
/**
* Set the media as the device's wallpaper if the imageUrl is not null
* Fails silently if setting the wallpaper fails

View file

@ -185,23 +185,17 @@ public class RecentLanguagesDao {
if (from == to) {
return;
}
if (from < 6) {
if (from < 19) {
// doesn't exist yet
from++;
onUpdate(db, from, to);
return;
}
if (from == 6) {
// table added in version 7
if (from == 19) {
// table added in version 20
onCreate(db);
from++;
onUpdate(db, from, to);
return;
}
if (from == 7) {
from++;
onUpdate(db, from, to);
return;
}
}
}

View file

@ -48,7 +48,7 @@ class PageContentsCreator {
.append(media.getAuthor()).append("]]\n");
final String templatizedCreatedDate = getTemplatizedCreatedDate(
contribution.getDateCreated(), contribution.getDateCreatedSource());
contribution.getDateCreatedString(), contribution.getDateCreated(), contribution.getDateCreatedSource());
if (!StringUtils.isBlank(templatizedCreatedDate)) {
buffer.append("|date=").append(templatizedCreatedDate);
}
@ -90,12 +90,12 @@ class PageContentsCreator {
* @param dateCreatedSource
* @return
*/
private String getTemplatizedCreatedDate(Date dateCreated, String dateCreatedSource) {
private String getTemplatizedCreatedDate(String dateCreatedString, Date dateCreated, String dateCreatedSource) {
if (dateCreated != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return String.format(Locale.ENGLISH,
isExif(dateCreatedSource) ? TEMPLATE_DATE_ACC_TO_EXIF : TEMPLATE_DATA_OTHER_SOURCE,
dateFormat.format(dateCreated)
isExif(dateCreatedSource) ? dateCreatedString: dateFormat.format(dateCreated)
) + "\n";
}
return "";

View file

@ -25,6 +25,7 @@ public class UploadItem {
private boolean hasInvalidLocation;
private boolean isWLMUpload = false;
private String countryCode;
private String fileCreatedDateString; //according to EXIF data
/**
* Uri of uploadItem
@ -40,7 +41,8 @@ public class UploadItem {
final Place place,
final long createdTimestamp,
final String createdTimestampSource,
final Uri contentUri) {
final Uri contentUri,
final String fileCreatedDateString) {
this.createdTimestampSource = createdTimestampSource;
uploadMediaDetails = new ArrayList<>(Collections.singletonList(new UploadMediaDetail()));
this.place = place;
@ -50,6 +52,7 @@ public class UploadItem {
this.createdTimestamp = createdTimestamp;
this.contentUri = contentUri;
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
this.fileCreatedDateString = fileCreatedDateString;
}
public String getCreatedTimestampSource() {
@ -83,6 +86,8 @@ public class UploadItem {
*/
public Uri getContentUri() { return contentUri; }
public String getFileCreatedDateString() { return fileCreatedDateString; }
public void setImageQuality(final int imageQuality) {
this.imageQuality.onNext(imageQuality);
}

View file

@ -102,8 +102,10 @@ public class UploadModel {
.getFileCreatedDate(context);
long fileCreatedDate = -1;
String createdTimestampSource = "";
String fileCreatedDateString = "";
if (dateTimeWithSource != null) {
fileCreatedDate = dateTimeWithSource.getEpochDate();
fileCreatedDateString = dateTimeWithSource.getDateString();
createdTimestampSource = dateTimeWithSource.getSource();
}
Timber.d("File created date is %d", fileCreatedDate);
@ -113,7 +115,8 @@ public class UploadModel {
Uri.parse(uploadableFile.getFilePath()),
uploadableFile.getMimeType(context), imageCoordinates, place, fileCreatedDate,
createdTimestampSource,
uploadableFile.getContentUri());
uploadableFile.getContentUri(),
fileCreatedDateString);
if (place != null) {
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
}

View file

@ -148,18 +148,16 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
if (callback != null) {
init();
}
}
private void init() {
tvTitle.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1,
callback.getTotalNumberOfSteps(), getString(R.string.media_detail_step_title)));
tooltip.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showInfoAlert(R.string.media_detail_step_title, R.string.media_details_tooltip);
}
});
tooltip.setOnClickListener(
v -> showInfoAlert(R.string.media_detail_step_title, R.string.media_details_tooltip));
initPresenter();
presenter.receiveImage(uploadableFile, place);
initRecyclerView();

View file

@ -49,6 +49,8 @@ public class NetworkUtils {
return NetworkConnectionType.WIFI;
}
// TODO for Android 12+ request permission from user is mandatory
/*
int mobileNetwork = telephonyManager.getNetworkType();
switch (mobileNetwork) {
case TelephonyManager.NETWORK_TYPE_GPRS:
@ -71,6 +73,8 @@ public class NetworkUtils {
default:
return NetworkConnectionType.UNKNOWN;
}
*/
return NetworkConnectionType.UNKNOWN;
}
/**

View file

@ -34,5 +34,9 @@
android:id="@+id/menu_view_user_page"
android:title="@string/menu_view_user_page"
app:showAsAction="never" />
<item
android:id="@+id/menu_view_report"
android:title="@string/menu_view_report"
app:showAsAction="never" />
</menu>

View file

@ -70,5 +70,10 @@
<item>yearly</item>
<item>all_time</item>
</string-array>
<string-array name="report_violation_options" >
<item>@string/report_user</item>
<item>@string/report_content</item>
<item>@string/request_user_block</item>
</string-array>
</resources>

View file

@ -691,7 +691,7 @@ Upload your first media by tapping on the add button.</string>
<string name="done">Done</string>
<string name="back">Back</string>
<string name="welcome_custom_picture_selector_text">Welcome to Custom Picture Selector</string>
<string name="custom_selector_info_text1">This picker shows differently pictures that are already to Commons.</string>
<string name="custom_selector_info_text1">This picker shows you which pictures you have already uploaded to Commons.</string>
<string name="custom_selector_info_text2">Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded. \n Touch and hold for image preview.</string>
<string name="welcome_custom_selector_ok">Awesome</string>
<string name="custom_selector_already_uploaded_image_text">This image has already been uploaded to Commons.</string>
@ -734,4 +734,9 @@ Upload your first media by tapping on the add button.</string>
<string name="error_feedback">Error while sending feedback</string>
<string name="enter_description">What is your feedback?</string>
<string name="your_feedback">Your feedback</string>
<string name="menu_view_report">Report</string>
<string name="report_violation">Report violation</string>
<string name="report_user">Report this user</string>
<string name="report_content">Report this content</string>
<string name="request_user_block">Request to block this user</string>
</resources>

View file

@ -271,9 +271,84 @@ class BookmarkItemsDaoTest {
@Test
fun migrateTableVersionFrom_v7_to_v8() {
onUpdate(database, 7, 8)
// Table didn't change in version 8
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v8_to_v9() {
onUpdate(database, 8, 9)
// Table didn't change in version 9
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v9_to_v10() {
onUpdate(database, 9, 10)
// Table didn't change in version 10
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v10_to_v11() {
onUpdate(database, 10, 11)
// Table didn't change in version 11
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v11_to_v12() {
onUpdate(database, 11, 12)
// Table didn't change in version 12
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v12_to_v13() {
onUpdate(database, 12, 13)
// Table didn't change in version 13
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v13_to_v14() {
onUpdate(database, 13, 14)
// Table didn't change in version 14
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v14_to_v15() {
onUpdate(database, 14, 15)
// Table didn't change in version 15
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v15_to_v16() {
onUpdate(database, 15, 16)
// Table didn't change in version 16
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v16_to_v17() {
onUpdate(database, 16, 17)
// Table didn't change in version 17
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v18_to_v19() {
onUpdate(database, 18, 19)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun migrateTableVersionFrom_v19_to_v19() {
onUpdate(database, 19, 19)
verifyZeroInteractions(database)
}
private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply {

View file

@ -18,6 +18,7 @@ import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import java.lang.reflect.Method
/**
* The unit test class for ContributionBoundaryCallbackTest
@ -95,7 +96,11 @@ class ContributionBoundaryCallbackTest {
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
Single.just(listOf(media()))
)
contributionBoundaryCallback.fetchContributions()
val method: Method = ContributionBoundaryCallback::class.java.getDeclaredMethod(
"fetchContributions"
)
method.isAccessible = true
method.invoke(contributionBoundaryCallback)
verify(repository).save(anyList());
verify(mediaClient).getMediaListForUser(anyString());
}
@ -104,7 +109,11 @@ class ContributionBoundaryCallbackTest {
fun testFetchContributionsFailed() {
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(Single.error(Exception("Error")))
contributionBoundaryCallback.fetchContributions()
val method: Method = ContributionBoundaryCallback::class.java.getDeclaredMethod(
"fetchContributions"
)
method.isAccessible = true
method.invoke(contributionBoundaryCallback)
verifyZeroInteractions(repository);
verify(mediaClient).getMediaListForUser(anyString());
}

View file

@ -177,20 +177,103 @@ class RecentLanguagesDaoUnitTest {
@Test
fun migrateTableVersionFrom_v5_to_v6() {
onUpdate(database, 5, 6)
// Table didnt exist before v7
// Table didnt exist in version 6
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v6_to_v7() {
onUpdate(database, 6, 7)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
// Table didnt exist in version 7
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v7_to_v8() {
onUpdate(database, 7, 8)
// Table didnt change in version 8
// Table didnt exist in version 8
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v8_to_v9() {
onUpdate(database, 8, 9)
// Table didnt exist in version 9
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v9_to_v10() {
onUpdate(database, 9, 10)
// Table didnt exist in version 10
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v10_to_v11() {
onUpdate(database, 10, 11)
// Table didnt exist in version 11
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v11_to_v12() {
onUpdate(database, 11, 12)
// Table didnt exist in version 12
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v12_to_v13() {
onUpdate(database, 12, 13)
// Table didnt exist in version 13
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v13_to_v14() {
onUpdate(database, 13, 14)
// Table didnt exist in version 14
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v14_to_v15() {
onUpdate(database, 14, 15)
// Table didnt exist in version 15
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v15_to_v16() {
onUpdate(database, 15, 16)
// Table didnt exist in version 16
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v16_to_v17() {
onUpdate(database, 16, 17)
// Table didnt exist in version 17
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v18_to_v19() {
onUpdate(database, 18, 19)
// Table didnt exist in version 18
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v19_to_v20() {
onUpdate(database, 19, 20)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun migrateTableVersionFrom_v20_to_v20() {
onUpdate(database, 20, 20)
verifyZeroInteractions(database)
}

View file

@ -8,6 +8,7 @@ import android.telephony.TelephonyManager;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import fr.free.nrw.commons.utils.model.NetworkConnectionType;
@ -97,6 +98,7 @@ public class NetworkUtilsTest {
}
@Test
@Ignore("Fix these test with telemetry permission")
public void testCellular2GNetwork() {
Context mockContext = mock(Context.class);
Application mockApplication = mock(Application.class);
@ -123,6 +125,7 @@ public class NetworkUtilsTest {
}
@Test
@Ignore("Fix these test with telemetry permission")
public void testCellular3GNetwork() {
Context mockContext = mock(Context.class);
Application mockApplication = mock(Application.class);
@ -149,6 +152,7 @@ public class NetworkUtilsTest {
}
@Test
@Ignore("Fix these test with telemetry permission")
public void testCellular4GNetwork() {
Context mockContext = mock(Context.class);
Application mockApplication = mock(Application.class);