diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index dcbba0597..a4682fd3c 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -70,7 +70,7 @@ body: required: false - type: textarea attributes: - label: Screenshots + label: Screen-shots description: Add screenshots related to the issue (if available). Can be created by pressing the Volume Down and Power Button at the same time on Android 4.0 and higher. validations: required: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 575aa6a32..fc22a2b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,5 @@ # Wikimedia Commons for Android -## v6.0.2 - -### What's changed -* Addressed a bug that prevented the keyboard from appearing in various text fields, such as on the upload wizard -* Links in the "File usages" list are now clickable and will take you to the correct page. -* Titles for file usages are now clearer and easier to understand -* Bug fixes and stability improvements - -## v6.0.1 - -### What's changed -* The app now supports Android 15 with an improved user interface -* Enhanced Nearby with robust and more reliable labels -* Bug fixes and stability improvements - ## v5.6.1 ### What's changed diff --git a/README.md b/README.md index 37f1a7872..0b31ff5be 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,11 @@ Thank you all for your work! | [
misaochan](https://github.com/misaochan) | [
translatewiki](https://github.com/translatewiki) | [
neslihanturan](https://github.com/neslihanturan) | [
yuvipanda](https://github.com/yuvipanda) | [
nicolas-raoul](https://github.com/nicolas-raoul) | | :---: | :---: | :---: | :---: | :---: | -| [
psh](https://github.com/psh) | [
domdomegg](https://github.com/domdomegg) | [
maskaravivek](https://github.com/maskaravivek) | [
madhurgupta10](https://github.com/madhurgupta10) | [
ashishkumar468](https://github.com/ashishkumar468) | -| [
bvibber](https://github.com/bvibber) | [
whym](https://github.com/whym) | [
akaita](https://github.com/akaita) | [
sivaraam](https://github.com/sivaraam) | [
veyndan](https://github.com/veyndan) | -| [
ujjwalagrawal17](https://github.com/ujjwalagrawal17) | [
macgills](https://github.com/macgills) | [
amire80](https://github.com/amire80) | [
dbrant](https://github.com/dbrant) | [
vanshikaarora](https://github.com/vanshikaarora) | -| [
RitikaPahwa4444](https://github.com/RitikaPahwa4444) | [
Ayan-10](https://github.com/Ayan-10) | [
rohit9625](https://github.com/rohit9625) | [
shashankiitbhu](https://github.com/shashankiitbhu) | [
Pratham2305](https://github.com/Pratham2305) | -| [
parneet-guraya](https://github.com/parneet-guraya) | [
sandarumk](https://github.com/sandarumk) | [
tanvidadu](https://github.com/tanvidadu) | [
cypherop](https://github.com/cypherop) | [
Prince-kushwaha](https://github.com/Prince-kushwaha) | - +| [
domdomegg](https://github.com/domdomegg) | [
maskaravivek](https://github.com/maskaravivek) | [
psh](https://github.com/psh) | [
madhurgupta10](https://github.com/madhurgupta10) | [
ashishkumar468](https://github.com/ashishkumar468) | +| [
bvibber](https://github.com/bvibber) | [
whym](https://github.com/whym) | [
akaita](https://github.com/akaita) | [
veyndan](https://github.com/veyndan) | [
ujjwalagrawal17](https://github.com/ujjwalagrawal17) | +| [
macgills](https://github.com/macgills) | [
dbrant](https://github.com/dbrant) | [
vanshikaarora](https://github.com/vanshikaarora) | [
sivaraam](https://github.com/sivaraam) | [
Ayan-10](https://github.com/Ayan-10) | +| [
shashankiitbhu](https://github.com/shashankiitbhu) | [
Pratham2305](https://github.com/Pratham2305) | [
sandarumk](https://github.com/sandarumk) | [
tanvidadu](https://github.com/tanvidadu) | [
cypherop](https://github.com/cypherop) | +| [
Prince-kushwaha](https://github.com/Prince-kushwaha) | [
tobias47n9e](https://github.com/tobias47n9e) | [
4D17Y4](https://github.com/4D17Y4) | [
hismaeel](https://github.com/hismaeel) | [
tshradheya](https://github.com/tshradheya) | .. and [many more](https://github.com/commons-app/apps-android-commons/graphs/contributors). diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 41788128c..ddbbb4984 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,8 +24,8 @@ android { applicationId = "fr.free.nrw.commons" minSdk = 21 targetSdk = 35 - versionCode = 1059 - versionName = "6.1.0" + versionCode = 1057 + versionName = "6.0.1" setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName()) testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -226,7 +226,6 @@ dependencies { implementation(libs.rxbinding) implementation(libs.rxbinding.appcompat) implementation(libs.facebook.fresco) - implementation(libs.facebook.fresco.middleware) implementation(libs.apache.commons.lang3) // UI diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17917666d..d56a874b5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -57,7 +57,8 @@ tools:replace="android:appComponentFactory"> + android:exported="false" + android:label="@string/title_activity_single_web_view" /> @@ -84,7 +85,6 @@ android:parentActivityName=".customselector.ui.selector.CustomSelectorActivity" /> @@ -103,7 +103,7 @@ android:exported="true" android:hardwareAccelerated="false" android:icon="@mipmap/ic_launcher" - android:windowSoftInputMode="adjustPan"> + android:windowSoftInputMode="adjustResize"> diff --git a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt index c54c3aefb..1c28d5fe4 100644 --- a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt +++ b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt @@ -1,11 +1,7 @@ package fr.free.nrw.commons import androidx.annotation.VisibleForTesting -import fr.free.nrw.commons.wikidata.GsonUtil import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar -import fr.free.nrw.commons.wikidata.mwapi.MwErrorResponse -import fr.free.nrw.commons.wikidata.mwapi.MwIOException -import fr.free.nrw.commons.wikidata.mwapi.MwLegacyServiceError import okhttp3.Cache import okhttp3.Interceptor import okhttp3.OkHttpClient @@ -90,25 +86,16 @@ private class UnsuccessfulResponseInterceptor : Interceptor { rsp.peekBody(ERRORS_PREFIX.length.toLong()).use { responseBody -> if (ERRORS_PREFIX == responseBody.string()) { rsp.body.use { body -> - val bodyString = body!!.string() - - throw MwIOException( - "MediaWiki API returned error: $bodyString", - GsonUtil.defaultGson.fromJson( - bodyString, - MwErrorResponse::class.java - ).error!!, - ) + throw IOException(body!!.string()) } } } - } catch (e: MwIOException) { + } catch (e: IOException) { // Log the error as debug (and therefore, "expected") or at error level if (suppressErrors) { Timber.d(e, "Suppressed (known / expected) error") } else { Timber.e(e) - throw e } } return rsp diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt index 0c9901b56..688f508ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt @@ -89,7 +89,7 @@ class LoginActivity : AccountAuthenticatorActivity() { binding = ActivityLoginBinding.inflate(layoutInflater) applyEdgeToEdgeAllInsets(binding!!.root) - binding!!.root.handleKeyboardInsets() + binding?.aboutPrivacyPolicy?.handleKeyboardInsets() with(binding!!) { setContentView(root) diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.kt index e21e1ac8f..d64ab16b3 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.kt @@ -144,18 +144,8 @@ class BookmarkItemsDao @Inject constructor( */ @SuppressLint("Range") fun fromCursor(cursor: Cursor) = with(cursor) { - var name = getString(COLUMN_NAME) - if (name == null) { - name = "" - } - - var id = getString(COLUMN_ID) - if (id == null) { - id = "" - } - DepictedItem( - name, + getString(COLUMN_NAME), getString(COLUMN_DESCRIPTION), getString(COLUMN_IMAGE), getStringArray(COLUMN_INSTANCE_LIST), @@ -165,7 +155,7 @@ class BookmarkItemsDao @Inject constructor( getStringArray(COLUMN_CATEGORIES_THUMBNAIL_LIST) ), getString(COLUMN_IS_SELECTED).toBoolean(), - id + getString(COLUMN_ID) ) } @@ -173,13 +163,19 @@ class BookmarkItemsDao @Inject constructor( categoryNameList: List, categoryDescriptionList: List, categoryThumbnailList: List - ): List = categoryNameList.mapIndexed { index, name -> - CategoryItem( - name = name, - description = categoryDescriptionList.getOrNull(index), - thumbnail = categoryThumbnailList.getOrNull(index), - isSelected = false - ) + ): List { + return buildList { + for (i in categoryNameList.indices) { + add( + CategoryItem( + categoryNameList[i], + categoryDescriptionList[i], + categoryThumbnailList[i], + false + ) + ) + } + } } /** diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesDao.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesDao.kt index 00c8e3228..e30b3160d 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesDao.kt @@ -128,10 +128,7 @@ class BookmarkPicturesDao @Inject constructor( } fun fromCursor(cursor: Cursor): Bookmark { - var fileName = cursor.getString(COLUMN_MEDIA_NAME) - if (fileName == null) { - fileName = "" - } + val fileName = cursor.getString(COLUMN_MEDIA_NAME) return Bookmark( fileName, cursor.getString(COLUMN_CREATOR), uriForName(fileName) ) diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt index 9f94e8592..6bf0bc0ed 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt @@ -7,8 +7,8 @@ import com.google.gson.annotations.SerializedName */ class CampaignConfig { @SerializedName("showOnlyLiveCampaigns") - var showOnlyLiveCampaigns = false + private val showOnlyLiveCampaigns = false @SerializedName("sortBy") - var sortBy: String? = null -} \ No newline at end of file + private val sortBy: String? = null +} diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt index 1656109e7..767732eb7 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt @@ -8,8 +8,8 @@ import fr.free.nrw.commons.campaigns.models.Campaign */ class CampaignResponseDTO { @SerializedName("config") - var campaignConfig: CampaignConfig? = null + val campaignConfig: CampaignConfig? = null @SerializedName("campaigns") - var campaigns: List? = null -} \ No newline at end of file + val campaigns: List? = null +} diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt index b9532a12e..29267452b 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt @@ -180,8 +180,8 @@ class ContributionController @Inject constructor(@param:Named("default_preferenc showAlertDialog( activity, activity.getString(R.string.location_permission_title), activity.getString(R.string.in_app_camera_location_permission_rationale), - activity.getString(R.string.ok), - activity.getString(R.string.cancel), + activity.getString(android.R.string.ok), + activity.getString(android.R.string.cancel), { createDialogsAndHandleLocationPermissions( activity, diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt index 6d0822604..b86cd6dc9 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt @@ -5,6 +5,7 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.res.Configuration +import android.net.Uri import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -19,8 +20,6 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.annotation.VisibleForTesting -import androidx.core.net.toUri -import androidx.core.os.BundleCompat import androidx.paging.PagedList import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -39,10 +38,12 @@ import fr.free.nrw.commons.filepicker.FilePicker import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.showShortToast import fr.free.nrw.commons.utils.copyToClipboard import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.model.WikiSite +import org.apache.commons.lang3.StringUtils import javax.inject.Inject import javax.inject.Named @@ -52,6 +53,10 @@ import javax.inject.Named */ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsListContract.View, ContributionsListAdapter.Callback, WikipediaInstructionsDialogFragment.Callback { + @JvmField + @Inject + var systemThemeUtils: SystemThemeUtils? = null + @JvmField @Inject var controller: ContributionController? = null @@ -78,14 +83,13 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL var sessionManager: SessionManager? = null private var binding: FragmentContributionsListBinding? = null - private var fabClose: Animation? = null - private var fabOpen: Animation? = null - private var rotateForward: Animation? = null - private var rotateBackward: Animation? = null + private var fab_close: Animation? = null + private var fab_open: Animation? = null + private var rotate_forward: Animation? = null + private var rotate_backward: Animation? = null private var isFabOpen = false - private lateinit var inAppCameraLocationPermissionLauncher: - ActivityResultLauncher> + private lateinit var inAppCameraLocationPermissionLauncher: ActivityResultLauncher> @VisibleForTesting var rvContributionsList: RecyclerView? = null @@ -96,8 +100,8 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL @VisibleForTesting var callback: Callback? = null - private val spanCountLandscape = 3 - private val spanCountPortrait = 1 + private val SPAN_COUNT_LANDSCAPE = 3 + private val SPAN_COUNT_PORTRAIT = 1 private var contributionsSize = 0 private var userName: String? = null @@ -146,7 +150,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL userName = requireArguments().getString(ProfileActivity.KEY_USERNAME) } - if (userName.isNullOrEmpty()) { + if (StringUtils.isEmpty(userName)) { userName = sessionManager!!.userName } inAppCameraLocationPermissionLauncher = @@ -157,8 +161,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL controller?.locationPermissionCallback?.onLocationPermissionGranted() } else { activity?.let { currentActivity -> - if (currentActivity.shouldShowRequestPermissionRationale( - permission.ACCESS_FINE_LOCATION)) { + if (currentActivity.shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { controller?.handleShowRationaleFlowCameraLocation( currentActivity, inAppCameraLocationPermissionLauncher, // Pass launcher @@ -166,8 +169,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL ) } else { controller?.locationPermissionCallback?.onLocationPermissionDenied( - currentActivity.getString( - R.string.in_app_camera_location_permission_denied) + currentActivity.getString(R.string.in_app_camera_location_permission_denied) ) } } @@ -187,7 +189,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL contributionsListPresenter!!.onAttachView(this) binding!!.fabCustomGallery.setOnClickListener { v: View? -> launchCustomSelector() } binding!!.fabCustomGallery.setOnLongClickListener { view: View? -> - showShortToast(context, R.string.custom_selector_title) + showShortToast(context, fr.free.nrw.commons.R.string.custom_selector_title) true } @@ -197,7 +199,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL } else { binding!!.tvContributionsOfUser.visibility = View.VISIBLE binding!!.tvContributionsOfUser.text = - getString(R.string.contributions_of_user, userName) + getString(fr.free.nrw.commons.R.string.contributions_of_user, userName) binding!!.fabLayout.visibility = View.GONE } @@ -235,10 +237,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL } private fun initAdapter() { - adapter = ContributionsListAdapter(this, - mediaClient!!, - mediaDataExtractor!!, - compositeDisposable) + adapter = ContributionsListAdapter(this, mediaClient!!, mediaDataExtractor!!, compositeDisposable) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -313,7 +312,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { if (e.action == MotionEvent.ACTION_DOWN) { if (isFabOpen) { - animateFAB(true) + animateFAB(isFabOpen) } } return false @@ -345,20 +344,14 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL } private fun getSpanCount(orientation: Int): Int { - return if (orientation == Configuration.ORIENTATION_LANDSCAPE) - spanCountLandscape - else - spanCountPortrait + return if (orientation == Configuration.ORIENTATION_LANDSCAPE) SPAN_COUNT_LANDSCAPE else SPAN_COUNT_PORTRAIT } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) // check orientation binding!!.fabLayout.orientation = - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) - LinearLayout.HORIZONTAL - else - LinearLayout.VERTICAL + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL rvContributionsList ?.setLayoutManager( GridLayoutManager(context, getSpanCount(newConfig.orientation)) @@ -366,10 +359,10 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL } private fun initializeAnimations() { - fabOpen = AnimationUtils.loadAnimation(activity, R.anim.fab_open) - fabClose = AnimationUtils.loadAnimation(activity, R.anim.fab_close) - rotateForward = AnimationUtils.loadAnimation(activity, R.anim.rotate_forward) - rotateBackward = AnimationUtils.loadAnimation(activity, R.anim.rotate_backward) + fab_open = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.fab_open) + fab_close = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.fab_close) + rotate_forward = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.rotate_forward) + rotate_backward = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.rotate_backward) } private fun setListeners() { @@ -385,7 +378,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL binding!!.fabCamera.setOnLongClickListener { view: View? -> showShortToast( context, - R.string.add_contribution_from_camera + fr.free.nrw.commons.R.string.add_contribution_from_camera ) true } @@ -394,7 +387,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL animateFAB(isFabOpen) } binding!!.fabGallery.setOnLongClickListener { view: View? -> - showShortToast(context, R.string.menu_from_gallery) + showShortToast(context, fr.free.nrw.commons.R.string.menu_from_gallery) true } } @@ -402,7 +395,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL /** * Launch Custom Selector. */ - private fun launchCustomSelector() { + protected fun launchCustomSelector() { controller!!.initiateCustomGalleryPickWithPermission( requireActivity(), customSelectorLauncherForResult @@ -418,18 +411,18 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL this.isFabOpen = !isFabOpen if (binding!!.fabPlus.isShown) { if (isFabOpen) { - binding!!.fabPlus.startAnimation(rotateBackward) - binding!!.fabCamera.startAnimation(fabClose) - binding!!.fabGallery.startAnimation(fabClose) - binding!!.fabCustomGallery.startAnimation(fabClose) + binding!!.fabPlus.startAnimation(rotate_backward) + binding!!.fabCamera.startAnimation(fab_close) + binding!!.fabGallery.startAnimation(fab_close) + binding!!.fabCustomGallery.startAnimation(fab_close) binding!!.fabCamera.hide() binding!!.fabGallery.hide() binding!!.fabCustomGallery.hide() } else { - binding!!.fabPlus.startAnimation(rotateForward) - binding!!.fabCamera.startAnimation(fabOpen) - binding!!.fabGallery.startAnimation(fabOpen) - binding!!.fabCustomGallery.startAnimation(fabOpen) + binding!!.fabPlus.startAnimation(rotate_forward) + binding!!.fabCamera.startAnimation(fab_open) + binding!!.fabGallery.startAnimation(fab_open) + binding!!.fabCustomGallery.startAnimation(fab_open) binding!!.fabCamera.show() binding!!.fabGallery.show() binding!!.fabCustomGallery.show() @@ -441,9 +434,9 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL /** * Shows welcome message if user has no contributions yet i.e. new user. */ - override fun showWelcomeTip(numberOfUploads: Boolean) { + override fun showWelcomeTip(shouldShow: Boolean) { binding!!.noContributionsYet.visibility = - if (numberOfUploads) View.VISIBLE else View.GONE + if (shouldShow) View.VISIBLE else View.GONE } /** @@ -463,22 +456,22 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - val layoutManager = rvContributionsList?.layoutManager as GridLayoutManager? + val layoutManager = rvContributionsList + ?.getLayoutManager() as GridLayoutManager? outState.putParcelable(RV_STATE, layoutManager!!.onSaveInstanceState()) } override fun onViewStateRestored(savedInstanceState: Bundle?) { super.onViewStateRestored(savedInstanceState) if (null != savedInstanceState) { - val savedRecyclerLayoutState = - BundleCompat.getParcelable(savedInstanceState, RV_STATE, Parcelable::class.java) + val savedRecyclerLayoutState = savedInstanceState.getParcelable(RV_STATE) rvContributionsList!!.layoutManager!!.onRestoreInstanceState(savedRecyclerLayoutState) } } - override fun openMediaDetail(contribution: Int, isWikipediaPageExists: Boolean) { + override fun openMediaDetail(position: Int, isWikipediaButtonDisplayed: Boolean) { if (null != callback) { //Just being safe, ideally they won't be called when detached - callback!!.showDetail(contribution, isWikipediaPageExists) + callback!!.showDetail(position, isWikipediaButtonDisplayed) } } @@ -490,8 +483,8 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL override fun addImageToWikipedia(contribution: Contribution?) { showAlertDialog( requireActivity(), - getString(R.string.add_picture_to_wikipedia_article_title), - getString(R.string.add_picture_to_wikipedia_article_desc), + getString(fr.free.nrw.commons.R.string.add_picture_to_wikipedia_article_title), + getString(fr.free.nrw.commons.R.string.add_picture_to_wikipedia_article_desc), { if (contribution != null) { showAddImageToWikipediaInstructions(contribution) @@ -505,18 +498,16 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL * @param contribution */ private fun showAddImageToWikipediaInstructions(contribution: Contribution) { - val fragmentManager = this.parentFragmentManager + val fragmentManager = fragmentManager val fragment = newInstance(contribution) fragment.callback = - WikipediaInstructionsDialogFragment.Callback { - contribution: Contribution?, - copyWikicode: Boolean -> - onConfirmClicked( + WikipediaInstructionsDialogFragment.Callback { contribution: Contribution?, copyWikicode: Boolean -> + this.onConfirmClicked( contribution, copyWikicode ) } - fragment.show(fragmentManager, "WikimediaFragment") + fragment.show(fragmentManager!!, "WikimediaFragment") } @@ -543,7 +534,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL val url = languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace ?.getWikipediaPageTitle()) - handleWebUrl(requireContext(), url.toUri()) + handleWebUrl(requireContext(), Uri.parse(url)) } fun getContributionStateAt(position: Int): Int { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt index d481017b2..5c2c44ab5 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt @@ -153,7 +153,21 @@ after opening the app. } } setUpPager() - + /** + * Ask the user for media location access just after login + * so that location in the EXIF metadata of the images shared by the user + * is retained on devices running Android 10 or above + */ +// if (VERSION.SDK_INT >= VERSION_CODES.Q) { +// ActivityCompat.requestPermissions(this, +// new String[]{Manifest.permission.ACCESS_MEDIA_LOCATION}, 0); +// PermissionUtils.checkPermissionsAndPerformAction( +// this, +// () -> {}, +// R.string.media_location_permission_denied, +// R.string.add_location_manually, +// permission.ACCESS_MEDIA_LOCATION); +// } checkAndResumeStuckUploads() } } @@ -324,7 +338,7 @@ after opening the app. ) .subscribeOn(Schedulers.io()) .blockingGet() - Timber.d("Resuming %d uploads...", stuckUploads.size) + Timber.d("Resuming " + stuckUploads.size + " uploads...") if (!stuckUploads.isEmpty()) { for (contribution in stuckUploads) { contribution.state = Contribution.STATE_QUEUED diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt index 8e899fcba..06c31fede 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt @@ -45,10 +45,10 @@ class SetWallpaperWorker(context: Context, params: WorkerParameters) : } } - override fun onFailureImpl(dataSource: DataSource?>) { + override fun onFailureImpl(dataSource: DataSource>?) { Timber.d("Error getting bitmap from image url %s", imageUrl.toString()) showNotification(context, "Setting Wallpaper Failed", "Failed to download image.") - dataSource.close() + dataSource?.close() } }, CallerThreadExecutor.getInstance()) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt b/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt index 4bf295f4c..ec08f6f73 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt @@ -39,11 +39,4 @@ data class Folder( return true } - - override fun hashCode(): Int { - var result = bucketId.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + images.hashCode() - return result - } } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt b/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt index a172f28e2..a2965fb5d 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt @@ -1,7 +1,6 @@ package fr.free.nrw.commons.customselector.model import android.net.Uri -import android.os.Build import android.os.Parcel import android.os.Parcelable @@ -49,12 +48,7 @@ data class Image( this( parcel.readLong(), parcel.readString()!!, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - parcel.readParcelable(Uri::class.java.classLoader, Uri::class.java)!! - } else { - @Suppress("DEPRECATION") - parcel.readParcelable(Uri::class.java.classLoader)!! - }, + parcel.readParcelable(Uri::class.java.classLoader)!!, parcel.readString()!!, parcel.readLong(), parcel.readString()!!, @@ -127,16 +121,4 @@ data class Image( override fun newArray(size: Int): Array = arrayOfNulls(size) } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + bucketId.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + uri.hashCode() - result = 31 * result + path.hashCode() - result = 31 * result + bucketName.hashCode() - result = 31 * result + sha1.hashCode() - result = 31 * result + date.hashCode() - return result - } } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index c3ef4a784..62a440ff4 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -168,7 +168,8 @@ class ImageAdapter( // Getting selected index when switch is off } else if (actionableImagesMap.size > position) { - ImageHelper.getIndex(selectedImages, ArrayList(actionableImagesMap.values)[position]) + ImageHelper + .getIndex(selectedImages, ArrayList(actionableImagesMap.values)[position]) // For any other case return -1 } else { @@ -347,14 +348,8 @@ class ImageAdapter( numberOfSelectedImagesMarkedAsNotForUpload-- } notifyItemChanged(position, ImageUnselected()) - // Notify listener of deselection to update UI - imageSelectListener.onSelectedImagesChanged(selectedImages, numberOfSelectedImagesMarkedAsNotForUpload) } else { - // Prevent adding the same image multiple times - val image = if (showAlreadyActionedImages) images[position] else ArrayList(actionableImagesMap.values)[position] - if (selectedImages.contains(image)) { - return // Image already selected, ignore additional clicks - } + val image = images[position] scope.launch(ioDispatcher) { val imageSHA1 = imageLoader.getSHA1(image, defaultDispatcher) withContext(Dispatchers.Main) { @@ -378,6 +373,7 @@ class ImageAdapter( } selectedImages.add(image) notifyItemChanged(position, ImageSelectedOrUpdated()) + imageSelectListener.onSelectedImagesChanged(selectedImages, numberOfSelectedImagesMarkedAsNotForUpload) } } @@ -636,4 +632,4 @@ class ImageAdapter( fun setSingleSelection(single: Boolean) { singleSelection = single } -} \ No newline at end of file +} 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 a5182fe62..4f37106cc 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 @@ -9,6 +9,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ProgressBar +import android.widget.Switch import androidx.appcompat.app.AlertDialog import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible @@ -19,7 +20,6 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.switchmaterial.SwitchMaterial import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao @@ -47,7 +47,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch -import timber.log.Timber import java.util.TreeMap import javax.inject.Inject import kotlin.collections.ArrayList @@ -82,7 +81,7 @@ class ImageFragment : */ private var selectorRV: RecyclerView? = null private var loader: ProgressBar? = null - private var switch: SwitchMaterial? = null + private var switch: Switch? = null lateinit var filteredImages: ArrayList /** @@ -212,12 +211,8 @@ class ImageFragment : savedInstanceState: Bundle?, ): View? { _binding = FragmentCustomSelectorBinding.inflate(inflater, container, false) - - // ensures imageAdapter is initialized - if (!::imageAdapter.isInitialized) { - imageAdapter = ImageAdapter(requireActivity(), activity as ImageSelectListener, imageLoader!!) - Timber.d("Initialized imageAdapter in onCreateView") - } + imageAdapter = + ImageAdapter(requireActivity(), activity as ImageSelectListener, imageLoader!!) // Set single selection mode if needed val singleSelection = (activity as? CustomSelectorActivity)?.intent?.getBooleanExtra(CustomSelectorActivity.EXTRA_SINGLE_SELECTION, false) == true imageAdapter.setSingleSelection(singleSelection) @@ -375,12 +370,7 @@ class ImageFragment : * notifyDataSetChanged, rebuild the holder views to account for deleted images. */ override fun onResume() { - if (::imageAdapter.isInitialized) { - imageAdapter.notifyDataSetChanged() - Timber.d("Notified imageAdapter in onResume") - } else { - Timber.w("imageAdapter not initialized in onResume") - } + imageAdapter.notifyDataSetChanged() super.onResume() } @@ -390,19 +380,14 @@ class ImageFragment : * Save the Image Fragment state. */ override fun onDestroy() { - if (::imageAdapter.isInitialized) { - imageAdapter.cleanUp() - Timber.d("Cleaned up imageAdapter in onDestroy") - } else { - Timber.w("imageAdapter not initialized in onDestroy, skipping cleanup") - } + imageAdapter.cleanUp() val position = - (selectorRV?.layoutManager as? GridLayoutManager) - ?.findFirstVisibleItemPosition() ?: -1 + (selectorRV?.layoutManager as GridLayoutManager) + .findFirstVisibleItemPosition() - // check for valid position and non-empty image list - if (position != -1 && filteredImages.isNotEmpty() && ::imageAdapter.isInitialized) { + // Check for empty RecyclerView. + if (position != -1 && filteredImages.size > 0) { context?.let { context -> context .getSharedPreferences( @@ -411,57 +396,34 @@ class ImageFragment : )?.let { prefs -> prefs.edit()?.let { editor -> editor.putLong("ItemId", imageAdapter.getImageIdAt(position))?.apply() - Timber.d("Saved last visible item ID: %d", imageAdapter.getImageIdAt(position)) } } } - } else { - Timber.d("Skipped saving item ID: position=%d, filteredImages.size=%d, imageAdapter initialized=%b", - position, filteredImages.size, ::imageAdapter.isInitialized) } super.onDestroy() } override fun onDestroyView() { _binding = null - selectorRV = null - loader = null - switch = null - progressLayout = null super.onDestroyView() } override fun refresh() { - if (::imageAdapter.isInitialized) { - imageAdapter.refresh(filteredImages, allImages, getUploadingContributions()) - Timber.d("Refreshed imageAdapter") - } else { - Timber.w("imageAdapter not initialized in refresh") - } + imageAdapter.refresh(filteredImages, allImages, getUploadingContributions()) } /** * Removes the image from the actionable image map */ fun removeImage(image: Image) { - if (::imageAdapter.isInitialized) { - imageAdapter.removeImageFromActionableImageMap(image) - Timber.d("Removed image from actionable image map") - } else { - Timber.w("imageAdapter not initialized in removeImage") - } + imageAdapter.removeImageFromActionableImageMap(image) } /** * Clears the selected images */ fun clearSelectedImages() { - if (::imageAdapter.isInitialized) { - imageAdapter.clearSelectedImages() - Timber.d("Cleared selected images") - } else { - Timber.w("imageAdapter not initialized in clearSelectedImages") - } + imageAdapter.clearSelectedImages() } /** @@ -472,15 +434,6 @@ class ImageFragment : selectedImages: ArrayList, shouldRefresh: Boolean, ) { - if (::imageAdapter.isInitialized) { - imageAdapter.setSelectedImages(selectedImages) - if (shouldRefresh) { - imageAdapter.refresh(filteredImages, allImages, getUploadingContributions()) - } - Timber.d("Passed %d selected images to imageAdapter, shouldRefresh=%b", selectedImages.size, shouldRefresh) - } else { - Timber.w("imageAdapter not initialized in passSelectedImages") - } } /** @@ -490,7 +443,6 @@ class ImageFragment : if (!progressDialog.isShowing) { progressDialogLayout.progressDialogText.text = text progressDialog.show() - Timber.d("Showing mark/unmark progress dialog: %s", text) } } @@ -500,7 +452,6 @@ class ImageFragment : fun dismissMarkUnmarkProgressDialog() { if (progressDialog.isShowing) { progressDialog.dismiss() - Timber.d("Dismissed mark/unmark progress dialog") } } @@ -510,4 +461,4 @@ class ImageFragment : listOf(Contribution.STATE_IN_PROGRESS, Contribution.STATE_FAILED, Contribution.STATE_QUEUED, Contribution.STATE_PAUSED), )?.subscribeOn(Schedulers.io()) ?.blockingGet() ?: emptyList() -} \ No newline at end of file +} 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 b1f1b7f9b..89d43845b 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 @@ -150,7 +150,7 @@ class DescriptionEditActivity : this, getString(titleStringID), getString(messageStringId), - getString(R.string.ok), + getString(android.R.string.ok), null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.kt index bc8f9cfaa..ea96b50a3 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.kt @@ -64,9 +64,6 @@ class ExploreFragment : CommonsDaggerSupportFragment() { override fun onPageScrollStateChanged(state: Int) = Unit override fun onPageSelected(position: Int) { binding!!.viewPager.canScroll = position != 2 - if (position == 2) { - mapRootFragment?.requestLocationIfNeeded() - } } }) setTabs() @@ -174,12 +171,14 @@ class ExploreFragment : CommonsDaggerSupportFragment() { // if on Map tab, show all menu options, else only show search binding!!.viewPager.addOnPageChangeListener(object : OnPageChangeListener { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) = Unit - override fun onPageScrollStateChanged(state: Int) = Unit + override fun onPageSelected(position: Int) { - binding!!.viewPager.canScroll = position != 2 - others.setVisible(position == 2) - if (position == 2) { - mapRootFragment?.requestLocationIfNeeded() + others.setVisible((position == 2)) + } + + override fun onPageScrollStateChanged(state: Int) { + if (state == ViewPager.SCROLL_STATE_IDLE && binding!!.viewPager.currentItem == 2) { + onPageSelected(2) } } }) @@ -195,6 +194,7 @@ class ExploreFragment : CommonsDaggerSupportFragment() { */ override fun onOptionsItemSelected(item: MenuItem): Boolean { // Handle item selection + when (item.itemId) { R.id.action_search -> { startActivityWithFlags(requireActivity(), SearchActivity::class.java) @@ -224,4 +224,6 @@ class ExploreFragment : CommonsDaggerSupportFragment() { retainInstance = true } } -} \ No newline at end of file +} + + diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.kt index d405709a8..af65834eb 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.kt @@ -193,20 +193,9 @@ class ExploreMapRootFragment : CommonsDaggerSupportFragment, MediaDetailProvider binding = null } - fun requestLocationIfNeeded() { - mapFragment?.requestLocationIfNeeded() - } - - override fun setUserVisibleHint(isVisibleToUser: Boolean) { - super.setUserVisibleHint(isVisibleToUser) - if (isVisibleToUser) { - requestLocationIfNeeded() - } - } - companion object { fun newInstance(): ExploreMapRootFragment = ExploreMapRootFragment().apply { retainInstance = true } } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt index d025fdfe1..32af67e95 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt @@ -112,8 +112,8 @@ class WikidataItemDetailsActivity : BaseActivity(), MediaDetailProvider, Categor viewPagerAdapter!!.setTabs( R.string.title_for_media to depictionImagesListFragment!!, - R.string.title_for_child_classes to childDepictionsFragment, - R.string.title_for_parent_classes to parentDepictionsFragment + R.string.title_for_subcategories to childDepictionsFragment, + R.string.title_for_parent_categories to parentDepictionsFragment ) binding!!.viewPager.offscreenPageLimit = 2 viewPagerAdapter!!.notifyDataSetChanged() diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt index a1bae09fb..e64f12db3 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt @@ -140,8 +140,8 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi requireActivity(), requireActivity().getString(R.string.location_permission_title), requireActivity().getString(R.string.location_permission_rationale_explore), - requireActivity().getString(R.string.ok), - requireActivity().getString(R.string.cancel), + requireActivity().getString(android.R.string.ok), + requireActivity().getString(android.R.string.cancel), { askForLocationPermission() }, null, null @@ -269,60 +269,31 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi override fun onZoom(event: ZoomEvent?): Boolean = false }) - // removed tha permission check here to prevent it from running on fragment creation + if (!locationPermissionsHelper!!.checkLocationPermission(requireActivity())) { + askForLocationPermission() + } } override fun onResume() { super.onResume() binding!!.mapView.onResume() presenter!!.attachView(this) - locationManager.addLocationListener(this) - if (broadcastReceiver != null) { - requireActivity().registerReceiver(broadcastReceiver, intentFilter) + registerNetworkReceiver() + if (isResumed) { + if (locationPermissionsHelper!!.checkLocationPermission(requireActivity())) { + performMapReadyActions() + } else { + startMapWithoutPermission() + } } - setSearchThisAreaButtonVisibility(false) } override fun onPause() { super.onPause() // unregistering the broadcastReceiver, as it was causing an exception and a potential crash unregisterNetworkReceiver() - locationManager.unregisterLocationManager() - locationManager.removeLocationListener(this) } - fun requestLocationIfNeeded() { - if (!isVisible) return // skips if not visible to user - if (locationPermissionsHelper!!.checkLocationPermission(requireActivity())) { - if (locationPermissionsHelper!!.isLocationAccessToAppsTurnedOn()) { - locationManager.registerLocationManager() - drawMyLocationMarker() - } else { - locationPermissionsHelper!!.showLocationOffDialog(requireActivity(), R.string.location_off_dialog_text) - } - } else { - locationPermissionsHelper!!.requestForLocationAccess( - R.string.location_permission_title, - R.string.location_permission_rationale - ) - } - } - - private fun drawMyLocationMarker() { - val location = locationManager.getLastLocation() - if (location != null) { - val geoPoint = GeoPoint(location.latitude, location.longitude) - val startMarker = Marker(binding!!.mapView).apply { - setPosition(geoPoint) - setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) - icon = ContextCompat.getDrawable(requireContext(), R.drawable.current_location_marker) - title = "Your Location" - textLabelFontSize = 24 - } - binding!!.mapView.overlays.add(startMarker) - binding!!.mapView.invalidate() - } - } /** * Unregisters the networkReceiver @@ -965,17 +936,13 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi if (geoPoint != null) { binding!!.mapView.controller.setCenter(geoPoint) val overlays = binding!!.mapView.overlays - // collects the indices of items to remove - val indicesToRemove = mutableListOf() for (i in overlays.indices) { - if (overlays[i] is Marker || overlays[i] is ScaleDiskOverlay) { - indicesToRemove.add(i) + if (overlays[i] is Marker) { + binding!!.mapView.overlays.removeAt(i) + } else if (overlays[i] is ScaleDiskOverlay) { + binding!!.mapView.overlays.removeAt(i) } } - // removes the items in reverse order to avoid index shifting - indicesToRemove.sortedDescending().forEach { index -> - binding!!.mapView.overlays.removeAt(index) - } val diskOverlay = ScaleDiskOverlay( requireContext(), geoPoint, 2000, GeoConstants.UnitOfMeasure.foot @@ -985,6 +952,7 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi this.style = Paint.Style.STROKE this.strokeWidth = 2f }) + setCirclePaint1(Paint().apply { setColor(Color.argb(40, 128, 128, 128)) this.style = Paint.Style.FILL_AND_STROKE @@ -993,6 +961,7 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi setDisplaySizeMax(1700) } binding!!.mapView.overlays.add(diskOverlay) + val startMarker = Marker( binding!!.mapView ).apply { @@ -1110,24 +1079,7 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi override fun onLocationPermissionDenied(toastMessage: String) = Unit - override fun onLocationPermissionGranted() { - if (locationPermissionsHelper!!.isLocationAccessToAppsTurnedOn()) { - locationManager.registerLocationManager() - drawMyLocationMarker() - } else { - locationPermissionsHelper!!.showLocationOffDialog(requireActivity(), R.string.location_off_dialog_text) - } - onLocationChanged(LocationChangeType.PERMISSION_JUST_GRANTED, null) - } - - fun onLocationChanged(locationChangeType: LocationChangeType, location: Location?) { - if (locationChangeType == LocationChangeType.PERMISSION_JUST_GRANTED) { - val curLatLng = locationManager.getLastLocation() ?: getMapCenter() - populatePlaces(curLatLng) - } else { - presenter!!.updateMap(locationChangeType) - } - } + override fun onLocationPermissionGranted() = Unit companion object { fun newInstance(): ExploreMapFragment { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.kt b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.kt index d16d250dd..e1d0740de 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDao.kt @@ -163,19 +163,11 @@ class RecentSearchesDao @Inject constructor( * @param cursor * @return RecentSearch object */ - fun fromCursor(cursor: Cursor): RecentSearch { - var query = cursor.getString(COLUMN_NAME) - - if (query == null) { - query = "" - } - - return RecentSearch( - uriForId(cursor.getInt(COLUMN_ID)), - query, - Date(cursor.getLong(COLUMN_LAST_USED)) - ) - } + fun fromCursor(cursor: Cursor): RecentSearch = RecentSearch( + uriForId(cursor.getInt(COLUMN_ID)), + cursor.getString(COLUMN_NAME), + Date(cursor.getLong(COLUMN_LAST_USED)) + ) /** * This class contains the database table architechture for recent searches, diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt index e7903c9ed..c0f1bd5db 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt @@ -67,10 +67,10 @@ class RecentSearchesFragment : CommonsDaggerSupportFragment() { private fun showDeleteRecentAlertDialog(context: Context) { AlertDialog.Builder(context) .setMessage(getString(R.string.delete_recent_searches_dialog)) - .setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int -> + .setPositiveButton(android.R.string.yes) { dialog: DialogInterface, _: Int -> setDeleteRecentPositiveButton(context, dialog) } - .setNegativeButton(R.string.no, null) + .setNegativeButton(android.R.string.no, null) .setCancelable(false) .create() .show() @@ -102,7 +102,7 @@ class RecentSearchesFragment : CommonsDaggerSupportFragment() { setDeletePositiveButton(context, dialog, position) } ) - .setNegativeButton(R.string.cancel, null) + .setNegativeButton(android.R.string.cancel, null) .setCancelable(false) .create() .show() diff --git a/app/src/main/java/fr/free/nrw/commons/fileusages/FileUsagesUiModel.kt b/app/src/main/java/fr/free/nrw/commons/fileusages/FileUsagesUiModel.kt index 540c87e4c..63b0740d0 100644 --- a/app/src/main/java/fr/free/nrw/commons/fileusages/FileUsagesUiModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/fileusages/FileUsagesUiModel.kt @@ -1,68 +1,18 @@ package fr.free.nrw.commons.fileusages -import android.net.Uri -import timber.log.Timber - /** - * Data model for displaying file usage information in the UI, including the title and link to the page. + * Show where file is being used on Commons and oher wikis. */ data class FileUsagesUiModel( val title: String, val link: String? ) -/** - * Converts a FileUsage object to a UI model for Commons file usages. - * Creates a link to the file's page on Commons. - */ fun FileUsage.toUiModel(): FileUsagesUiModel { - // Replace spaces with underscores and URL-encode the title for the link - val encodedTitle = Uri.encode(title.replace(" ", "_")) - return FileUsagesUiModel( - title = title, - link = "https://commons.wikimedia.org/wiki/$encodedTitle" - ) + return FileUsagesUiModel(title = title, link = "https://commons.wikimedia.org/wiki/$title") } -/** - * Converts a GlobalFileUsage object to a UI model for file usages on other wikis. - * Generates a link to the page and prefixes the title with the wiki code (e.g., "(en) Title"). - */ fun GlobalFileUsage.toUiModel(): FileUsagesUiModel { - // Log input values for debugging - Timber.d("Converting GlobalFileUsage: wiki=$wiki, title=$title") - - // Check for invalid or empty inputs - if (wiki.isBlank() || title.isBlank()) { - Timber.w("Invalid input: wiki=$wiki, title=$title") - return FileUsagesUiModel(title = title, link = null) - } - - // Extract wiki code for prefix (e.g., "en" from "en.wikipedia.org" or "enwiki") - val wikiCode = when { - wiki.contains(".") -> wiki.substringBefore(".") // e.g., "en" from "en.wikipedia.org" - wiki == "commonswiki" -> "commons" - wiki.endsWith("wiki") -> wiki.removeSuffix("wiki") - else -> wiki - } - - // Create prefixed title, e.g., "(en) Changi East Depot" - val prefixedTitle = "($wikiCode) $title" - - // Determine the domain for the URL - val domain = when { - wiki.contains(".") -> wiki // Already a full domain, e.g., "en.wikipedia.org" - wiki == "commonswiki" -> "commons.wikimedia.org" - wiki.endsWith("wiki") -> wiki.removeSuffix("wiki") + ".wikipedia.org" - else -> "$wiki.wikipedia.org" // Fallback for simple codes like "en" - } - - // Normalize title: replace spaces with underscores and URL-encode - val encodedTitle = Uri.encode(title.replace(" ", "_")) - - // Build the full URL - val url = "https://$domain/wiki/$encodedTitle" - Timber.d("Generated URL: $url") - - return FileUsagesUiModel(title = prefixedTitle, link = url) -} \ No newline at end of file + // link is associated with sub items under wiki group (which is not used ATM) + return FileUsagesUiModel(title = wiki, link = null) +} diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt index 47b4165ad..fefb59adb 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt @@ -64,8 +64,8 @@ class LocationPermissionsHelper( activity, activity.getString(dialogTitleResource), activity.getString(dialogTextResource), - activity.getString(R.string.ok), - activity.getString(R.string.cancel), + activity.getString(android.R.string.ok), + activity.getString(android.R.string.cancel), { ActivityCompat.requestPermissions( activity, diff --git a/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt b/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt index 08dee587b..e4fedf2e4 100644 --- a/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt @@ -46,7 +46,6 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Compani import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomInsets -import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets import fr.free.nrw.commons.utils.applyEdgeToEdgeTopPaddingInsets import fr.free.nrw.commons.utils.handleGeoCoordinates import io.reactivex.android.schedulers.AndroidSchedulers @@ -343,10 +342,6 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { } private fun setupMapView() { - - val mapBottomLayout: ConstraintLayout = findViewById(R.id.map_bottom_layout) - mapBottomLayout.applyEdgeToEdgeBottomPaddingInsets() - requestLocationPermissions() //If location metadata is available, move map to that location. diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt index 41e65ae4e..d34c162dc 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt @@ -541,7 +541,6 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C } ) binding.progressBarEdit.visibility = View.GONE - binding.descriptionEdit.visibility = View.VISIBLE } override fun onConfigurationChanged(newConfig: Configuration) { @@ -1027,12 +1026,12 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C val message: String = if (result) { context.getString( R.string.send_thank_success_message, - media!!.user + media!!.displayTitle ) } else { context.getString( R.string.send_thank_failure_message, - media!!.user + media!!.displayTitle ) } @@ -2129,17 +2128,22 @@ fun FileUsagesContainer( val uriHandle = LocalUriHandler.current Column(modifier = modifier) { + Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { + Text( text = stringResource(R.string.usages_on_commons_heading), textAlign = TextAlign.Center, style = MaterialTheme.typography.titleSmall ) - IconButton(onClick = { isCommonsListExpanded = !isCommonsListExpanded }) { + + IconButton(onClick = { + isCommonsListExpanded = !isCommonsListExpanded + }) { Icon( imageVector = if (isCommonsListExpanded) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, @@ -2153,8 +2157,11 @@ fun FileUsagesContainer( MediaDetailViewModel.FileUsagesContainerState.Loading -> { LinearProgressIndicator() } + is MediaDetailViewModel.FileUsagesContainerState.Success -> { + val data = commonsContainerState.data + if (data.isNullOrEmpty()) { ListItem(headlineContent = { Text( @@ -2174,7 +2181,7 @@ fun FileUsagesContainer( headlineContent = { Text( modifier = Modifier.clickable { - usage.link?.let { uriHandle.openUri(it) } + uriHandle.openUri(usage.link!!) }, text = usage.title, style = MaterialTheme.typography.titleSmall.copy( @@ -2182,11 +2189,11 @@ fun FileUsagesContainer( textDecoration = TextDecoration.Underline ) ) - } - ) + }) } } } + is MediaDetailViewModel.FileUsagesContainerState.Error -> { ListItem(headlineContent = { Text( @@ -2196,10 +2203,12 @@ fun FileUsagesContainer( ) }) } + MediaDetailViewModel.FileUsagesContainerState.Initial -> {} } } + Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -2210,7 +2219,10 @@ fun FileUsagesContainer( textAlign = TextAlign.Center, style = MaterialTheme.typography.titleSmall ) - IconButton(onClick = { isOtherWikisListExpanded = !isOtherWikisListExpanded }) { + + IconButton(onClick = { + isOtherWikisListExpanded = !isOtherWikisListExpanded + }) { Icon( imageVector = if (isOtherWikisListExpanded) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, @@ -2224,8 +2236,11 @@ fun FileUsagesContainer( MediaDetailViewModel.FileUsagesContainerState.Loading -> { LinearProgressIndicator() } + is MediaDetailViewModel.FileUsagesContainerState.Success -> { + val data = globalContainerState.data + if (data.isNullOrEmpty()) { ListItem(headlineContent = { Text( @@ -2244,20 +2259,16 @@ fun FileUsagesContainer( }, headlineContent = { Text( - modifier = Modifier.clickable { - usage.link?.let { uriHandle.openUri(it) } - }, text = usage.title, style = MaterialTheme.typography.titleSmall.copy( - color = Color(0xFF5A6AEC), textDecoration = TextDecoration.Underline ) ) - } - ) + }) } } } + is MediaDetailViewModel.FileUsagesContainerState.Error -> { ListItem(headlineContent = { Text( @@ -2267,8 +2278,10 @@ fun FileUsagesContainer( ) }) } + MediaDetailViewModel.FileUsagesContainerState.Initial -> {} } } + } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/CheckBoxTriStates.java b/app/src/main/java/fr/free/nrw/commons/nearby/CheckBoxTriStates.java index 323f9756f..db2c1f5d9 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/CheckBoxTriStates.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/CheckBoxTriStates.java @@ -44,7 +44,7 @@ public class CheckBoxTriStates extends AppCompatCheckBox { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { switch (state) { case UNKNOWN: - setState(UNCHECKED); + setState(UNCHECKED);; break; case UNCHECKED: setState(CHECKED); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java index 53e9970a6..b5f760c9f 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java @@ -91,7 +91,6 @@ public class NearbyFilterSearchRecyclerViewAdapter label.setSelected(!label.isSelected()); holder.placeTypeLayout.setSelected(label.isSelected()); - NearbyFilterState.setSelectedLabels(new ArrayList<>(selectedLabels)); callback.filterByMarkerType(selectedLabels, 0, false, false); }); } @@ -153,7 +152,6 @@ public class NearbyFilterSearchRecyclerViewAdapter label.setSelected(false); selectedLabels.remove(label); } - NearbyFilterState.setSelectedLabels(new ArrayList<>(selectedLabels)); notifyDataSetChanged(); } @@ -165,7 +163,6 @@ public class NearbyFilterSearchRecyclerViewAdapter selectedLabels.add(label); } } - NearbyFilterState.setSelectedLabels(new ArrayList<>(selectedLabels)); notifyDataSetChanged(); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterState.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterState.java index d0aec96af..d3ece9bfa 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterState.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterState.java @@ -9,7 +9,7 @@ public class NearbyFilterState { private int checkBoxTriState; private ArrayList