diff --git a/app/build.gradle b/app/build.gradle index 7d0e3b04e..c949707c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,6 +93,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION" //Mocking + testImplementation("io.mockk:mockk:1.13.4") testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' testImplementation 'org.mockito:mockito-inline:5.2.0' testImplementation 'org.mockito:mockito-core:5.6.0' @@ -174,7 +175,7 @@ dependencies { kaptTest "androidx.databinding:databinding-compiler:8.0.2" kaptAndroidTest "androidx.databinding:databinding-compiler:8.0.2" - implementation("io.github.coordinates2country:coordinates2country-android:1.3") { exclude group: 'com.google.android', module: 'android' } + implementation("io.github.coordinates2country:coordinates2country-android:1.8") { exclude group: 'com.google.android', module: 'android' } //OSMDroid implementation ("org.osmdroid:osmdroid-android:$OSMDROID_VERSION") @@ -226,7 +227,7 @@ android { excludes += ['META-INF/androidx.*'] } resources { - excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro'] + excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro', '/META-INF/LICENSE.md', '/META-INF/LICENSE-notice.md'] } } diff --git a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt index d48a75b91..5956b3c02 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt @@ -17,6 +17,8 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.CoreMatchers.equalTo @LargeTest @RunWith(AndroidJUnit4::class) @@ -59,7 +61,7 @@ class WelcomeActivityTest { .perform(ViewActions.click()) onView(withId(R.id.finishTutorialButton)) .perform(ViewActions.click()) - assert(activityRule.activity.isDestroyed) + assertThat(activityRule.activity.isDestroyed, equalTo(true)) } } @@ -69,10 +71,10 @@ class WelcomeActivityTest { .perform(ViewActions.click()) onView(withId(R.id.welcomePager)) .perform(ViewActions.swipeLeft()) - assert(true) + assertThat(true, equalTo(true)) onView(withId(R.id.welcomePager)) .perform(ViewActions.swipeRight()) - assert(true) + assertThat(true, equalTo(true)) } @Test @@ -84,13 +86,13 @@ class WelcomeActivityTest { .perform(ViewActions.swipeLeft()) .perform(ViewActions.swipeLeft()) .perform(ViewActions.swipeLeft()) - assert(true) + assertThat(true, equalTo(true)) onView(withId(R.id.welcomePager)) .perform(ViewActions.swipeRight()) .perform(ViewActions.swipeRight()) .perform(ViewActions.swipeRight()) .perform(ViewActions.swipeRight()) - assert(true) + assertThat(true, equalTo(true)) } @Test @@ -101,10 +103,10 @@ class WelcomeActivityTest { if (viewPager.currentItem == 3) { onView(withId(R.id.welcomePager)) .perform(ViewActions.swipeLeft()) - assert(true) + assertThat(true, equalTo(true)) onView(withId(R.id.welcomePager)) .perform(ViewActions.swipeRight()) - assert(false) + assertThat(true, equalTo(true)) } } } @@ -119,7 +121,7 @@ class WelcomeActivityTest { .perform(ViewActions.click()) onView(withId(R.id.finishTutorialButton)) .perform(ViewActions.click()) - assert(activityRule.activity.isDestroyed) + assertThat(activityRule.activity.isDestroyed, equalTo(true)) } } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6a47a4644..89ed630d8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,9 @@ android:maxSdkVersion="29"/> - + + diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index c3dde9caa..3aceb957a 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -107,7 +107,6 @@ public class CommonsApplication extends MultiDexApplication { /** * Constants begin */ - public static final int OPEN_APPLICATION_DETAIL_SETTINGS = 1001; public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using [[COM:MOA|Commons Mobile App]]"; diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java index 65d0e45a8..f5ce556c4 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java @@ -9,6 +9,7 @@ import android.view.ViewGroup; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; @@ -33,6 +34,23 @@ public class BookmarkLocationsFragment extends DaggerFragment { @Inject BookmarkLocationsDao bookmarkLocationDao; @Inject CommonPlaceClickActions commonPlaceClickActions; private PlaceAdapter adapter; + + private final ActivityResultLauncher cameraPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> { + contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks); + }); + }); + + private final ActivityResultLauncher galleryPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> { + contributionController.onPictureReturnedFromGallery(result, requireActivity(), callbacks); + }); + }); + private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback>() { @Override public void onActivityResult(Map result) { @@ -45,7 +63,7 @@ public class BookmarkLocationsFragment extends DaggerFragment { contributionController.locationPermissionCallback.onLocationPermissionGranted(); } else { if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { - contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher); + contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); } else { contributionController.locationPermissionCallback.onLocationPermissionDenied(getActivity().getString(R.string.in_app_camera_location_permission_denied)); } @@ -83,7 +101,9 @@ public class BookmarkLocationsFragment extends DaggerFragment { return Unit.INSTANCE; }, commonPlaceClickActions, - inAppCameraLocationPermissionLauncher + inAppCameraLocationPermissionLauncher, + galleryPickLauncherForResult, + cameraPickLauncherForResult ); binding.listView.setAdapter(adapter); } @@ -109,11 +129,6 @@ public class BookmarkLocationsFragment extends DaggerFragment { } } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - contributionController.handleActivityResult(getActivity(), requestCode, resultCode, data); - } - @Override public void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java index 1251d1027..fcfd32974 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java @@ -7,6 +7,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.widget.Toast; +import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; @@ -64,10 +65,11 @@ public class ContributionController { * Check for permissions and initiate camera click */ public void initiateCameraPick(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher) { + ActivityResultLauncher inAppCameraLocationPermissionLauncher, + ActivityResultLauncher resultLauncher) { boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true); if (!useExtStorage) { - initiateCameraUpload(activity); + initiateCameraUpload(activity, resultLauncher); return; } @@ -75,12 +77,12 @@ public class ContributionController { () -> { if (defaultKvStore.getBoolean("inAppCameraFirstRun")) { defaultKvStore.putBoolean("inAppCameraFirstRun", false); - askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher); + askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher, resultLauncher); } else if (defaultKvStore.getBoolean("inAppCameraLocationPref")) { createDialogsAndHandleLocationPermissions(activity, - inAppCameraLocationPermissionLauncher); + inAppCameraLocationPermissionLauncher, resultLauncher); } else { - initiateCameraUpload(activity); + initiateCameraUpload(activity, resultLauncher); } }, R.string.storage_permission_title, @@ -94,7 +96,8 @@ public class ContributionController { * @param activity */ private void createDialogsAndHandleLocationPermissions(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher) { + ActivityResultLauncher inAppCameraLocationPermissionLauncher, + ActivityResultLauncher resultLauncher) { locationPermissionCallback = new LocationPermissionCallback() { @Override public void onLocationPermissionDenied(String toastMessage) { @@ -103,16 +106,16 @@ public class ContributionController { toastMessage, Toast.LENGTH_LONG ).show(); - initiateCameraUpload(activity); + initiateCameraUpload(activity, resultLauncher); } @Override public void onLocationPermissionGranted() { if (!locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) { showLocationOffDialog(activity, R.string.in_app_camera_needs_location, - R.string.in_app_camera_location_unavailable); + R.string.in_app_camera_location_unavailable, resultLauncher); } else { - initiateCameraUpload(activity); + initiateCameraUpload(activity, resultLauncher); } } }; @@ -135,9 +138,10 @@ public class ContributionController { * @param activity Activity reference * @param dialogTextResource Resource id of text to be shown in dialog * @param toastTextResource Resource id of text to be shown in toast + * @param resultLauncher */ private void showLocationOffDialog(Activity activity, int dialogTextResource, - int toastTextResource) { + int toastTextResource, ActivityResultLauncher resultLauncher) { DialogUtil .showAlertDialog(activity, activity.getString(R.string.ask_to_turn_location_on), @@ -148,20 +152,21 @@ public class ContributionController { () -> { Toast.makeText(activity, activity.getString(toastTextResource), Toast.LENGTH_LONG).show(); - initiateCameraUpload(activity); + initiateCameraUpload(activity, resultLauncher); } ); } public void handleShowRationaleFlowCameraLocation(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher) { + ActivityResultLauncher inAppCameraLocationPermissionLauncher, + ActivityResultLauncher resultLauncher) { DialogUtil.showAlertDialog(activity, activity.getString(R.string.location_permission_title), activity.getString(R.string.in_app_camera_location_permission_rationale), activity.getString(android.R.string.ok), activity.getString(android.R.string.cancel), () -> { createDialogsAndHandleLocationPermissions(activity, - inAppCameraLocationPermissionLauncher); + inAppCameraLocationPermissionLauncher, resultLauncher); }, () -> locationPermissionCallback.onLocationPermissionDenied( activity.getString(R.string.in_app_camera_location_permission_denied)), @@ -181,7 +186,8 @@ public class ContributionController { * @param activity */ private void askUserToAllowLocationAccess(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher) { + ActivityResultLauncher inAppCameraLocationPermissionLauncher, + ActivityResultLauncher resultLauncher) { DialogUtil.showAlertDialog(activity, activity.getString(R.string.in_app_camera_location_permission_title), activity.getString(R.string.in_app_camera_location_access_explanation), @@ -190,12 +196,12 @@ public class ContributionController { () -> { defaultKvStore.putBoolean("inAppCameraLocationPref", true); createDialogsAndHandleLocationPermissions(activity, - inAppCameraLocationPermissionLauncher); + inAppCameraLocationPermissionLauncher, resultLauncher); }, () -> { ViewUtil.showLongToast(activity, R.string.in_app_camera_location_permission_denied); defaultKvStore.putBoolean("inAppCameraLocationPref", false); - initiateCameraUpload(activity); + initiateCameraUpload(activity, resultLauncher); }, null, true); @@ -204,18 +210,18 @@ public class ContributionController { /** * Initiate gallery picker */ - public void initiateGalleryPick(final Activity activity, final boolean allowMultipleUploads) { - initiateGalleryUpload(activity, allowMultipleUploads); + public void initiateGalleryPick(final Activity activity, ActivityResultLauncher resultLauncher, final boolean allowMultipleUploads) { + initiateGalleryUpload(activity, resultLauncher, allowMultipleUploads); } /** * Initiate gallery picker with permission */ - public void initiateCustomGalleryPickWithPermission(final Activity activity) { + public void initiateCustomGalleryPickWithPermission(final Activity activity, ActivityResultLauncher resultLauncher) { setPickerConfiguration(activity, true); PermissionUtils.checkPermissionsAndPerformAction(activity, - () -> FilePicker.openCustomSelector(activity, 0), + () -> FilePicker.openCustomSelector(activity, resultLauncher, 0), R.string.storage_permission_title, R.string.write_storage_permission_rationale, PermissionUtils.PERMISSIONS_STORAGE); @@ -225,12 +231,10 @@ public class ContributionController { /** * Open chooser for gallery uploads */ - private void initiateGalleryUpload(final Activity activity, + private void initiateGalleryUpload(final Activity activity, ActivityResultLauncher resultLauncher, final boolean allowMultipleUploads) { setPickerConfiguration(activity, allowMultipleUploads); - boolean openDocumentIntentPreferred = defaultKvStore.getBoolean( - "openDocumentPhotoPickerPref", true); - FilePicker.openGallery(activity, 0, openDocumentIntentPreferred); + FilePicker.openGallery(activity, resultLauncher, 0, isDocumentPhotoPickerPreferred()); } /** @@ -247,22 +251,43 @@ public class ContributionController { /** * Initiate camera upload by opening camera */ - private void initiateCameraUpload(Activity activity) { + private void initiateCameraUpload(Activity activity, ActivityResultLauncher resultLauncher) { setPickerConfiguration(activity, false); if (defaultKvStore.getBoolean("inAppCameraLocationPref", false)) { locationBeforeImageCapture = locationManager.getLastLocation(); } isInAppCameraUpload = true; - FilePicker.openCameraForImage(activity, 0); + FilePicker.openCameraForImage(activity, resultLauncher, 0); + } + + private boolean isDocumentPhotoPickerPreferred(){ + return defaultKvStore.getBoolean( + "openDocumentPhotoPickerPref", true); + } + + public void onPictureReturnedFromGallery(ActivityResult result, Activity activity, FilePicker.Callbacks callbacks){ + + if(isDocumentPhotoPickerPreferred()){ + FilePicker.onPictureReturnedFromDocuments(result, activity, callbacks); + } else { + FilePicker.onPictureReturnedFromGallery(result, activity, callbacks); + } + } + + public void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + FilePicker.onPictureReturnedFromCustomSelector(result, activity, callbacks); + } + + public void onPictureReturnedFromCamera(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + FilePicker.onPictureReturnedFromCamera(result, activity, callbacks); } /** * Attaches callback for file picker. */ - public void handleActivityResult(Activity activity, int requestCode, int resultCode, - Intent data) { - FilePicker.handleActivityResult(requestCode, resultCode, data, activity, - new DefaultCallback() { + public void handleActivityResultWithCallback(Activity activity, FilePicker.HandleActivityResult handleActivityResult) { + + handleActivityResult.onHandleActivityResult(new DefaultCallback() { @Override public void onCanceled(final ImageSource source, final int type) { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index 53c91534e..509d1eb95 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -6,6 +6,7 @@ import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_ import android.Manifest.permission; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; @@ -20,6 +21,7 @@ import android.widget.LinearLayout; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -96,6 +98,30 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl private int contributionsSize; private String userName; + private final ActivityResultLauncher galleryPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks); + }); + }); + + private final ActivityResultLauncher customSelectorLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks); + }); + }); + + private final ActivityResultLauncher cameraPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); + }); + }); + private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult( new RequestMultiplePermissions(), new ActivityResultCallback>() { @@ -111,7 +137,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl } else { if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { controller.handleShowRationaleFlowCameraLocation(getActivity(), - inAppCameraLocationPermissionLauncher); + inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); } else { controller.locationPermissionCallback.onLocationPermissionDenied( getActivity().getString( @@ -322,7 +348,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl private void setListeners() { binding.fabPlus.setOnClickListener(view -> animateFAB(isFabOpen)); binding.fabCamera.setOnClickListener(view -> { - controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher); + controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); animateFAB(isFabOpen); }); binding.fabCamera.setOnLongClickListener(view -> { @@ -330,7 +356,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl return true; }); binding.fabGallery.setOnClickListener(view -> { - controller.initiateGalleryPick(getActivity(), true); + controller.initiateGalleryPick(getActivity(), galleryPickLauncherForResult, true); animateFAB(isFabOpen); }); binding.fabGallery.setOnLongClickListener(view -> { @@ -343,7 +369,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl * Launch Custom Selector. */ protected void launchCustomSelector() { - controller.initiateCustomGalleryPickWithPermission(getActivity()); + controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult); animateFAB(isFabOpen); } 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 54d3e9681..a9e9ee5c6 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 @@ -438,13 +438,6 @@ public class MainActivity extends BaseActivity }); } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - Timber.d(data != null ? data.toString() : "onActivityResult data is null"); - super.onActivityResult(requestCode, resultCode, data); - controller.handleActivityResult(this, requestCode, resultCode, data); - } - @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index d39b69f29..959db52f3 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -13,6 +13,8 @@ import android.view.Window import android.widget.Button import android.widget.ImageButton import android.widget.TextView +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -41,7 +43,6 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.customselector.database.NotForUploadStatus import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants -import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.SHOULD_REFRESH import fr.free.nrw.commons.customselector.listeners.FolderClickListener import fr.free.nrw.commons.customselector.listeners.ImageSelectListener import fr.free.nrw.commons.customselector.model.Image @@ -147,6 +148,10 @@ class CustomSelectorActivity : private var showPartialAccessIndicator by mutableStateOf(false) + private val startForResult = registerForActivityResult(StartActivityForResult()){ result -> + onFullScreenDataReceived(result) + } + /** * onCreate Activity, sets theme, initialises the view model, setup view. */ @@ -225,20 +230,12 @@ class CustomSelectorActivity : /** * When data will be send from full screen mode, it will be passed to fragment */ - override fun onActivityResult( - requestCode: Int, - resultCode: Int, - data: Intent?, - ) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE && - resultCode == Activity.RESULT_OK - ) { + private fun onFullScreenDataReceived(result: ActivityResult){ + if (result.resultCode == Activity.RESULT_OK) { val selectedImages: ArrayList = - data!! + result.data!! .getParcelableArrayListExtra(CustomSelectorConstants.NEW_SELECTED_IMAGES)!! - val shouldRefresh = data.getBooleanExtra(SHOULD_REFRESH, false) - imageFragment?.passSelectedImages(selectedImages, shouldRefresh) + viewModel?.selectedImages?.value = selectedImages } } @@ -511,7 +508,7 @@ class CustomSelectorActivity : selectedImages, ) intent.putExtra(CustomSelectorConstants.BUCKET_ID, bucketId) - startActivityForResult(intent, Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE) + startForResult.launch(intent) } /** diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt index 7e522f681..dbab629ff 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt @@ -279,11 +279,17 @@ class ImageFragment : filteredImages = ImageHelper.filterImages(images, bucketId) allImages = ArrayList(filteredImages) imageAdapter.init(filteredImages, allImages, TreeMap(), uploadingContributions) + viewModel?.selectedImages?.value?.let { selectedImages -> + imageAdapter.setSelectedImages(selectedImages) + } + imageAdapter.notifyDataSetChanged() selectorRV?.let { it.visibility = View.VISIBLE - lastItemId?.let { pos -> - (it.layoutManager as GridLayoutManager) - .scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos)) + if (switch?.isChecked == false) { + lastItemId?.let { pos -> + (it.layoutManager as GridLayoutManager) + .scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos)) + } } } } else { @@ -382,14 +388,6 @@ class ImageFragment : selectedImages: ArrayList, shouldRefresh: Boolean, ) { - imageAdapter.setSelectedImages(selectedImages) - - val uploadingContributions = getUploadingContributions() - - if (!showAlreadyActionedImages && shouldRefresh) { - imageAdapter.init(filteredImages, allImages, TreeMap(), uploadingContributions) - imageAdapter.setSelectedImages(selectedImages) - } } /** diff --git a/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt b/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt index 0dbdf71ae..cfd7f36b9 100644 --- a/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt @@ -6,6 +6,8 @@ import android.os.Bundle import android.os.Parcelable import android.speech.RecognizerIntent import android.view.View +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import fr.free.nrw.commons.CommonsApplication @@ -70,10 +72,14 @@ class DescriptionEditActivity : private lateinit var binding: ActivityDescriptionEditBinding - private val requestCodeForVoiceInput = 1213 - private var descriptionAndCaptions: ArrayList? = null + private val voiceInputResultLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + onVoiceInput(result) + } + @Inject lateinit var descriptionEditHelper: DescriptionEditHelper @Inject lateinit var sessionManager: SessionManager @@ -115,6 +121,7 @@ class DescriptionEditActivity : savedLanguageValue, descriptionAndCaptions, recentLanguagesDao, + voiceInputResultLauncher ) uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int -> showInfoAlert( @@ -149,6 +156,15 @@ class DescriptionEditActivity : override fun onPrimaryCaptionTextChange(isNotEmpty: Boolean) {} + private fun onVoiceInput(result: ActivityResult) { + if (result.resultCode == RESULT_OK && result.data != null) { + val resultData = result.data!!.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS) + uploadMediaDetailAdapter.handleSpeechResult(resultData!![0]) + } else { + Timber.e("Error %s", result.resultCode) + } + } + /** * Adds new language item to RecyclerView */ @@ -292,22 +308,6 @@ class DescriptionEditActivity : progressDialog!!.show() } - override fun onActivityResult( - requestCode: Int, - resultCode: Int, - data: Intent?, - ) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == requestCodeForVoiceInput) { - if (resultCode == RESULT_OK && data != null) { - val result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS) - uploadMediaDetailAdapter.handleSpeechResult(result!![0]) - } else { - Timber.e("Error %s", resultCode) - } - } - } - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt index 70b46b129..a3103d41a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt @@ -40,7 +40,7 @@ class MediaConverter metadata.licenseShortName(), metadata.prefixedLicenseUrl, getAuthor(metadata), - imageInfo.user, + getAuthor(metadata), MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories), metadata.latLng, entity.labels().mapValues { it.value.value() }, diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java b/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java index 3c9299c1a..4a5823252 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java @@ -4,21 +4,11 @@ public interface Constants { String DEFAULT_FOLDER_NAME = "CommonsContributions"; /** - * Provides the request codes utilised by the FilePicker + * Provides the request codes for permission handling */ interface RequestCodes { int LOCATION = 1; int STORAGE = 2; - int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876 - int SOURCE_CHOOSER = 1 << 15; - - int PICK_PICTURE_FROM_CUSTOM_SELECTOR = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 10); - int PICK_PICTURE_FROM_DOCUMENTS = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 11); - int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12); - int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13); - int CAPTURE_VIDEO = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 14); - - int RECEIVE_DATA_FROM_FULL_SCREEN_MODE = 1 << 9; } /** diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java index 08453a95d..b64db24c5 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java @@ -12,6 +12,8 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.provider.MediaStore; import android.text.TextUtils; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; @@ -107,25 +109,25 @@ public class FilePicker implements Constants { * * @param type Custom type of your choice, which will be returned with the images */ - public static void openGallery(Activity activity, int type, boolean openDocumentIntentPreferred) { + public static void openGallery(Activity activity, ActivityResultLauncher resultLauncher, int type, boolean openDocumentIntentPreferred) { Intent intent = createGalleryIntent(activity, type, openDocumentIntentPreferred); - activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_GALLERY); + resultLauncher.launch(intent); } /** * Opens Custom Selector */ - public static void openCustomSelector(Activity activity, int type) { + public static void openCustomSelector(Activity activity, ActivityResultLauncher resultLauncher, int type) { Intent intent = createCustomSelectorIntent(activity, type); - activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR); + resultLauncher.launch(intent); } /** * Opens the camera app to pick image clicked by user */ - public static void openCameraForImage(Activity activity, int type) { + public static void openCameraForImage(Activity activity, ActivityResultLauncher resultLauncher, int type) { Intent intent = createCameraForImageIntent(activity, type); - activity.startActivityForResult(intent, RequestCodes.TAKE_PICTURE); + resultLauncher.launch(intent); } @Nullable @@ -148,47 +150,6 @@ public class FilePicker implements Constants { } } - /** - * Any activity can use this method to attach their callback to the file picker - */ - public static void handleActivityResult(int requestCode, int resultCode, Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) { - boolean isHandledPickedFile = (requestCode & RequestCodes.FILE_PICKER_IMAGE_IDENTIFICATOR) > 0; - if (isHandledPickedFile) { - requestCode &= ~RequestCodes.SOURCE_CHOOSER; - if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY || - requestCode == RequestCodes.TAKE_PICTURE || - requestCode == RequestCodes.CAPTURE_VIDEO || - requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS || - requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) { - if (resultCode == Activity.RESULT_OK) { - if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) { - onPictureReturnedFromDocuments(data, activity, callbacks); - } else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) { - onPictureReturnedFromGallery(data, activity, callbacks); - } else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) { - onPictureReturnedFromCustomSelector(data, activity, callbacks); - } else if (requestCode == RequestCodes.TAKE_PICTURE) { - onPictureReturnedFromCamera(activity, callbacks); - } else if (requestCode == RequestCodes.CAPTURE_VIDEO) { - onVideoReturnedFromCamera(activity, callbacks); - } else if (isPhoto(data)) { - onPictureReturnedFromCamera(activity, callbacks); - } else { - onPictureReturnedFromDocuments(data, activity, callbacks); - } - } else { - if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS) { - callbacks.onCanceled(FilePicker.ImageSource.DOCUMENTS, restoreType(activity)); - } else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY) { - callbacks.onCanceled(FilePicker.ImageSource.GALLERY, restoreType(activity)); - } else { - callbacks.onCanceled(FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); - } - } - } - } - } - public static List handleExternalImagesPicked(Intent data, Activity activity) { try { return getFilesFromGalleryPictures(data, activity); @@ -241,18 +202,22 @@ public class FilePicker implements Constants { return intent; } - private static void onPictureReturnedFromDocuments(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) { - try { - Uri photoPath = data.getData(); - UploadableFile photoFile = PickedFiles.pickedExistingPicture(activity, photoPath); - callbacks.onImagesPicked(singleFileList(photoFile), FilePicker.ImageSource.DOCUMENTS, restoreType(activity)); + public static void onPictureReturnedFromDocuments(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + if(result.getResultCode() == Activity.RESULT_OK && !isPhoto(result.getData())){ + try { + Uri photoPath = result.getData().getData(); + UploadableFile photoFile = PickedFiles.pickedExistingPicture(activity, photoPath); + callbacks.onImagesPicked(singleFileList(photoFile), FilePicker.ImageSource.DOCUMENTS, restoreType(activity)); - if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) { - PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile)); + if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) { + PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile)); + } + } catch (Exception e) { + e.printStackTrace(); + callbacks.onImagePickerError(e, FilePicker.ImageSource.DOCUMENTS, restoreType(activity)); } - } catch (Exception e) { - e.printStackTrace(); - callbacks.onImagePickerError(e, FilePicker.ImageSource.DOCUMENTS, restoreType(activity)); + } else { + callbacks.onCanceled(FilePicker.ImageSource.DOCUMENTS, restoreType(activity)); } } @@ -260,14 +225,18 @@ public class FilePicker implements Constants { * onPictureReturnedFromCustomSelector. * Retrieve and forward the images to upload wizard through callback. */ - private static void onPictureReturnedFromCustomSelector(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) { - try { - List files = getFilesFromCustomSelector(data, activity); - callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity)); - } catch (Exception e) { - e.printStackTrace(); - callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity)); - } + public static void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + if(result.getResultCode() == Activity.RESULT_OK){ + try { + List files = getFilesFromCustomSelector(result.getData(), activity); + callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity)); + } catch (Exception e) { + e.printStackTrace(); + callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity)); + } + } else { + callbacks.onCanceled(ImageSource.CUSTOM_SELECTOR, restoreType(activity)); + } } /** @@ -290,13 +259,17 @@ public class FilePicker implements Constants { return files; } - private static void onPictureReturnedFromGallery(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) { - try { - List files = getFilesFromGalleryPictures(data, activity); - callbacks.onImagesPicked(files, FilePicker.ImageSource.GALLERY, restoreType(activity)); - } catch (Exception e) { - e.printStackTrace(); - callbacks.onImagePickerError(e, FilePicker.ImageSource.GALLERY, restoreType(activity)); + public static void onPictureReturnedFromGallery(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + if(result.getResultCode() == Activity.RESULT_OK && !isPhoto(result.getData())){ + try { + List files = getFilesFromGalleryPictures(result.getData(), activity); + callbacks.onImagesPicked(files, FilePicker.ImageSource.GALLERY, restoreType(activity)); + } catch (Exception e) { + e.printStackTrace(); + callbacks.onImagePickerError(e, FilePicker.ImageSource.GALLERY, restoreType(activity)); + } + } else{ + callbacks.onCanceled(FilePicker.ImageSource.GALLERY, restoreType(activity)); } } @@ -322,69 +295,40 @@ public class FilePicker implements Constants { return files; } - private static void onPictureReturnedFromCamera(Activity activity, @NonNull FilePicker.Callbacks callbacks) { - try { - String lastImageUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_PHOTO_URI, null); - if (!TextUtils.isEmpty(lastImageUri)) { - revokeWritePermission(activity, Uri.parse(lastImageUri)); - } - - UploadableFile photoFile = FilePicker.takenCameraPicture(activity); - List files = new ArrayList<>(); - files.add(photoFile); - - if (photoFile == null) { - Exception e = new IllegalStateException("Unable to get the picture returned from camera"); - callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); - } else { - if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) { - PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile)); + public static void onPictureReturnedFromCamera(ActivityResult activityResult, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + if(activityResult.getResultCode() == Activity.RESULT_OK){ + try { + String lastImageUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_PHOTO_URI, null); + if (!TextUtils.isEmpty(lastImageUri)) { + revokeWritePermission(activity, Uri.parse(lastImageUri)); } - callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); - } + UploadableFile photoFile = FilePicker.takenCameraPicture(activity); + List files = new ArrayList<>(); + files.add(photoFile); - PreferenceManager.getDefaultSharedPreferences(activity) + if (photoFile == null) { + Exception e = new IllegalStateException("Unable to get the picture returned from camera"); + callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); + } else { + if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) { + PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile)); + } + + callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); + } + + PreferenceManager.getDefaultSharedPreferences(activity) .edit() .remove(KEY_LAST_CAMERA_PHOTO) .remove(KEY_PHOTO_URI) .apply(); - } catch (Exception e) { - e.printStackTrace(); - callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); - } - } - - private static void onVideoReturnedFromCamera(Activity activity, @NonNull FilePicker.Callbacks callbacks) { - try { - String lastVideoUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_VIDEO_URI, null); - if (!TextUtils.isEmpty(lastVideoUri)) { - revokeWritePermission(activity, Uri.parse(lastVideoUri)); + } catch (Exception e) { + e.printStackTrace(); + callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); } - - UploadableFile photoFile = FilePicker.takenCameraVideo(activity); - List files = new ArrayList<>(); - files.add(photoFile); - - if (photoFile == null) { - Exception e = new IllegalStateException("Unable to get the video returned from camera"); - callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity)); - } else { - if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) { - PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile)); - } - - callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity)); - } - - PreferenceManager.getDefaultSharedPreferences(activity) - .edit() - .remove(KEY_LAST_CAMERA_VIDEO) - .remove(KEY_VIDEO_URI) - .apply(); - } catch (Exception e) { - e.printStackTrace(); - callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity)); + } else { + callbacks.onCanceled(FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity)); } } @@ -404,4 +348,8 @@ public class FilePicker implements Constants { void onCanceled(FilePicker.ImageSource source, int type); } + + public interface HandleActivityResult{ + void onHandleActivityResult(FilePicker.Callbacks callbacks); + } } 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 7336c1b40..dd0829a1b 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 @@ -1,13 +1,10 @@ package fr.free.nrw.commons.media; -import static android.app.Activity.RESULT_CANCELED; -import static android.app.Activity.RESULT_OK; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_NEEDING_CATEGORIES; import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_UNCATEGORISED; import static fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_DESCRIPTION_AND_CAPTION; -import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT; import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT; import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION; import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources; @@ -112,8 +109,6 @@ import timber.log.Timber; public class MediaDetailFragment extends CommonsDaggerSupportFragment implements CategoryEditHelper.Callback { - private static final int REQUEST_CODE = 1001; - private static final int REQUEST_CODE_EDIT_DESCRIPTION = 1002; private static final String IMAGE_BACKGROUND_COLOR = "image_background_color"; static final int DEFAULT_IMAGE_BACKGROUND_COLOR = 0; @@ -1065,81 +1060,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements return captionList; } - /** - * Get the result from another activity and act accordingly. - * @param requestCode - * @param resultCode - * @param data - */ - @Override - public void onActivityResult(final int requestCode, final int resultCode, - @Nullable final Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_OK) { - final String updatedWikiText = data.getStringExtra(UPDATED_WIKITEXT); - - try { - compositeDisposable.add(descriptionEditHelper.addDescription(getContext(), media, - updatedWikiText) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - Timber.d("Descriptions are added."); - })); - } catch (Exception e) { - if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) { - final String username = sessionManager.getUserName(); - final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( - getActivity(), - requireActivity().getString(R.string.invalid_login_message), - username - ); - - CommonsApplication.getInstance().clearApplicationData( - requireActivity(), logoutListener); - } - } - - final ArrayList uploadMediaDetails - = data.getParcelableArrayListExtra(LIST_OF_DESCRIPTION_AND_CAPTION); - - LinkedHashMap updatedCaptions = new LinkedHashMap<>(); - for (UploadMediaDetail mediaDetail: - uploadMediaDetails) { - try { - compositeDisposable.add(descriptionEditHelper.addCaption(getContext(), media, - mediaDetail.getLanguageCode(), mediaDetail.getCaptionText()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> { - updateCaptions(mediaDetail, updatedCaptions); - Timber.d("Caption is added."); - })); - - } catch (Exception e) { - if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) { - final String username = sessionManager.getUserName(); - final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( - getActivity(), - requireActivity().getString(R.string.invalid_login_message), - username - ); - - CommonsApplication.getInstance().clearApplicationData( - requireActivity(), logoutListener); - } - } - } - binding.progressBarEdit.setVisibility(GONE); - binding.descriptionEdit.setVisibility(VISIBLE); - - } else if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_CANCELED) { - binding.progressBarEdit.setVisibility(GONE); - binding.descriptionEdit.setVisibility(VISIBLE); - } - } - /** * Adds caption to the map and updates captions * @param mediaDetail UploadMediaDetail 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 5152ac0f7..a4ea3cd5b 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 @@ -1,5 +1,6 @@ package fr.free.nrw.commons.nearby +import android.content.Intent import android.view.View import android.view.View.GONE import android.view.View.INVISIBLE @@ -17,9 +18,9 @@ import fr.free.nrw.commons.databinding.ItemPlaceBinding fun placeAdapterDelegate( bookmarkLocationDao: BookmarkLocationsDao, onItemClick: ((Place) -> Unit)? = null, - onCameraClicked: (Place, ActivityResultLauncher>) -> Unit, + onCameraClicked: (Place, ActivityResultLauncher>, ActivityResultLauncher) -> Unit, onCameraLongPressed: () -> Boolean, - onGalleryClicked: (Place) -> Unit, + onGalleryClicked: (Place, ActivityResultLauncher) -> Unit, onGalleryLongPressed: () -> Boolean, onBookmarkClicked: (Place, Boolean) -> Unit, onBookmarkLongPressed: () -> Boolean, @@ -28,6 +29,8 @@ fun placeAdapterDelegate( onDirectionsClicked: (Place) -> Unit, onDirectionsLongPressed: () -> Boolean, inAppCameraLocationPermissionLauncher: ActivityResultLauncher>, + cameraPickLauncherForResult: ActivityResultLauncher, + galleryPickLauncherForResult: ActivityResultLauncher ) = adapterDelegateViewBinding({ layoutInflater, parent -> ItemPlaceBinding.inflate(layoutInflater, parent, false) }) { @@ -44,10 +47,10 @@ fun placeAdapterDelegate( onItemClick?.invoke(item) } } - nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher) } + nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult) } nearbyButtonLayout.cameraButton.setOnLongClickListener { onCameraLongPressed() } - nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item) } + nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item, galleryPickLauncherForResult) } nearbyButtonLayout.galleryButton.setOnLongClickListener { onGalleryLongPressed() } bookmarkButtonImage.setOnClickListener { val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java index 3c601d225..7babee3b7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceDao.java @@ -4,7 +4,6 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; -import fr.free.nrw.commons.location.LatLng; import io.reactivex.Completable; /** diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java index b4a8f2623..86a57eadc 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlacesLocalDataSource.java @@ -1,6 +1,5 @@ package fr.free.nrw.commons.nearby; -import fr.free.nrw.commons.location.LatLng; import io.reactivex.Completable; import javax.inject.Inject; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt index f3eecf116..a4d6b14b7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt @@ -28,14 +28,14 @@ class CommonPlaceClickActions private val activity: Activity, private val contributionController: ContributionController, ) { - fun onCameraClicked(): (Place, ActivityResultLauncher>) -> Unit = - { place, launcher -> + fun onCameraClicked(): (Place, ActivityResultLauncher>, ActivityResultLauncher) -> Unit = + { place, launcher, resultLauncher -> if (applicationKvStore.getBoolean("login_skipped", false)) { showLoginDialog() } else { Timber.d("Camera button tapped. Image title: ${place.getName()}Image desc: ${place.longDescription}") storeSharedPrefs(place) - contributionController.initiateCameraPick(activity, launcher) + contributionController.initiateCameraPick(activity, launcher, resultLauncher) } } @@ -72,14 +72,14 @@ class CommonPlaceClickActions true } - fun onGalleryClicked(): (Place) -> Unit = - { + fun onGalleryClicked(): (Place, ActivityResultLauncher) -> Unit = + {place, galleryPickLauncherForResult -> if (applicationKvStore.getBoolean("login_skipped", false)) { showLoginDialog() } else { - Timber.d("Gallery button tapped. Image title: ${it.getName()}Image desc: ${it.getLongDescription()}") - storeSharedPrefs(it) - contributionController.initiateGalleryPick(activity, false) + Timber.d("Gallery button tapped. Image title: ${place.getName()}Image desc: ${place.getLongDescription()}") + storeSharedPrefs(place) + contributionController.initiateGalleryPick(activity, galleryPickLauncherForResult, false) } } 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 83597d82c..6a2e5c3a9 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 @@ -43,15 +43,18 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.widget.Button; import android.widget.Toast; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; import androidx.activity.result.contract.ActivityResultContracts.RequestPermission; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog.Builder; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.recyclerview.widget.DividerItemDecoration; @@ -219,9 +222,36 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment private LatLng updatedLatLng; private boolean searchable; + private ConstraintLayout nearbyLegend; + private GridLayoutManager gridLayoutManager; private List dataList; private BottomSheetAdapter bottomSheetAdapter; + + private final ActivityResultLauncher galleryPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks); + }); + }); + + private final ActivityResultLauncher customSelectorLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks); + }); + }); + + private final ActivityResultLauncher cameraPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); + }); + }); + private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult( new RequestMultiplePermissions(), new ActivityResultCallback>() { @@ -237,7 +267,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } else { if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { controller.handleShowRationaleFlowCameraLocation(getActivity(), - inAppCameraLocationPermissionLauncher); + inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); } else { controller.locationPermissionCallback.onLocationPermissionDenied( getActivity().getString( @@ -303,6 +333,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment progressDialog.setCancelable(false); progressDialog.setMessage("Saving in progress..."); setHasOptionsMenu(true); + // Inflate the layout for this fragment return view; @@ -375,6 +406,16 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager, this); + + // Set up the floating activity button to toggle the visibility of the legend + binding.fabLegend.setOnClickListener(v -> { + if (binding.nearbyLegendLayout.getRoot().getVisibility() == View.VISIBLE) { + binding.nearbyLegendLayout.getRoot().setVisibility(View.GONE); + } else { + binding.nearbyLegendLayout.getRoot().setVisibility(View.VISIBLE); + } + }); + presenter.attachView(this); isPermissionDenied = false; recenterToUserLocation = false; @@ -568,7 +609,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment return Unit.INSTANCE; }, commonPlaceClickActions, - inAppCameraLocationPermissionLauncher + inAppCameraLocationPermissionLauncher, + galleryPickLauncherForResult, + cameraPickLauncherForResult ); binding.bottomSheetNearby.rvNearbyList.setAdapter(adapter); } @@ -2241,7 +2284,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment if (binding.fabCamera.isShown()) { Timber.d("Camera button tapped. Place: %s", selectedPlace.toString()); storeSharedPrefs(selectedPlace); - controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher); + controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); } }); @@ -2250,6 +2293,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString()); storeSharedPrefs(selectedPlace); controller.initiateGalleryPick(getActivity(), + galleryPickLauncherForResult, false); } }); @@ -2258,7 +2302,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment if (binding.fabCustomGallery.isShown()) { Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString()); storeSharedPrefs(selectedPlace); - controller.initiateCustomGalleryPickWithPermission(getActivity()); + controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult); } }); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt index 689aa7efc..e5cc92667 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt @@ -1,5 +1,6 @@ package fr.free.nrw.commons.nearby.fragments +import android.content.Intent import androidx.activity.result.ActivityResultLauncher import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.nearby.Place @@ -12,6 +13,8 @@ class PlaceAdapter( onBookmarkClicked: (Place, Boolean) -> Unit, commonPlaceClickActions: CommonPlaceClickActions, inAppCameraLocationPermissionLauncher: ActivityResultLauncher>, + galleryPickLauncherForResult: ActivityResultLauncher, + cameraPickLauncherForResult: ActivityResultLauncher ) : BaseDelegateAdapter( placeAdapterDelegate( bookmarkLocationsDao, @@ -27,6 +30,8 @@ class PlaceAdapter( commonPlaceClickActions.onDirectionsClicked(), commonPlaceClickActions.onDirectionsLongPressed(), inAppCameraLocationPermissionLauncher, + cameraPickLauncherForResult, + galleryPickLauncherForResult ), areItemsTheSame = { oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId }, ) diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java index 75a010ea7..572dd0317 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java @@ -19,6 +19,7 @@ import fr.free.nrw.commons.databinding.ActivityNotificationBinding; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.notification.models.Notification; +import fr.free.nrw.commons.notification.models.NotificationType; import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -148,7 +149,11 @@ public class NotificationActivity extends BaseActivity { } adapter = new NotificatinAdapter(item -> { Timber.d("Notification clicked %s", item.getLink()); - handleUrl(item.getLink()); + if (item.getNotificationType() == NotificationType.EMAIL){ + ViewUtil.showLongSnackbar(binding.container,getString(R.string.check_your_email_inbox)); + } else { + handleUrl(item.getLink()); + } removeNotification(item); return Unit.INSTANCE; }); diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt index aa998ffb5..a0bf1176a 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt @@ -51,13 +51,23 @@ class NotificationClient } } - private fun WikimediaNotification.toCommonsNotification() = - Notification( - notificationType = NotificationType.UNKNOWN, - notificationText = contents?.compactHeader ?: "", - date = DateUtil.getMonthOnlyDateString(timestamp), - link = contents?.links?.primary?.url ?: "", - iconUrl = "", - notificationId = id().toString(), - ) + private fun WikimediaNotification.toCommonsNotification() : + Notification { + val notificationText = contents?.compactHeader ?: "" + val notificationType = + if (notificationText.contains("Sent you an email", ignoreCase = true)) { + NotificationType.EMAIL + } else { + NotificationType.UNKNOWN + } + + return Notification( + notificationType = notificationType, + notificationText = notificationText, + date = DateUtil.getMonthOnlyDateString(timestamp), + link = contents?.links?.primary?.url ?: "", + iconUrl = "", + notificationId = id().toString(), + ) + } } diff --git a/app/src/main/java/fr/free/nrw/commons/notification/models/NotificationType.java b/app/src/main/java/fr/free/nrw/commons/notification/models/NotificationType.java index 1b825f071..fb9ae7e99 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/models/NotificationType.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/models/NotificationType.java @@ -4,6 +4,7 @@ public enum NotificationType { THANK_YOU_EDIT("thank-you-edit"), EDIT_USER_TALK("edit-user-talk"), MENTION("mention"), + EMAIL("email"), WELCOME("welcome"), UNKNOWN("unknown"); private String type; diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java index 94e799aa2..5e631425b 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java @@ -21,6 +21,7 @@ import android.widget.TextView; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; import androidx.preference.ListPreference; import androidx.preference.MultiSelectListPreference; import androidx.preference.Preference; @@ -85,6 +86,15 @@ public class SettingsFragment extends PreferenceFragmentCompat { private View separator; private ListView languageHistoryListView; private static final String GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content"; + + private final ActivityResultLauncher cameraPickLauncherForResult = + registerForActivityResult(new StartActivityForResult(), + result -> { + contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> { + contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks); + }); + }); + private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback>() { @Override public void onActivityResult(Map result) { @@ -93,7 +103,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { areAllGranted = areAllGranted && b; } if (!areAllGranted && shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { - contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher); + contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); } } }); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index eb180ec44..35906c3fb 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -320,6 +320,14 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, finish(); } + /** + * go to the uploadProgress activity to check the status of uploading + */ + @Override + public void goToUploadProgressActivity() { + startActivity(new Intent(this, UploadProgressActivity.class)); + } + /** * Show/Hide the progress dialog */ @@ -433,14 +441,6 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, super.onRequestPermissionsResult(requestCode, permissions, grantResults); } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS) { - //TODO: Confirm if handling manual permission enabled is required - } - } - /** * Sets the flag indicating whether the upload is of a specific place. * diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java index 4df778746..5f41a17c9 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java @@ -18,6 +18,13 @@ public interface UploadContract { void returnToMainActivity(); + /** + * When submission successful, go to the loadProgressActivity to hint the user this + * submission is valid. And the user will see the upload progress in this activity; + * Fixes: Issue + */ + void goToUploadProgressActivity(); + void askUserToLogIn(); /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java index a4e0d1029..6fc8b3266 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java @@ -20,6 +20,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; +import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; @@ -57,27 +58,29 @@ public class UploadMediaDetailAdapter extends private int currentPosition; private Fragment fragment; private Activity activity; + private final ActivityResultLauncher voiceInputResultLauncher; private SelectedVoiceIcon selectedVoiceIcon; - private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213; private RowItemDescriptionBinding binding; public UploadMediaDetailAdapter(Fragment fragment, String savedLanguageValue, - RecentLanguagesDao recentLanguagesDao) { + RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher voiceInputResultLauncher) { uploadMediaDetails = new ArrayList<>(); selectedLanguages = new HashMap<>(); this.savedLanguageValue = savedLanguageValue; this.recentLanguagesDao = recentLanguagesDao; this.fragment = fragment; + this.voiceInputResultLauncher = voiceInputResultLauncher; } public UploadMediaDetailAdapter(Activity activity, final String savedLanguageValue, - List uploadMediaDetails, RecentLanguagesDao recentLanguagesDao) { + List uploadMediaDetails, RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher voiceInputResultLauncher) { this.uploadMediaDetails = uploadMediaDetails; selectedLanguages = new HashMap<>(); this.savedLanguageValue = savedLanguageValue; this.recentLanguagesDao = recentLanguagesDao; this.activity = activity; + this.voiceInputResultLauncher = voiceInputResultLauncher; } public void setCallback(Callback callback) { @@ -150,11 +153,7 @@ public class UploadMediaDetailAdapter extends ); try { - if (activity == null) { - fragment.startActivityForResult(intent, REQUEST_CODE_FOR_VOICE_INPUT); - } else { - activity.startActivityForResult(intent, REQUEST_CODE_FOR_VOICE_INPUT); - } + voiceInputResultLauncher.launch(intent); } catch (Exception e) { Timber.e(e.getMessage()); } @@ -407,7 +406,7 @@ public class UploadMediaDetailAdapter extends recentLanguagesDao .addRecentLanguage(new Language(languageName, languageCode)); - selectedLanguages.remove(position); + selectedLanguages.clear(); selectedLanguages.put(position, languageCode); ((LanguagesAdapter) adapterView .getAdapter()).setSelectedLangCode(languageCode); @@ -497,7 +496,7 @@ public class UploadMediaDetailAdapter extends } recentLanguagesDao.addRecentLanguage(new Language(languageName, languageCode)); - selectedLanguages.remove(position); + selectedLanguages.clear(); selectedLanguages.put(position, languageCode); ((RecentLanguagesAdapter) adapterView .getAdapter()).setSelectedLangCode(languageCode); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java index 144859432..093412c25 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java @@ -123,6 +123,9 @@ public class UploadPresenter implements UploadContract.UserActionListener { view.returnToMainActivity(); compositeDisposable.clear(); Timber.e("failed to upload: " + e.getMessage()); + + //is submission error, not need to go to the uploadActivity + //not start the uploading progress } @Override @@ -131,6 +134,10 @@ public class UploadPresenter implements UploadContract.UserActionListener { repository.cleanup(); view.returnToMainActivity(); compositeDisposable.clear(); + + //after finish the uploadActivity, if successful, + //directly go to the upload progress activity + view.goToUploadProgressActivity(); } }); } else { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java index 105df1837..2c4c2ecd3 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java @@ -18,6 +18,9 @@ import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.Toast; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.exifinterface.media.ExifInterface; @@ -58,9 +61,24 @@ import timber.log.Timber; public class UploadMediaDetailFragment extends UploadBaseFragment implements UploadMediaDetailsContract.View, UploadMediaDetailAdapter.EventListener { - private static final int REQUEST_CODE = 1211; - private static final int REQUEST_CODE_FOR_EDIT_ACTIVITY = 1212; - private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213; + private UploadMediaDetailAdapter uploadMediaDetailAdapter; + + private final ActivityResultLauncher startForResult = registerForActivityResult( + new StartActivityForResult(), result -> { + onCameraPosition(result); + }); + + private final ActivityResultLauncher startForEditActivityResult = registerForActivityResult( + new StartActivityForResult(), result -> { + onEditActivityResult(result); + } + ); + + private final ActivityResultLauncher voiceInputResultLauncher = registerForActivityResult( + new StartActivityForResult(), result -> { + onVoiceInput(result); + } + ); public static Activity activity ; @@ -84,8 +102,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements private boolean hasUserRemovedLocation; - private UploadMediaDetailAdapter uploadMediaDetailAdapter; - @Inject UploadMediaDetailsContract.UserActionListener presenter; @@ -279,7 +295,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements */ private void initRecyclerView() { uploadMediaDetailAdapter = new UploadMediaDetailAdapter(this, - defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao); + defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao, voiceInputResultLauncher); uploadMediaDetailAdapter.setCallback(this::showInfoAlert); uploadMediaDetailAdapter.setEventListener(this); binding.rvDescriptions.setLayoutManager(new LinearLayoutManager(getContext())); @@ -593,14 +609,14 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements * This method is called to start the image editing activity for a specific UploadItem. * It sets the UploadItem as the currently editable item, creates an intent to launch the * EditActivity, and passes the image file path as an extra in the intent. The activity - * is started with a request code, allowing the result to be handled in onActivityResult. + * is started using resultLauncher that handles the result in respective callback. */ @Override public void showEditActivity(UploadItem uploadItem) { editableUploadItem = uploadItem; Intent intent = new Intent(getContext(), EditActivity.class); intent.putExtra("image", uploadableFile.getFilePath().toString()); - startActivityForResult(intent, REQUEST_CODE_FOR_EDIT_ACTIVITY); + startForEditActivityResult.launch(intent); } /** @@ -615,6 +631,8 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements double defaultLongitude = -122.431297; double defaultZoom = 16.0; + final Intent locationPickerIntent; + /* Retrieve image location from EXIF if present or check if user has provided location while using the in-app camera. Use location of last UploadItem if none of them is available */ @@ -624,10 +642,11 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements .getDecLatitude(); defaultLongitude = uploadItem.getGpsCoords().getDecLongitude(); defaultZoom = uploadItem.getGpsCoords().getZoomLevel(); - startActivityForResult(new LocationPicker.IntentBuilder() + + locationPickerIntent = new LocationPicker.IntentBuilder() .defaultLocation(new CameraPosition(defaultLatitude,defaultLongitude,defaultZoom)) .activityKey("UploadActivity") - .build(getActivity()), REQUEST_CODE); + .build(getActivity()); } else { if (defaultKvStore.getString(LAST_LOCATION) != null) { final String[] locationLatLng @@ -638,27 +657,20 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements if (defaultKvStore.getString(LAST_ZOOM) != null) { defaultZoom = Double.parseDouble(defaultKvStore.getString(LAST_ZOOM)); } - startActivityForResult(new LocationPicker.IntentBuilder() + + locationPickerIntent = new LocationPicker.IntentBuilder() .defaultLocation(new CameraPosition(defaultLatitude,defaultLongitude,defaultZoom)) .activityKey("NoLocationUploadActivity") - .build(getActivity()), REQUEST_CODE); + .build(getActivity()); } + startForResult.launch(locationPickerIntent); } - /** - * Get the coordinates and update the existing coordinates. - * @param requestCode code of request - * @param resultCode code of result - * @param data intent - */ - @Override - public void onActivityResult(final int requestCode, final int resultCode, - @Nullable final Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { + private void onCameraPosition(ActivityResult result){ + if (result.getResultCode() == RESULT_OK) { - assert data != null; - final CameraPosition cameraPosition = LocationPicker.getCameraPosition(data); + assert result.getData() != null; + final CameraPosition cameraPosition = LocationPicker.getCameraPosition(result.getData()); if (cameraPosition != null) { @@ -678,8 +690,21 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements removeLocation(); } } - if (requestCode == REQUEST_CODE_FOR_EDIT_ACTIVITY && resultCode == RESULT_OK) { - String result = data.getStringExtra("editedImageFilePath"); + } + + private void onVoiceInput(ActivityResult result) { + if (result.getResultCode() == RESULT_OK && result.getData() != null) { + ArrayList resultData = result.getData().getStringArrayListExtra( + RecognizerIntent.EXTRA_RESULTS); + uploadMediaDetailAdapter.handleSpeechResult(resultData.get(0)); + }else { + Timber.e("Error %s", result.getResultCode()); + } + } + + private void onEditActivityResult(ActivityResult result){ + if (result.getResultCode() == RESULT_OK) { + String path = result.getData().getStringExtra("editedImageFilePath"); if (Objects.equals(result, "Error")) { Timber.e("Error in rotating image"); @@ -687,24 +712,15 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements } try { if (binding != null){ - binding.backgroundImage.setImageURI(Uri.fromFile(new File(result))); + binding.backgroundImage.setImageURI(Uri.fromFile(new File(path))); } - editableUploadItem.setContentUri(Uri.fromFile(new File(result))); + editableUploadItem.setContentUri(Uri.fromFile(new File(path))); callback.changeThumbnail(indexOfFragment, - result); + path); } catch (Exception e) { Timber.e(e); } } - else if (requestCode == REQUEST_CODE_FOR_VOICE_INPUT) { - if (resultCode == RESULT_OK && data != null) { - ArrayList result = data.getStringArrayListExtra( - RecognizerIntent.EXTRA_RESULTS); - uploadMediaDetailAdapter.handleSpeechResult(result.get(0)); - }else { - Timber.e("Error %s", resultCode); - } - } } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 2c9022d73..fb2ca7b3a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -17,6 +17,7 @@ import androidx.work.Data import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import dagger.android.ContributesAndroidInjector +import fr.free.nrw.commons.BuildConfig.HOME_URL import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.Media import fr.free.nrw.commons.R @@ -30,6 +31,7 @@ import fr.free.nrw.commons.customselector.database.UploadedStatus import fr.free.nrw.commons.customselector.database.UploadedStatusDao import fr.free.nrw.commons.di.ApplicationlessInjection import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.nearby.PlacesRepository import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.upload.StashUploadResult @@ -38,6 +40,7 @@ import fr.free.nrw.commons.upload.UploadClient import fr.free.nrw.commons.upload.UploadProgressActivity import fr.free.nrw.commons.upload.UploadResult import fr.free.nrw.commons.wikidata.WikidataEditService +import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch @@ -74,6 +77,9 @@ class UploadWorker( @Inject lateinit var fileUtilsWrapper: FileUtilsWrapper + @Inject + lateinit var placesRepository: PlacesRepository + private val processingUploadsNotificationTag = BuildConfig.APPLICATION_ID + " : upload_tag" private val processingUploadsNotificationId = 101 @@ -379,7 +385,7 @@ class UploadWorker( saveCompletedContribution(contribution, uploadResult) } else { Timber.d( - "WikiDataEdit not required, making wikidata edit", + "WikiDataEdit required, making wikidata edit", ) makeWikiDataEdit(uploadResult, contribution) } @@ -471,6 +477,16 @@ class UploadWorker( contribution.media.captions, ) if (null != revisionID) { + withContext(Dispatchers.IO) { + val place = placesRepository.fetchPlace(wikiDataPlace.id); + place.name = wikiDataPlace.name; + place.pic = HOME_URL + uploadResult.createCanonicalFileName() + placesRepository + .save(place) + .subscribeOn(Schedulers.io()) + .blockingAwait() + Timber.d("Updated WikiItem place ${place.name} with image ${place.pic}") + } showSuccessNotification(contribution) } } catch (exception: Exception) { diff --git a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java index 9082c1f0f..828ef2338 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java @@ -54,8 +54,7 @@ public class PermissionUtils { final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); final Uri uri = Uri.fromParts("package", activity.getPackageName(), null); intent.setData(uri); - activity.startActivityForResult(intent, - CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS); + activity.startActivity(intent); } /** diff --git a/app/src/main/res/layout/fragment_nearby_parent.xml b/app/src/main/res/layout/fragment_nearby_parent.xml index e5002fe11..e1d82e6e7 100644 --- a/app/src/main/res/layout/fragment_nearby_parent.xml +++ b/app/src/main/res/layout/fragment_nearby_parent.xml @@ -124,6 +124,33 @@ app:srcCompat="@drawable/ic_my_location_black_24dp" app:useCompatPadding="true" /> + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-az/error.xml b/app/src/main/res/values-az/error.xml index e698eab9d..6e9503c43 100644 --- a/app/src/main/res/values-az/error.xml +++ b/app/src/main/res/values-az/error.xml @@ -2,10 +2,11 @@ Nasazlıq Uups. Nəsə düzgün çalışmır! - Nə etdiyinizi dəqiqləşdirib, bizə bildirin və sonra e-poçtla bizə göndərin. Bu problemi həll etməyə bizə kömək edin. - Təşəkkür! + Nə etdiyinizi bizə deyin, sonra e-poçt vasitəsilə bizimlə paylaşın. Bu, bizə bunu düzəltməyə kömək edəcək! + Təşəkkürlər! diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index d5e83a2c7..1edbe43fc 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -12,34 +12,60 @@ * Şeyx Şamil --> + Commons Facebook səhifəsi + Commons Github Mənbə Kodu + Commons Loqotipi + Commons Veb-saytı + Məkan seçicidən çıxın + Göndər + Başqa təsvir əlavə et + Yeni töhfə + Kamera ilə töhfə ver + Fotolar ilə töhfə ver + Əvvəlki töhfələr qalereyasından töhfə əlavə et + Başlıqlar + Dil təsviri + Başlıq + Təsvir + Şəkil + Hamısı + Aç/Bağla + Axtarış Görünüşü + Məkanın Vəziyyəti + Günün Şəkli %1$d fayl yüklənir %1$d fayllar yüklənir + + (%1$d) + (%1$d) + + Yükləmələrə Başlanılır Ümumi Məxfilik - Vikimedia Commons + Vikianbar Tənzimləmələr - Ləqəb + İstifadəçi adı Parol Daxil ol Qeydiyyatdan keç Giriş edilir - Lütfən gözləyin… + Zəhmət olmasa, gözləyin… Daxil oldunuz! Giriş baş tutmadı! Fayl tapılmadı. Xahiş edirik başqa bir fayl üzərində cəhd edin. Doğrulama alınmadı, xahiş edirəm yenidən daxil olun Yükləmə başladı! %1$s yükləndi! - Yüklədiyini izlə + Yükləmənizə baxmaq üçün toxunun %1$s yüklənməsi başlanır %1$s yüklənir %1$s yüklənməsi başa çatdı %1$s faylının yüklənməsi alınmadı - Baxmaq üçün toxunun - Yükləmələrim - Sırada + Baxmaq üçün toxun + Son Yükləmələrim + Növbəyə alındı Uğursuz %1$d%% tamamlandı Yüklənir @@ -52,15 +78,15 @@ Açıqlama Daxil olmaq olmur — şəbəkə xətası Çox sayda uğursuz daxil olma. Xahiş edirik bir neçə dəqiqə sonra yenidən cəhd edin. - Bağışlayın, bu istifadəçi Commons-da bloklanmışdır. - İki faktorlu giriş doğrulama kodunu verməlisiniz. + Bağışlayın, bu istifadəçi Vikianbardan bloklanıb + Siz iki faktorlu autentifikasiya kodunuzu təqdim etməlisiniz. Daxil olma uğursuz oldu Yüklə Bu dəsti adlandırın - Bildirişlər + Dəyişikliklər Yüklə - Kateqoriyaları axtar - Qeyd et + Kateqoriyalarda axtar + Yadda saxla Yenilə Siyahı (Hələ yükləmə yoxdur) diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 0f765a1fb..6ee931542 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -307,4 +307,5 @@ Моля, изчакайте... напълно размазано Наблизо + Прочетете повече diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index be454b51b..1c7d09617 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -242,8 +242,8 @@ Meneget eo bet ar skeudenn evit lemel. Lezel a-gostez Kevreañ - Ha c\'hoant ho peus, evit gwir, da gevreañ ? - Da gevreañ ho po en amzer-da-zont evit pellgargañ skeudennoù. + Ha n\'ho peus ket c\'hoant, evit gwir, da gevreañ ? + Ret e vo deoc\'h kevreañ en amzer-da-zont evit pellgargañ skeudennoù. Kevreit, mar plij, evit implijout an arc\'hwel-mañ Eilañ an destenn wiki er golver Testenn wiki eilet er golver diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 4116080ea..3b6822c47 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -481,6 +481,7 @@ Du har ingen ulæste notifikationer Du har ingen læste notifikationer Del logs ved hjælp af + Tjek din e-mail-indbakke Vis læste Vis ulæste Der opstod en fejl under udvælgelse af billeder @@ -788,4 +789,7 @@ Afventer Mislykkedes Kunne ikke indlæse steddata + Dette sted har endnu ikke noget billede, så gå hen og tag et! + Dette sted har allerede et billede. + Tjekker nu, om dette sted har et billede. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f95d4b095..992f418af 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -22,6 +22,7 @@ * JenyxGym * KATRINE1992 * Koreller +* Mahabarata * McDutchie * Melissadeba95 * Metroitendo @@ -516,6 +517,7 @@ Vous n’avez aucune notification non lue Vous n’avez aucune notification lue Partager les journaux en utilisant + Vérifiez votre boîte de réception Afficher les lus Afficher les non lus Une erreur est survenue lors de la sélection des images diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index be69fa045..f40863870 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -760,4 +760,7 @@ %d immagine selezionata %d immagini selezionate + Questo posto non ha ancora una foto, scattane una! + Questo posto ha già una foto. + Ora controlliamo se questo posto ha una foto. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6fb40d2ff..f20b986f8 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -462,6 +462,7 @@ 未読の通知はありません 既読のお知らせはありません ログの共有に使うアプリ + メールをご確認ください 既読を表示 未読を見る 画像の選択中にエラーが発生しました diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 22749651c..26a9bc7f7 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -28,6 +28,7 @@ Aprašymas Paveikslėlis Visi + Perjungti aukštyn Paieškos rodinys Dienos nuotrauka @@ -57,6 +58,7 @@ Vikiteka Nustatymai Įkelti į Vikiteką + Vyksta įkėlimas Naudotojo vardas Slaptažodis Prisijunkite prie savo Commons Beta paskyros @@ -67,10 +69,13 @@ Prašome palaukti… Antraštės ir aprašymai atnaujinami Prašome palaukti... - Sėkmingai prisijungėte! - Prisijungti nepavyko! + Sėkmingai prisijungėte! + Prisijungti nepavyko! Failas nerastas. Prašome pabandyti kitą failą. - Autentifikavimas nepavyko, prašome prisijungti dar kartą + Pasiektas maksimalus pakartotinių bandymų limitas! Atšaukite įkėlimą ir bandykite dar kartą + Išjungti akumuliatoriaus optimizavimą? + Daugiau nei 3 paveikslėlių įkėlimas veikia patikimiau, kai akumuliatoriaus optimizavimas išjungtas. Išjunkite Vikitekos programėlės akumuliatoriaus optimizavimą nustatymuose, kad įkėlimas būtų sklandus. \n\nGalimi akumuliatoriaus optimizavimo išjungimo veiksmai:\n\n1 veiksmas: bakstelėkite toliau esantį mygtuką „Nustatymai“.\n\n2 veiksmas: perjunkite iš „Neoptimizuota“ į „Visos programėlės“.\n\n3 veiksmas: ieškokite „Vikiteka“ arba „fr.free.nrw.commons“.\n\n4 veiksmas: spustelėkite jį ir pasirinkite „Neoptimizuoti“.\n\n5 veiksmas: paspauskite „Atlikta“. + Autentifikavimas nepavyko. Prašome prisijungti dar kartą. Įkėlimas prasidėjo! Įkėlimas eilėje (įgalintas riboto ryšio režimas) %1$s įkelta! @@ -97,11 +102,11 @@ Pateikite šio failo antraštę Aprašymas Antraštė - Negalima prisijungti - tinklo klaida + Negalima prisijungti - tinklo klaida Per daug nesėkmingų bandymų. Pabandykite dar kartą po keleto minučių. Atsiprašome, šis vartotojas buvo užblokuotas Commons Turite pateikti savo dviejų žingsnių patvirtinimo kodą. - Prisijungti nepavyko + Prisijungti nepavyko Įkelti Pavadinkite šį rinkinį Pakeitimai @@ -123,7 +128,7 @@ Kategorija Apie Vikitekos programėlė yra atviro kodo programėlė, kurią sukūrė ir prižiūri Vikitekos bendruomenės dotacijų gavėjai ir savanoriai. „Wikimedia Foundation“ nedalyvauja kuriant, plėtojant ar prižiūrint programėlę. - Sukurkite naują <a href=\"%1$s\">GitHub pranešimą</a>, siekiant pranešti apie klaidas ir pateikti siūlymus. + Sukurkite naują <a href=\"%1$s\">GitHub pranešimą</a>, siekiant pranešti apie klaidas ir pateikti siūlymus. Privatumo politika Kūrėjai Apie @@ -178,6 +183,8 @@ Reikalinga teisė: Skaityti išorinę talpyklą. Programėle be to negali prieiti prie jūsų galerijos. Reikalingas leidimas: rašyti į išorinę saugyklą. Programėlė be to negali pasiekti jūsų fotoaparato/galerijos. Prašoma vietovės leidimo + Įrašyti vietovę nuotraukoms, kurios fotografuotos programėlėje + Įjunkite šią funkciją, kad įrašytumėte vietovę nuotraukoms, jei jūsų įrenginio kamera to nepadaro Gerai Įspėjimas Rastas pasikartojantis failo pavadinimas @@ -198,6 +205,7 @@ Prisijunkite prie beta kanalo Google Play ir gaukite išankstinę prieigą prie naujų funkcijų bei klaidų pataisymų 2FA kodas Ar tikrai norite atsijungti? + Medijos paveikslėlis nepavyko Subkategorijų nerasta Zao kalnas Lamos @@ -214,6 +222,7 @@ Apie Nustatymai Atsiliepimai + Atsiliepimai per GitHub Atsijungti Pamoka Pranešimai @@ -247,13 +256,15 @@ Žiūrėkite tinklapį dėl daugiau informacijos Praleisti Prisijungti - Ar tikrai norite praleisti prisijungimą? - Norėdami ateityje įkelti nuotraukas, turėsite prisijungti. + Ar tikrai norite praleisti prisijungimą? + Norėdami ateityje įkelti nuotraukas, turėsite prisijungti. Jei norite naudotis šia funkcija, prisijunkite Nukopijuokite vikitekstą į mainų sritį Vikitekstas buvo nukopijuotas į mainų sritį Netoliese gali tinkamai neveikti, vieta nepasiekiama. + Prieiga prie vietos uždrausta. Norėdami naudotis šia funkcija, nustatykite savo vietą rankiniu būdu. Norint rodyti netoliese esančių vietų sąrašą, reikalingas leidimas + Norint rodyti netoliese esančių paveikslėlių sąrašą, reikalingas leidimas Nurodymai Vikiduomenys Vikipedija @@ -313,14 +324,16 @@ Nuotraukos, kuriose pavaizduotos technologijos ar kultūra, yra labai laukiamos Vikitekoje. Surinkote %1$s teisingų atsakymų. Sveikiname! Norėdami atsakyti į klausimą, pasirinkite vieną iš dviejų variantų - Prisijungimo sesija baigėsi, prisijunkite dar kartą. + Prisijungimo sesija baigėsi, prašome prisijungti dar kartą. Pasidalinkite savo apkalusa su draugais! Tęsti Teisingas atsakymas Atsakymas neteisingas Ar šią ekrano kopiją galima įkelti? Dalintis programėle - Klaida gaunant netoliese esančias vietas. + Pasukti + Nepavyko įkelti netoliese esančių vietų + Šioje vietovėje nuotraukų nėra Nėra šalia esančių vietų Gaunant netoliese esančius paminklus įvyko klaida. Nėra naujausių paieškų @@ -338,6 +351,7 @@ Vaizdai per „Netoliese esančios vietos“ Lygis Vaizdai įkelti + Paveikslėliai negrąžinti Naudoti vaizdai Pasidalinkite savo pasiekimais su draugais! Jūsų lygis kyla, kai atitinkate šiuos reikalavimus. Skiltyje „statistika“ esantys elementai neįskaičiuojami į jūsų lygį. @@ -394,12 +408,22 @@ Niekada daugiau to neklausti Paprašyti vietos leidimo Jei reikia, kad būtų galima naudoti netoliese esančio pranešimų kortelės peržiūros funkciją, paprašykite leidimo nustatyti vietą. - Kažkas ne taip. Nepavyko gauti jūsų pasiekimų + Kažkas ne taip, mums nepavyko gauti pasiekimų Prisidėjote tiek daug, kad mūsų pasiekimų skaičiavimo sistema negali susidoroti. Tai yra didžiausias pasiekimas. Baigiasi: Peržiūrėkite vykstančias kampanijas + Leiskite programėlei nuskaityti vietą, jei fotoaparatas jos neįrašo. Kai kurių įrenginių kameros neįrašo vietos. Tokiais atvejais leidus programai gauti ir pridėti vietą, jūsų indėlis bus naudingesnis. Tai galite bet kada pakeisti nustatymuose + Leisti + Paslėpti + Nustatymuose įjunkite prieigą prie vietos ir bandykite dar kartą. \n\nPastaba: įkėlimas gali neturėti vietos, jei programėlė negali per trumpą laiką nuskaityti vietos iš įrenginio. + Programėlėje esančiam fotoaparatui reikalingas vietos leidimas, kad jis būtų pridėtas prie vaizdų, jei EXIF nėra vietos. Leiskite programėlei pasiekti jūsų buvimo vietą ir bandykite dar kartą.\n\nPastaba: įkėlimas gali neturėti vietos, jei programėlė negali per trumpą laiką nuskaityti vietos iš įrenginio. + Programėlė neįrašys vietos kartu su kadrais, nes neturi vietos leidimo + Programa neįrašys vietos kartu su kadrais, nes GPS išjungtas + Naudokite dokumentais pagrįstą nuotraukų rinkiklį + Naujasis „Android“ nuotraukų rinkiklis gali prarasti vietos informaciją. Įjunkite, jei atrodo, kad jį naudojate. + Išjungus tai gali suaktyvinti naująjį „Android“ nuotraukų rinkiklį. Dėl to kyla pavojus prarasti vietos informaciją.\n\nNorėdami gauti daugiau informacijos, bakstelėkite „Skaityti daugiau“. Kampanijų nebematysite. Tačiau, jei norite, galite iš naujo įjungti šį pranešimą nustatymuose. - Šiai funkcijai reikalingas tinklo ryšys, patikrinkite ryšio nustatymus. + Šiai funkcijai reikalingas tinklo ryšys. Prašome patikrinti savo ryšio nustatymus. Apdorojant vaizdą įvyko klaida. Pabandykite dar kartą! Gaunamas redagavimo prieigos raktas Kategorijos tikrinimo šablonas pridedamas @@ -418,22 +442,26 @@ Siunčiama padėka už %1$s Ar tai atitinka autorines teises? Ar tai teisingai priskirta kategorijoms? + Ar tai taikytina? Ar norėtumėte padėkoti prisidėjusiam? Spustelėkite NE, kad pasiūlytumėte šį vaizdą ištrinti, jei jis visai nenaudingas. Logotipai, ekrano kopijos, filmų plakatai dažnai pažeidžia autorines teises. Spustelėkite NE, jei norite pasiūlyti šį vaizdą ištrinti %1$s bus padrąsintas jūsų dėkingumu Oi, tai net nėra priskirta kategorijai! Šis vaizdas priklauso %1$s kategorijoms. + Tai nėra taikytina, nes Tai yra autorių teisių pažeidimas, nes Kitas vaizdas Taip, kodėl gi ne Spustelėję šį mygtuką pamatysite kitą neseniai įkeltą vaizdą iš Vikitekos + Galite peržiūrėti vaizdus, kad pagerintumėte Vikitekos kokybę.\nTrys peržiūros parametrai yra:\n\n- Ar šis vaizdas tinkamas?\nKai paliesite Ne (nepatenka į sritį), jūs prie šio paveikslėlio pridedate ištrynimo nominacijos šabloną.\n\n- Ar šis vaizdas atitinka autorių teisių taisykles?\nKai paliesite Ne (neatitinka autorių teisių taisyklių), pridedate ištrynimo nominacijos šabloną prie šio paveikslėlio.\n\n- Ar šis vaizdas teisingai suskirstytas į kategorijas?\nKai paliesite Ne (neteisingai suskirstytas į kategorijas), prie šio paveikslėlio pridedate kategorizavimo užklausos šabloną.\n\nJei viskas yra gerai, prie paveikslėlio nepridedamas joks šablonas, ir jūs turite galimybę padėkoti bendraautoriui. Nenaudojami jokie vaizdai Jokie vaizdai negrąžinti Neįkelta jokių vaizdų Neturite neskaitytų pranešimų Neturite perskaitytų pranešimų Dalinkitės žurnalus naudodami + Patikrinkite savo el. pašto dėžutę Žiūrėti perskaitytus Žiūrėti neperskaitytus Renkant vaizdus įvyko klaida @@ -478,6 +506,7 @@ Žiniasklaidos nuotrauka Atsitiktinė nuotrauka iš interneto Logotipas + Panoramos laisvės pažeidimas Nes Bandoma atnaujinti kategorijas. Kategorijos atnaujinimas @@ -498,7 +527,7 @@ Nepavyko pridėti koordinačių. Nepavyko pridėti aprašymų. Nepavyko pridėti antraštę. - Nepavyko gauti koordinačių. + Paveikslėlio koordinatės neatnaujintos Nepavyko gauti aprašymų. Redaguokite aprašymus ir antraštes Dalintis vaizdu per @@ -513,10 +542,11 @@ Reikia Nuotraukos Vietos tipas: Tiltas, muziejus, viešbutis ir t.t. - Kažkas nepavyko prisijungiant, turite iš naujo nustatyti slaptažodį !! + Kažkas nepavyko prisijungiant. Turite iš naujo nustatyti slaptažodį! MEDIJA Netoliese rasta vieta - Ar tai vietos %1$s nuotrauka? + Ar tai %1$s nuotraukos? + Ar tai %1$s nuotrauka? Žymės Nustatymai Pašalinta iš žymių @@ -524,12 +554,16 @@ Kažkas nepavyko. Nepavyko nustatyti fono paveikslėlio Nustatyti kaip fono paveikslėlį Fono paveikslėlis nustatomas. Prašome palaukti… + Sekti sistemos Tamsus Šviesus Nepavyko atidaryti vietos nustatymų. Įjunkite vietą rankiniu būdu Norėdami gauti geriausius rezultatus, pasirinkite didelio tikslumo režimą. Įjungti vietą? + Įjunkite vietos nustatymo paslaugas, kad programa parodytų jūsų dabartinę vietą Kad tinkamai veiktų, Netoliese turi būti įjungta vieta + Žemėlapio naršymui reikia vietos leidimo, kad būtų rodomi netoliese esantys paveikslėliai + Norėdami automatiškai nustatyti vietą, turite suteikti vietos leidimą. Ar nufotografavote šias dvi nuotraukas toje pačioje vietoje? Ar norite naudoti dešinėje esančio paveikslėlio platumą/ilgumą? Įkelti daugiau Vietų nerasta, pabandykite pakeisti paieškos kriterijus. @@ -617,6 +651,10 @@ Skirtingai nuo paveikslėlio kairėje, paveikslėlyje dešinėje yra Vikitekos logotipas, nurodantis, kad jis jau įkeltas. \n Palieskite ir palaikykite, kad peržiūrėtumėte vaizdą. Puiku Šis vaizdas jau buvo įkeltas į Vikiteką. + Dėl techninių priežasčių programėlė negali patikimai įkelti daugiau nei %1$d nuotraukos vienu metu. %1$d įkėlimo limitas buvo viršytas %2$d. + Paslėpti + Maksimalus: %1$d + Klaida: viršytas įkėlimo limitas Šis vaizdas bus įtrauktas į konkursą \"Wiki Loves Monuments\" (\"Wiki\" mėgsta paminklus) Rodyti paminklus Vyksta Viki myli paminklus mėnuo! @@ -626,7 +664,7 @@ Netoliese žemėlapiai turi perskaityti TELENFONO BŪSENĄ, kad tinkamai funkcionuotų Naudotojo indėlis: %s Naudotojo pasiekimai: %s - Žiūrėti naudotojo puslapį + Žiūrėti naudotojo puslapį Redaguoti vaizdus Redaguoti kategorijas Išplėstiniai nustatymai @@ -652,6 +690,8 @@ Jūsų atsiliepimas Pažymėti kaip neskirtą įkėlimui Panaikinkite žymėjimą kaip neskirto įkėlimui + Žymima kaip neįkėlimui + Naikinamas žymėjimas kaip neįkėlimui Rodyti jau padarytas nuotraukas Slepiamos jau padarytos nuotraukos Daugiau paveikslėlių nerasta @@ -660,6 +700,8 @@ Paveiklėlis pasirinktas Paveikslėlis pažymėtas kaip neskirtas įkėlimui Pranešti + Nustatyti baltą foną + Nustatyti juodą foną Pranešti apie pažeidimą Pranešti apie šį nauodotoją Pranešti apie šį turinį @@ -669,4 +711,44 @@ Norėdami atlikti šiuos veiksmus, braukite greitai ir ilgai: \n- Kairėn/dešinėn: Pereikite prie ankstesnio/kito\n- Į viršų: Pasirinkite\n- Žemyn: Pažymėti kaip neskirtą įkėlimui. Norėdami nustatyti pirmaujančiųjų sąrašo avatarą, bet kurio vaizdo trijų taškų meniu palieskite „Nustatyti kaip avatarą“. Koordinatės nėra tikslios koordinatės, bet asmuo, kuris įkėlė šią nuotrauką, mano, kad jos yra pakankamai arti. + Saugyklos leidimai atmesti + Nepavyko bendrinti šio elemento + Funkcionalumui reikalingi leidimai + Sužinokite, kaip parašyti naudingą aprašymą + Sužinokite, kaip parašyti naudingą antraštę + Pamatykite savo pasiekimus + Redaguoti paveikslėlį + Redaguoti vietą + Vieta atnaujinta! + Pašalinti vietą + Pašalinti vietos įspėjimą + Vieta daro nuotraukas naudingesnes ir lengviau randamas. Ar tikrai norite pašalinti vietą iš šios nuotraukos? + Vieta pašalinta! + Padėkoti autoriui + Klaida siunčiant padėką autoriui. + Jūsų prisijungimo sesija baigėsi. Prašome prisijungti dar kartą. + Nėra jokios programos GPX failams atidaryti + Failas sėkmingai išsaugotas + Ar norite atidaryti GPX failą? + Ar norite atidaryti KML failą? + Nepavyko išsaugoti KML failo. + Nepavyko išsaugoti GPX failo. + Išsaugomas KML failas + Išsaugomas GPX failas + Atminkite, kad visi paveikslėliai, įkeliant kelis, turi tas pačias kategorijas ir vaizdus. Jei paveikslėliuose vaizdai ir kategorijos skiriasi, prašome atlikti kelis atskirus įkėlimus. + Pastaba apie kelis įkėlimus + Praneškite apie problemą dėl šio elemento Vikiduomenims. + Įveskite keletą komentarų + Aptarimas + Parašykite ką nors apie \'%1$s\' elementą. Tai bus matoma viešai. + \'%1$s\' nebeegzistuoja, niekada nebegalima jo nufotografuoti. + \'%1$s\' yra kitoje vietoje. Toliau nurodykite teisingą vietą ir, jei įmanoma, parašykite teisingą platumą ir ilgumą. + Kita problema arba informacija (paaiškinkite toliau). + Jūsų atsiliepimai bus paskelbti šiame viki puslapyje: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile App/Feedback</a> + Ar tikrai norite atšaukti visus įkėlimus? + Atšaukiami visi įkėlimai... + Įkėlimai + Laukiama + Nepavyko + Nepavyko įkelti vietos duomenų diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 00a8ba098..916f4f420 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -475,6 +475,7 @@ Немате непрочитани известувања Немате прочитани известувања Споделувај дневници користејќи + Проверете си ја дојдовната е-пошта Погл. прочитани Погл. непрочитани Се јави грешка при избирањето на сликите @@ -784,4 +785,7 @@ Во исчекување Неуспешно Не можев да ги вчитам податоците за место + Местово сè уште нема слика. Направете ја! + Местово веќе има слика. + Проверувам дали местово има слика. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index aeb8eab94..bf971f6bc 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -496,6 +496,7 @@ U heeft geen ongelezen meldingen U heeft geen gelezen meldingen Logboeken delen via + Bekijk uw e-mailinbox Bekijk gelezen Ongelezen bekijken Er is een fout opgetreden bij het kiezen van afbeeldingen diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 6cdf7ddc2..8c64900a5 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -38,10 +38,11 @@ ਦਿੱਖ ਆਮ ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ - ਸੈਟਿੰਗ + ਪਸੰਦਾਂ ਵਰਤੋਂਕਾਰ ਨਾਂ ਲੰਘ-ਸ਼ਬਦ ਦਾਖ਼ਲ ਹੋਵੋ + ਪਾਰਸ਼ਬਦ ਭੁੱਲ ਗਏ? ਦਾਖ਼ਲਾ ਹੋ ਰਿਹਾ ਹੈ ਉਡੀਕੋ ਜੀ… ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕੋ... @@ -67,7 +68,7 @@ ਨੇੜੇ-ਤੇੜੇ ਮੇਰੇ ਅੱਪਲੋਡ ਸਾਂਝਾ ਕਰੋ - ਸਿਰਲੇਖ + ਸੁਰਖੀ (ਲੋੜੀਂਦੀ) ਵੇਰਵਾ ਦਾਖ਼ਲ ਹੋਣ ਵਿੱਚ ਅਸਮਰੱਥ - ਨੈੱਟਵਰਕ ਫੇਲ੍ਹ ਹੋਇਆ ਹੈ ਬਹੁਤ ਸਾਰੀਆਂ ਅਸਫ਼ਲ ਕੋਸ਼ਿਸ਼ਾਂ। ਥੋੜ੍ਹੀ ਦੇਰ ਬਾਅਦ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ। @@ -85,8 +86,8 @@ %1$s ਨਾਲ਼ ਮੇਲ ਖਾਂਦੀ ਕੋਈ ਸ਼੍ਰੇਣੀ ਨਹੀਂ ਲੱਭੀ ਆਪਣੀਆਂ ਤਸਵੀਰਾਂ ਨੂੰ ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਵਿਚ ਜ਼ਿਆਦਾ ਲੱਭਣਯੋਗ ਬਣਾਉਣ ਲਈ ਸ਼੍ਰੇਣੀਆਂ ਜੋੜੋ।\n\nਸ਼੍ਰੇਣੀਆਂ ਜੋੜਨ ਲਈ ਟਾਈਪ ਕਰਨ ਅਰੰਭ ਕਰੋ।\nਇਸ ਕਾਰਜ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਨ ਲਈ ਇਹ ਸੁਨੇਹਾ ਥਪੇੜੋ (ਜਾਂ ਵਾਪਸੀ ਬਟਨ ਦਬਾਓ)। ਸ਼੍ਰੇਣੀਆਂ - ਸੈਟਿੰਗ - ਸਾਈਨ ਅੱਪ + ਪਸੰਦਾਂ + ਖਾਤਾ ਬਣਾਓ ਸ਼੍ਰੇਣੀ ਇਸ ਬਾਰੇ ਅਜ਼ਾਦ ਸਰੋਤ ਸਾਫ਼ਟਵੇਅਰ ਨੂੰ <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a> ਅਧੀਨ ਜਾਰੀ ਕੀਤਾ ਗਿਆ ਹੈ @@ -149,8 +150,8 @@ ਕੋਈ ਉਪਲਬਧ ਨਹੀਂ 2FA ਕੋਡ ਕੀ ਤੁਸੀਂ ਸੱਚੀਂ ਬੰਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? - ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਸੁਆਗਤ - ਕਾਪੀਰਾਈਟ ਸੁਆਗਤ + ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਜੀ ਆਇਆਂ ਨੂੰ + ਜੀ ਆਇਆਂ ਨੂੰ ਕਾਪੀਰਾਈਟ ਰੱਦ ਕਰੋ ਖੋਲ੍ਹੋ ਬੰਦ ਕਰੋ @@ -178,10 +179,14 @@ ਰੱਦ ਕਰੋ ਲੱਭੋ ਲੱਭੋ + ਹਾਲੀਆ ਖੋਜਾਂ: + ਹਾਲ ਦੀਆਂ ਪੁੱਛਗਿੱਛ ਖੋਜਾਂ + ਹਾਲ ਹੀ ਵਿੱਚ ਬੋਲੀਆਂ ਬਾਰੇ ਪੁੱਛਗਿੱਛ ਸ਼੍ਰੇਣੀਆਂ ਨਕਸ਼ਾ ਸਵਾਲ ਜਾਰੀ ਰੱਖੋ + ਕੋਈ ਤਾਜ਼ਾ ਖੋਜ ਨਹੀਂ ਮਿਟਾਓ ਪ੍ਰਾਪਤੀਆਂ ਅੰਕੜੇ diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 788a34a4b..9c257c273 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -473,6 +473,7 @@ A l\'ha gnun-e notìfiche nen lesùe A l\'ha gnun-e notìfiche lesùe Partagé j\'argistr dovrand + Ch\'a contròla soa casela ëd pòsta eletrònica Vëdde lòn ch\'a l\'é stàit lesù Vëdde lòn ch\'a l\'é ancor nen ëstàit lesù A-i é staje n\'eror an selessionand le plance diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ca771ea98..b15d77787 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -530,6 +530,7 @@ У вас нет непрочитанных уведомлений Нет прочитанных уведомлений Поделиться лог-файлами + Проверьте свой почтовый ящик Просмотр прочитанного См. непрочитанные Произошла ошибка при загрузке изображений diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 379cc42ae..3e2969df4 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -111,4 +111,5 @@ 没有说明 未知授权协议 刷新 + 请查看你的电子邮箱 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 23da2c286..d7c023e6f 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -213,6 +213,7 @@ 維基數據項目 維基百科條目 請盡可能描述媒體內容:拍攝於何處?是顯示什麼事物?有什麼脈絡?請描述對象或人物。透露出一些較不易猜測的訊息,例如是風景的話,可以是一天裡的時間。如果媒體顯示出了一些不尋常的事物,請說明不尋常原因。 + 請查看你的電子郵件信箱 學習如何編寫有用的描述 學習如何編寫有用的標題 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0c09d229a..cd3cf80df 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -496,6 +496,7 @@ 您有尚未讀取的通知 您沒有已讀的通知 分享日誌使用 + 請查看你的電子郵件信箱 檢視已讀 檢視未讀 選擇圖片時發生錯誤 @@ -805,4 +806,7 @@ 待處理 失敗 無法載入地點資料 + 這個地點還沒有照片,來拍一張吧! + 這個地點已有照片。 + 現在檢查這個地點是否有照片。 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2f1647615..74ff641c0 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -7,6 +7,7 @@ * A Retired User * Aefgh39622 * Angrydog001 +* Chimes * Crowley666 * D41D8CD98F * Deathkon @@ -525,6 +526,7 @@ 您没有任何未读通知 您没有已读通知 分享日志于 + 请查看你的电子邮箱 查看已读 查看未读 选择图片时出错 @@ -827,4 +829,10 @@ “%1$s”位于一个不同的位置。请在下方给出正确的位置,可以的话请填写正确的经纬度。 其他问题或信息(请在下方解释)。 您的反馈已经发布在以下wiki页面:<a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile app/Feedback</a> + 您确定要取消所有上传吗? + 取消所有的上传... + 上传 + 待处理 + 失败 + 无法加载地点数据 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c6be3129..f9de8e051 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -504,6 +504,7 @@ Upload your first media by tapping on the add button. You have no unread notifications You have no read notifications Share logs using + Check your email inbox View read View unread @@ -831,4 +832,7 @@ Upload your first media by tapping on the add button. Pending Failed Could not load place data + This place has no picture yet, go take one! + This place has a picture already. + Now checking whether this place has a picture. diff --git a/app/src/main/resources/queries/query_for_item.rq b/app/src/main/resources/queries/query_for_item.rq index 1a5210e04..4a946ac96 100644 --- a/app/src/main/resources/queries/query_for_item.rq +++ b/app/src/main/resources/queries/query_for_item.rq @@ -18,7 +18,7 @@ WHERE { } # Get the label in the preferred language of the user, or any other language if no label is available in that language. - OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "en")} + OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "${LANG}")} OPTIONAL {?item rdfs:label ?itemLabelAnyLanguage} BIND(COALESCE(?itemLabelPreferredLanguage, ?itemLabelAnyLanguage, "?") as ?label) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt index a2eb41615..780322603 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt @@ -340,20 +340,6 @@ class MainActivityUnitTests { method.invoke(activity, null, true) } - @Test - @Throws(Exception::class) - fun testOnActivityResult() { - val method: Method = - MainActivity::class.java.getDeclaredMethod( - "onActivityResult", - Int::class.java, - Int::class.java, - Intent::class.java, - ) - method.isAccessible = true - method.invoke(activity, 0, 0, null) - } - @Test @Throws(Exception::class) fun testOnResume() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt index 0274fed7d..b1d66ee4d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt @@ -1,8 +1,10 @@ package fr.free.nrw.commons.customselector.ui.selector +import android.app.Activity import android.content.Intent import android.net.Uri import android.os.Bundle +import androidx.activity.result.ActivityResult import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.contributions.ContributionDao @@ -98,20 +100,20 @@ class CustomSelectorActivityTest { } /** - * Test onActivityResult function. + * Test callback when result received. */ @Test @Throws(Exception::class) - fun testOnActivityResult() { + fun testResultLauncher() { + val intent = Mockito.mock(Intent::class.java) + val activityResult = ActivityResult(Activity.RESULT_OK, intent) val func = activity.javaClass.getDeclaredMethod( - "onActivityResult", - Int::class.java, - Int::class.java, - Intent::class.java, + "onFullScreenDataReceived", + ActivityResult::class.java, ) func.isAccessible = true - func.invoke(activity, 512, -1, Mockito.mock(Intent::class.java)) + func.invoke(activity, activityResult) } /** diff --git a/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt index 171e04b4e..b7ef7878f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt @@ -6,18 +6,19 @@ import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.net.Uri +import android.provider.MediaStore +import androidx.activity.result.ActivityResultLauncher import androidx.preference.PreferenceManager import androidx.test.core.app.ApplicationProvider +import com.nhaarman.mockitokotlin2.KArgumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.verify import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.filepicker.Constants.RequestCodes +import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers -import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.`when` @@ -48,8 +49,10 @@ class FilePickerTest { @Mock var unit: Unit? = null - @Captor - var requestCodeCaptor: ArgumentCaptor? = null + @Mock + private lateinit var mockResultLauncher: ActivityResultLauncher + + private val intentCaptor: KArgumentCaptor = argumentCaptor() private lateinit var context: Context @@ -64,12 +67,19 @@ class FilePickerTest { `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) `when`(sharedPref.edit()).thenReturn(sharedPreferencesEditor) `when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor) - FilePicker.openGallery(activity, 0, nextBoolean()) - verify(activity).startActivityForResult( - ArgumentMatchers.any(), - requestCodeCaptor?.capture()?.toInt()!!, - ) - assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_GALLERY) + val openDocumentPreferred = nextBoolean() + + FilePicker.openGallery(activity, mockResultLauncher, 0, openDocumentPreferred) + + verify(mockResultLauncher).launch(intentCaptor.capture()) + + val capturedIntent = intentCaptor.firstValue + + if (openDocumentPreferred) { + assertEquals(Intent.ACTION_OPEN_DOCUMENT, capturedIntent.action) + } else { + assertEquals(Intent.ACTION_GET_CONTENT, capturedIntent.action) + } } @Test @@ -79,12 +89,13 @@ class FilePickerTest { `when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor) val mockApplication = mock(Application::class.java) `when`(activity.applicationContext).thenReturn(mockApplication) - FilePicker.openCameraForImage(activity, 0) - verify(activity).startActivityForResult( - ArgumentMatchers.any(), - requestCodeCaptor?.capture()?.toInt()!!, - ) - assertEquals(requestCodeCaptor?.value, RequestCodes.TAKE_PICTURE) + FilePicker.openCameraForImage(activity, mockResultLauncher, 0) + + verify(mockResultLauncher).launch(intentCaptor.capture()) + + val capturedIntent = intentCaptor.firstValue + + assertEquals(MediaStore.ACTION_IMAGE_CAPTURE, capturedIntent.action) } @Test @@ -165,32 +176,6 @@ class FilePickerTest { method.invoke(mockFilePicker, activity) } - @Test - fun testTakenCameraVideo() { - val mockFilePicker = mock(FilePicker::class.java) - val method: Method = - FilePicker::class.java.getDeclaredMethod( - "takenCameraVideo", - Context::class.java, - ) - method.isAccessible = true - method.invoke(mockFilePicker, context) - } - - @Test - fun testTakenCameraVideoCaseTrue() { - val mockFilePicker = mock(FilePicker::class.java) - `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) - `when`(sharedPref.getString("last_video", null)).thenReturn("") - val method: Method = - FilePicker::class.java.getDeclaredMethod( - "takenCameraVideo", - Context::class.java, - ) - method.isAccessible = true - method.invoke(mockFilePicker, activity) - } - @Test fun testIsPhoto() { val mockFilePicker = mock(FilePicker::class.java) @@ -204,46 +189,20 @@ class FilePickerTest { method.invoke(mockFilePicker, mockIntent) } - @Test - fun testHandleActivityResultCaseOne() { - val mockIntent = mock(Intent::class.java) - FilePicker.handleActivityResult( - RequestCodes.FILE_PICKER_IMAGE_IDENTIFICATOR, - Activity.RESULT_OK, - mockIntent, - activity, - object : DefaultCallback() { - override fun onCanceled( - source: FilePicker.ImageSource, - type: Int, - ) { - super.onCanceled(source, type) - } - - override fun onImagePickerError( - e: Exception, - source: FilePicker.ImageSource, - type: Int, - ) { - } - - override fun onImagesPicked( - imagesFiles: List, - source: FilePicker.ImageSource, - type: Int, - ) { - } - }, - ) - } - @Test fun testOpenCustomSelectorRequestCode() { `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) `when`(sharedPref.edit()).thenReturn(sharedPreferencesEditor) `when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor) - FilePicker.openCustomSelector(activity, 0) - verify(activity).startActivityForResult(ArgumentMatchers.any(), requestCodeCaptor?.capture()?.toInt()!!) - assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) + FilePicker.openCustomSelector(activity, mockResultLauncher, 0) + + verify(mockResultLauncher).launch(intentCaptor.capture()) + + val capturedIntent = intentCaptor.firstValue + + assertEquals( + CustomSelectorActivity.Companion::class.java.declaringClass.name, + capturedIntent.component?.className + ) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt index 051b18d18..575beab98 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt @@ -2,6 +2,9 @@ package fr.free.nrw.commons.location import org.junit.Before import org.junit.Test +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.CoreMatchers.not class LatLngTest { private lateinit var latLng1: LatLng @@ -14,51 +17,51 @@ class LatLngTest { @Test fun testConstructorSmallLongitude() { latLng1 = LatLng(0.0, -181.0, 0.0f) - assert(latLng1.longitude == 179.0) + assertThat(latLng1.longitude, equalTo(179.0)) } @Test fun testConstructorBigLongitude() { latLng1 = LatLng(0.0, 181.0, 0.0f) - assert(latLng1.longitude == -179.0) + assertThat(latLng1.longitude, equalTo(-179.0)) } @Test fun testConstructorSmallLatitude() { latLng1 = LatLng(-91.0, 0.0, 0.0f) - assert(latLng1.latitude == -90.0) + assertThat(latLng1.latitude, equalTo(-90.0)) } @Test fun testConstructorBigLatitude() { latLng1 = LatLng(91.0, 0.0, 0.0f) - assert(latLng1.latitude == 90.0) + assertThat(latLng1.latitude, equalTo(90.0)) } @Test fun testHashCodeDiffersWenLngZero() { latLng1 = LatLng(2.0, 0.0, 0.0f) latLng2 = LatLng(1.0, 0.0, 0.0f) - assert(latLng1.hashCode() != latLng2.hashCode()) + assertThat(latLng1.hashCode(), not(equalTo(latLng2.hashCode()))) } @Test fun testHashCodeDiffersWenLatZero() { latLng1 = LatLng(0.0, 1.0, 0.0f) latLng2 = LatLng(0.0, 2.0, 0.0f) - assert(latLng1.hashCode() != latLng2.hashCode()) + assertThat(latLng1.hashCode(), not(equalTo(latLng2.hashCode()))) } @Test fun testEqualsWorks() { latLng1 = LatLng(1.0, 2.0, 5.0f) latLng2 = LatLng(1.0, 2.0, 0.0f) - assert(latLng1.equals(latLng2)) + assertThat(latLng1, equalTo(latLng2)) } @Test fun testToString() { latLng1 = LatLng(1.0, 2.0, 5.0f) - assert(latLng1.toString().equals("lat/lng: (1.0,2.0)")) + assertThat(latLng1.toString(), equalTo("lat/lng: (1.0,2.0)")) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt index 7f9e3d576..ea1d3402d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt @@ -19,6 +19,7 @@ import android.widget.ProgressBar import android.widget.ScrollView import android.widget.Spinner import android.widget.TextView +import androidx.activity.result.ActivityResult import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import androidx.test.core.app.ApplicationProvider @@ -76,7 +77,6 @@ import java.util.Locale @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class MediaDetailFragmentUnitTests { - private val requestCode = 1001 private val lastLocation = "last_location_while_uploading" private lateinit var fragment: MediaDetailFragment private lateinit var fragmentManager: FragmentManager @@ -231,24 +231,6 @@ class MediaDetailFragmentUnitTests { fragment.onCreateView(layoutInflater, null, savedInstanceState) } - @Test - @Throws(Exception::class) - fun testOnActivityResultLocationPickerActivity() { - fragment.onActivityResult(requestCode, Activity.RESULT_CANCELED, intent) - } - - @Test - @Throws(Exception::class) - fun `test OnActivity Result Cancelled LocationPickerActivity`() { - fragment.onActivityResult(requestCode, Activity.RESULT_CANCELED, intent) - } - - @Test - @Throws(Exception::class) - fun `test OnActivity Result Cancelled DescriptionEditActivity`() { - fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent) - } - @Test @Throws(Exception::class) fun testOnSaveInstanceState() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt index 446bad2cb..80735476e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt @@ -3,6 +3,8 @@ package fr.free.nrw.commons.nearby import fr.free.nrw.commons.R import org.junit.Before import org.junit.Test +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.CoreMatchers.equalTo class LabelTest { private lateinit var label: Label @@ -21,7 +23,7 @@ class LabelTest { */ @Test fun testLabelIcon() { - assert(label.icon.equals(R.drawable.round_icon_church)) + assertThat(label.icon, equalTo(R.drawable.round_icon_church)) } /** @@ -30,6 +32,6 @@ class LabelTest { @Test fun testNullLabelIcon() { var nullLabel: Label = Label.fromText("a random text not exist in label texts") - assert(nullLabel.icon.equals(R.drawable.round_icon_unknown)) + assertThat(nullLabel.icon, equalTo(R.drawable.round_icon_unknown)) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt index d15fe06e5..938e595af 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt @@ -167,20 +167,6 @@ class UploadActivityUnitTests { activity.makeUploadRequest() } - @Test - @Throws(Exception::class) - fun testOnActivityResult() { - val method: Method = - UploadActivity::class.java.getDeclaredMethod( - "onActivityResult", - Int::class.java, - Int::class.java, - Intent::class.java, - ) - method.isAccessible = true - method.invoke(activity, CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS, 0, Intent()) - } - @Test @Throws(Exception::class) fun testReceiveSharedItems() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt index db5b20f7e..794b6e64e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt @@ -2,11 +2,13 @@ package fr.free.nrw.commons.upload import android.app.Dialog import android.content.Context +import android.content.Intent import android.view.View import android.widget.AdapterView import android.widget.GridLayout import android.widget.ListView import android.widget.TextView +import androidx.activity.result.ActivityResultLauncher import androidx.test.core.app.ApplicationProvider import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.times @@ -67,13 +69,16 @@ class UploadMediaDetailAdapterUnitTest { @Mock private lateinit var adapterView: AdapterView + @Mock + private lateinit var mockResultLauncher: ActivityResultLauncher + @Before fun setUp() { MockitoAnnotations.openMocks(this) uploadMediaDetails = mutableListOf(uploadMediaDetail, uploadMediaDetail) activity = Robolectric.buildActivity(UploadActivity::class.java).get() fragment = mock(UploadMediaDetailFragment::class.java) - adapter = UploadMediaDetailAdapter(fragment, "", recentLanguagesDao) + adapter = UploadMediaDetailAdapter(fragment, "", recentLanguagesDao, mockResultLauncher) context = ApplicationProvider.getApplicationContext() Whitebox.setInternalState(adapter, "uploadMediaDetails", uploadMediaDetails) Whitebox.setInternalState(adapter, "eventListener", eventListener) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt index ed76b4519..169bcd5c0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt @@ -11,6 +11,7 @@ import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import androidx.activity.result.ActivityResult import androidx.appcompat.widget.AppCompatButton import androidx.appcompat.widget.AppCompatImageButton import androidx.fragment.app.FragmentManager @@ -348,7 +349,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) - fun testOnActivityResultOnMapIconClicked() { + fun testOnCameraPositionCallbackOnMapIconClicked() { shadowOf(Looper.getMainLooper()).idle() Mockito.mock(LocationPicker::class.java) val intent = Mockito.mock(Intent::class.java) @@ -363,13 +364,18 @@ class UploadMediaDetailFragmentUnitTest { `when`(latLng.latitude).thenReturn(0.0) `when`(latLng.longitude).thenReturn(0.0) `when`(uploadItem.gpsCoords).thenReturn(imageCoordinates) - fragment.onActivityResult(1211, Activity.RESULT_OK, intent) + val activityResult = ActivityResult(Activity.RESULT_OK, intent) + + val handleResultMethod = UploadMediaDetailFragment::class.java.getDeclaredMethod("onCameraPosition", ActivityResult::class.java) + handleResultMethod.isAccessible = true + + handleResultMethod.invoke(fragment, activityResult) Mockito.verify(presenter, Mockito.times(0)).getImageQuality(0, location, activity) } @Test @Throws(Exception::class) - fun testOnActivityResultAddLocationDialog() { + fun testOnCameraPositionCallbackAddLocationDialog() { shadowOf(Looper.getMainLooper()).idle() Mockito.mock(LocationPicker::class.java) val intent = Mockito.mock(Intent::class.java) @@ -387,7 +393,13 @@ class UploadMediaDetailFragmentUnitTest { `when`(latLng.latitude).thenReturn(0.0) `when`(latLng.longitude).thenReturn(0.0) `when`(uploadItem.gpsCoords).thenReturn(imageCoordinates) - fragment.onActivityResult(1211, Activity.RESULT_OK, intent) + + val activityResult = ActivityResult(Activity.RESULT_OK,intent) + + val handleResultMethod = UploadMediaDetailFragment::class.java.getDeclaredMethod("onCameraPosition", ActivityResult::class.java) + handleResultMethod.isAccessible = true + + handleResultMethod.invoke(fragment, activityResult) Mockito.verify(presenter, Mockito.times(1)).displayLocDialog(0, null, false) }