diff --git a/CHANGELOG.md b/CHANGELOG.md index fc22a2b99..6fd325813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Wikimedia Commons for Android +## 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/app/build.gradle.kts b/app/build.gradle.kts index 674a6473f..ddbbb4984 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,14 +18,14 @@ if (isRunningOnTravisAndIsNotPRBuild) { android { namespace = "fr.free.nrw.commons" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "fr.free.nrw.commons" minSdk = 21 - targetSdk = 34 - versionCode = 1055 - versionName = "5.6.1" + targetSdk = 35 + versionCode = 1057 + versionName = "6.0.1" setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName()) testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt index ebbb4097a..865ad3ddb 100644 --- a/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt @@ -19,6 +19,7 @@ import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import java.util.Collections import androidx.core.net.toUri +import fr.free.nrw.commons.utils.applyEdgeToEdgeTopInsets import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.utils.setUnderlinedText @@ -47,6 +48,7 @@ class AboutActivity : BaseActivity() { */ binding = ActivityAboutBinding.inflate(layoutInflater) val view: View = binding!!.root + applyEdgeToEdgeTopInsets(binding!!.toolbarLayout) setContentView(view) setSupportActionBar(binding!!.toolbarBinding.toolbar) 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 d15c72f57..1c28d5fe4 100644 --- a/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt +++ b/app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt @@ -50,7 +50,7 @@ object OkHttpConnectionFactory { } } -private class CommonHeaderRequestInterceptor : Interceptor { +class CommonHeaderRequestInterceptor : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request().newBuilder() diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt index 439ed1e92..0882ba117 100644 --- a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt @@ -9,6 +9,7 @@ import fr.free.nrw.commons.databinding.ActivityWelcomeBinding import fr.free.nrw.commons.databinding.PopupForCopyrightBinding import fr.free.nrw.commons.quiz.QuizActivity import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour class WelcomeActivity : BaseActivity() { @@ -23,6 +24,7 @@ class WelcomeActivity : BaseActivity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityWelcomeBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding!!.welcomePager.rootView) setContentView(binding!!.root) isQuiz = intent?.extras?.getBoolean("isQuiz", false) ?: false 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 7a665197b..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 @@ -22,6 +22,7 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatDelegate import androidx.core.app.NavUtils import androidx.core.content.ContextCompat +import androidx.core.view.WindowCompat import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R @@ -32,11 +33,13 @@ import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.databinding.ActivityLoginBinding import fr.free.nrw.commons.di.ApplicationlessInjection import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.utils.AbstractTextWatcher import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard +import fr.free.nrw.commons.utils.handleKeyboardInsets import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.disposables.CompositeDisposable import timber.log.Timber @@ -79,7 +82,14 @@ class LoginActivity : AccountAuthenticatorActivity() { delegate.installViewFactory() delegate.onCreate(savedInstanceState) + WindowCompat.getInsetsController(window, window.decorView) + .isAppearanceLightStatusBars = !isDarkTheme + + WindowCompat.setDecorFitsSystemWindows(window, false) + binding = ActivityLoginBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding!!.root) + binding?.aboutPrivacyPolicy?.handleKeyboardInsets() with(binding!!) { setContentView(root) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.kt b/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.kt index 5b48ecd8f..22f557bcd 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.kt @@ -10,6 +10,7 @@ import android.widget.Toast import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.R import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import timber.log.Timber class SignupActivity : BaseActivity() { @@ -21,6 +22,7 @@ class SignupActivity : BaseActivity() { Timber.d("Signup Activity started") webView = WebView(this) + applyEdgeToEdgeAllInsets(webView!!) with(webView!!) { setContentView(this) webViewClient = MyWebViewClient() diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsContentProvider.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsContentProvider.kt index 8007ba208..c532ed3cc 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsContentProvider.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsContentProvider.kt @@ -36,7 +36,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() { requireDb(), projection, selection, selectionArgs, null, null, sortOrder ).apply { - setNotificationUri(requireContext().contentResolver, uri) + setNotificationUri(context?.contentResolver, uri) } } @@ -66,7 +66,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() { ) } - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rowsUpdated } @@ -75,7 +75,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() { */ override fun insert(uri: Uri, contentValues: ContentValues?): Uri? { val id = requireDb().insert(TABLE_NAME, null, contentValues) - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return "$BASE_URI/$id".toUri() } @@ -89,7 +89,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() { "$COLUMN_ID = ?", arrayOf(uri.lastPathSegment) ) - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rows } diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesContentProvider.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesContentProvider.kt index bf6f6039b..a47eed8ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesContentProvider.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesContentProvider.kt @@ -36,7 +36,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() { requireDb(), projection, selection, selectionArgs, null, null, sortOrder ) - cursor.setNotificationUri(requireContext().contentResolver, uri) + cursor.setNotificationUri(context?.contentResolver, uri) return cursor } @@ -66,7 +66,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() { "Parameter `selection` should be empty when updating an ID" ) } - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rowsUpdated } @@ -75,7 +75,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() { */ override fun insert(uri: Uri, contentValues: ContentValues?): Uri { val id = requireDb().insert(TABLE_NAME, null, contentValues) - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return "$BASE_URI/$id".toUri() } @@ -85,7 +85,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() { "media_name = ?", arrayOf(uri.lastPathSegment) ) - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rows } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt index c998f96ac..fefe462a9 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt @@ -23,6 +23,7 @@ import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment import fr.free.nrw.commons.media.MediaDetailProvider import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.model.WikiSite import fr.free.nrw.commons.wikidata.model.page.PageTitle @@ -57,6 +58,7 @@ class CategoryDetailsActivity : BaseActivity(), binding = ActivityCategoryDetailsBinding.inflate(layoutInflater) val view = binding.root + applyEdgeToEdgeAllInsets(view) setContentView(view) supportFragmentManager = getSupportFragmentManager() viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager) 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 b9fa3e395..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 @@ -34,6 +34,7 @@ import fr.free.nrw.commons.quiz.QuizChecker import fr.free.nrw.commons.settings.SettingsFragment import fr.free.nrw.commons.startWelcome import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.upload.UploadProgressActivity import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest import fr.free.nrw.commons.utils.ViewUtilWrapper @@ -112,6 +113,7 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = MainBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding!!.root) setContentView(binding!!.root) setSupportActionBar(binding!!.toolbarBinding.toolbar) tabLayout = binding!!.fragmentMainNavTabLayout 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 7e7d7e4cd..2534b4aeb 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 @@ -40,6 +40,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat +import androidx.core.view.ViewGroupCompat import androidx.lifecycle.ViewModelProvider import fr.free.nrw.commons.R import fr.free.nrw.commons.customselector.database.NotForUploadStatus @@ -56,6 +57,8 @@ import fr.free.nrw.commons.media.ZoomableActivity import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.utils.CustomSelectorUtils +import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets +import fr.free.nrw.commons.utils.applyEdgeToEdgeTopInsets import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -198,6 +201,9 @@ class CustomSelectorActivity : .fillMaxWidth(), ) } + ViewGroupCompat.installCompatInsetsDispatch(binding.root) + applyEdgeToEdgeTopInsets(toolbarBinding.toolbarLayout) + bottomSheetBinding.bottomLayout.applyEdgeToEdgeBottomPaddingInsets() val view = binding.root setContentView(view) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt index 6ca2b06e4..0c3c5bdd0 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt @@ -18,6 +18,7 @@ import fr.free.nrw.commons.databinding.FragmentCustomSelectorBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.upload.FileProcessor +import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets import javax.inject.Inject /** @@ -99,6 +100,7 @@ class FolderFragment : CommonsDaggerSupportFragment() { selectorRV = binding?.selectorRv loader = binding?.loader with(binding?.selectorRv) { + this?.applyEdgeToEdgeBottomPaddingInsets() this?.layoutManager = gridLayoutManager this?.setHasFixedSize(true) this?.adapter = folderAdapter 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 6e08e30f1..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 @@ -41,6 +41,7 @@ import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper +import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -217,6 +218,7 @@ class ImageFragment : imageAdapter.setSingleSelection(singleSelection) gridLayoutManager = GridLayoutManager(context, getSpanCount()) with(binding?.selectorRv) { + this?.applyEdgeToEdgeBottomPaddingInsets() this?.layoutManager = gridLayoutManager this?.setHasFixedSize(true) this?.adapter = imageAdapter 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 44cefe4d5..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 @@ -7,6 +7,7 @@ import android.speech.RecognizerIntent import android.view.View import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.view.WindowCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import fr.free.nrw.commons.CommonsApplication @@ -20,9 +21,11 @@ import fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao import fr.free.nrw.commons.settings.Prefs import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomInsets import fr.free.nrw.commons.upload.UploadMediaDetail import fr.free.nrw.commons.upload.UploadMediaDetailAdapter import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.applyEdgeToEdgeTopPaddingInsets import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Consumer import io.reactivex.schedulers.Schedulers @@ -87,6 +90,10 @@ class DescriptionEditActivity : super.onCreate(savedInstanceState) binding = ActivityDescriptionEditBinding.inflate(layoutInflater) + applyEdgeToEdgeBottomInsets(binding.btnEditSubmit) + WindowCompat.getInsetsController(window, window.decorView) + .isAppearanceLightStatusBars = false + binding.toolbar.applyEdgeToEdgeTopPaddingInsets() setContentView(binding.root) val bundle = intent.extras diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.kt b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.kt index 2539db312..9246ff303 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.kt @@ -7,6 +7,7 @@ import dagger.Provides import fr.free.nrw.commons.BetaConstants import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.OkHttpConnectionFactory +import fr.free.nrw.commons.CommonHeaderRequestInterceptor import fr.free.nrw.commons.actions.PageEditClient import fr.free.nrw.commons.actions.PageEditInterface import fr.free.nrw.commons.actions.ThanksInterface @@ -60,6 +61,7 @@ class NetworkingModule { .connectTimeout(120, TimeUnit.SECONDS) .writeTimeout(120, TimeUnit.SECONDS) .addInterceptor(httpLoggingInterceptor) + .addInterceptor(CommonHeaderRequestInterceptor()) .readTimeout(120, TimeUnit.SECONDS) .cache(Cache(File(context.cacheDir, "okHttpCache"), OK_HTTP_CACHE_SIZE)) .build() diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.kt b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.kt index 7b7bb2cd5..0d7dfd218 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.kt @@ -22,6 +22,7 @@ import fr.free.nrw.commons.media.MediaDetailProvider import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.utils.FragmentUtils.isFragmentUIActive import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import io.reactivex.android.schedulers.AndroidSchedulers import timber.log.Timber import java.util.Date @@ -48,6 +49,7 @@ class SearchActivity : BaseActivity(), MediaDetailProvider, CategoryImagesCallba override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivitySearchBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding!!.root) setContentView(binding!!.root) title = getString(R.string.title_activity_search) 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 4696ae8d4..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 @@ -24,6 +24,7 @@ import fr.free.nrw.commons.media.MediaDetailProvider import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.structure.depictions.DepictModel import fr.free.nrw.commons.upload.structure.depictions.DepictedItem +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.WikidataConstants import io.reactivex.android.schedulers.AndroidSchedulers @@ -55,6 +56,7 @@ class WikidataItemDetailsActivity : BaseActivity(), MediaDetailProvider, Categor super.onCreate(savedInstanceState) binding = ActivityWikidataItemDetailsBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding!!.root) setContentView(binding!!.root) supportFragmentManager = getSupportFragmentManager() viewPagerAdapter = ViewPagerAdapter(this, getSupportFragmentManager()) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.kt b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.kt index 21f7a1a22..c18f101ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesContentProvider.kt @@ -50,7 +50,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() { else -> throw IllegalArgumentException("Unknown URI$uri") } - cursor.setNotificationUri(requireContext().contentResolver, uri) + cursor.setNotificationUri(context?.contentResolver, uri) return cursor } @@ -67,7 +67,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() { else -> throw IllegalArgumentException("Unknown URI: $uri") } - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return "$BASE_URI/$id".toUri() } @@ -88,7 +88,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() { else -> throw IllegalArgumentException("Unknown URI - $uri") } - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rows } @@ -108,7 +108,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() { } sqlDB.setTransactionSuccessful() sqlDB.endTransaction() - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return values.size } @@ -147,7 +147,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() { else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType") } - requireContext().contentResolver.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rowsUpdated } diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt index bb0a371e1..a7e3a671d 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt @@ -296,10 +296,19 @@ object FilePicker : Constants { * https://github.com/commons-app/apps-android-commons/issues/6357 */ private fun takePersistableUriPermissions(context: Context, result: ActivityResult) { - result.data?.data?.also { uri -> - val takeFlags: Int = (Intent.FLAG_GRANT_READ_URI_PERMISSION - or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - context.contentResolver.takePersistableUriPermission(uri, takeFlags) + result.data?.let { intentData -> + val takeFlags: Int = (Intent.FLAG_GRANT_READ_URI_PERMISSION) + // Persist the URI permission for all URIs in the clip data + // if multiple images are selected, + // or for the single URI if only one image is selected + intentData.clipData?.let { clipData -> + for (i in 0 until clipData.itemCount) { + context.contentResolver.takePersistableUriPermission( + clipData.getItemAt(i).uri, takeFlags) + } + } ?: intentData.data?.let { uri -> + context.contentResolver.takePersistableUriPermission(uri, takeFlags) + } } } @@ -358,6 +367,7 @@ object FilePicker : Constants { callbacks: Callbacks ) { if (result.resultCode == Activity.RESULT_OK && !isPhoto(result.data)) { + takePersistableUriPermissions(activity, result) try { val files = getFilesFromGalleryPictures(result.data, activity) callbacks.onImagesPicked(files, ImageSource.GALLERY, restoreType(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 a8b6ddf26..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 @@ -25,6 +25,7 @@ import androidx.core.content.ContextCompat import androidx.core.content.IntentCompat import androidx.core.os.BundleCompat import androidx.core.text.HtmlCompat +import androidx.core.view.WindowCompat import com.google.android.material.floatingactionbutton.FloatingActionButton import fr.free.nrw.commons.CameraPosition import fr.free.nrw.commons.CommonsApplication @@ -44,6 +45,8 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Compani import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Companion.LAST_ZOOM 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.applyEdgeToEdgeTopPaddingInsets import fr.free.nrw.commons.utils.handleGeoCoordinates import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -330,6 +333,9 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { */ private fun getToolbarUI() { val toolbar: ConstraintLayout = findViewById(R.id.location_picker_toolbar) + WindowCompat.getInsetsController(window, window.decorView) + .isAppearanceLightStatusBars = false + toolbar.applyEdgeToEdgeTopPaddingInsets() largeToolbarText = findViewById(R.id.location_picker_toolbar_primary_text_view) smallToolbarText = findViewById(R.id.location_picker_toolbar_secondary_text_view) toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.primaryColor)) @@ -460,6 +466,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { */ private fun addPlaceSelectedButton() { placeSelectedButton = findViewById(R.id.location_chosen_button) + applyEdgeToEdgeBottomInsets(placeSelectedButton) placeSelectedButton.setOnClickListener { placeSelected() } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt index c39d8901d..1d5d7bd80 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt @@ -7,7 +7,8 @@ class NearbyResultItem( private val wikipediaArticle: ResultTuple?, private val commonsArticle: ResultTuple?, private val location: ResultTuple?, - private val label: ResultTuple?, + @field:SerializedName("label") private val label: ResultTuple?, + @field:SerializedName("itemLabel") private val itemLabel: ResultTuple?, @field:SerializedName("streetAddress") private val address: ResultTuple?, private val icon: ResultTuple?, @field:SerializedName("class") private val className: ResultTuple?, @@ -29,7 +30,15 @@ class NearbyResultItem( fun getLocation(): ResultTuple = location ?: ResultTuple() - fun getLabel(): ResultTuple = label ?: ResultTuple() + /** + * Returns label for display (pins, popup), using fallback to itemLabel if needed. + */ + fun getLabel(): ResultTuple = label ?: itemLabel ?: ResultTuple() + + /** + * Returns only the original label field, for Wikidata edits. + */ + fun getOriginalLabel(): ResultTuple = label ?: ResultTuple() fun getIcon(): ResultTuple = icon ?: ResultTuple() diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt index 76975964b..4a43bf470 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt @@ -8,6 +8,7 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View +import androidx.core.view.ViewGroupCompat import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar @@ -19,8 +20,10 @@ import fr.free.nrw.commons.databinding.ActivityNotificationBinding 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.applyEdgeToEdgeTopInsets import fr.free.nrw.commons.utils.NetworkUtils import fr.free.nrw.commons.utils.ViewUtil +import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers @@ -56,6 +59,9 @@ class NotificationActivity : BaseActivity() { super.onCreate(savedInstanceState) isRead = intent.getStringExtra("title") == "read" binding = ActivityNotificationBinding.inflate(layoutInflater) + ViewGroupCompat.installCompatInsetsDispatch(binding.root) + applyEdgeToEdgeTopInsets(binding.toolbar.toolbar) + binding.listView.applyEdgeToEdgeBottomPaddingInsets() setContentView(binding.root) mNotificationWorkerFragment = supportFragmentManager.findFragmentByTag( tagNotificationWorkerFragment diff --git a/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt b/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt index d80be9ea2..c368d6cd4 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt @@ -21,6 +21,7 @@ import fr.free.nrw.commons.databinding.ActivityProfileBinding import fr.free.nrw.commons.profile.achievements.AchievementsFragment import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.utils.DialogUtil import java.io.File import java.io.FileOutputStream @@ -61,6 +62,7 @@ class ProfileActivity : BaseActivity() { super.onCreate(savedInstanceState) binding = ActivityProfileBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding.root) setContentView(binding.root) setSupportActionBar(binding.toolbarBinding.toolbar) diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.kt b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.kt index e77c24c8d..5dcdf283b 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.kt @@ -311,7 +311,7 @@ class LeaderboardFragment : CommonsDaggerSupportFragment() { } private class SelectionListener(private val handler: () -> Unit): AdapterView.OnItemSelectedListener { - override fun onItemSelected(adapterView: AdapterView<*>?, view: View, i: Int, l: Long) = + override fun onItemSelected(adapterView: AdapterView<*>?, view: View?, i: Int, l: Long) = handler() override fun onNothingSelected(p0: AdapterView<*>?) = Unit diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.kt b/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.kt index e65b819e5..11fd1e6a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizActivity.kt @@ -3,9 +3,11 @@ package fr.free.nrw.commons.quiz import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle +import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import com.facebook.drawee.drawable.ProgressBarDrawable @@ -15,6 +17,7 @@ import fr.free.nrw.commons.databinding.ActivityQuizBinding import java.util.ArrayList import fr.free.nrw.commons.R +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets class QuizActivity : AppCompatActivity() { @@ -37,7 +40,11 @@ class QuizActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() binding = ActivityQuizBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding.root) + WindowCompat.getInsetsController(window, window.decorView) + .isAppearanceLightStatusBars = true setContentView(binding.root) quizController.initialize(this) diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt b/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt index 81372b4a6..6979edd15 100644 --- a/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt @@ -12,9 +12,11 @@ import android.view.MenuItem import android.view.View import android.widget.ImageView import android.widget.TextView +import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat import fr.free.nrw.commons.databinding.ActivityQuizResultBinding import java.io.File @@ -22,6 +24,7 @@ import java.io.FileOutputStream import fr.free.nrw.commons.R import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets /** @@ -35,7 +38,11 @@ class QuizResultActivity : AppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() binding = ActivityQuizResultBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding!!.root) + WindowCompat.getInsetsController(window, window.decorView) + .isAppearanceLightStatusBars = true setContentView(binding?.root) setSupportActionBar(binding?.toolbar?.toolbar) diff --git a/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProvider.kt b/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProvider.kt index 04c18fbb7..80128ba73 100644 --- a/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProvider.kt +++ b/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProvider.kt @@ -59,7 +59,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() { null, sortOrder ) - cursor.setNotificationUri(requireContext().contentResolver, uri) + cursor.setNotificationUri(context?.contentResolver, uri) return cursor } @@ -90,7 +90,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() { throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID") } - requireContext().contentResolver?.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rowsUpdated } @@ -105,7 +105,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() { null, contentValues ) - requireContext().contentResolver?.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return "$BASE_URI/$id".toUri() } @@ -119,7 +119,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() { "language_code = ?", arrayOf(uri.lastPathSegment) ) - requireContext().contentResolver?.notifyChange(uri, null) + context?.contentResolver?.notifyChange(uri, null) return rows } } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt index 20f289f8f..42a75aeac 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt @@ -16,6 +16,7 @@ import fr.free.nrw.commons.databinding.ActivityReviewBinding import fr.free.nrw.commons.delete.DeleteHelper import fr.free.nrw.commons.media.MediaDetailFragment import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.ViewUtil import io.reactivex.android.schedulers.AndroidSchedulers @@ -73,6 +74,7 @@ class ReviewActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityReviewBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding.root) setContentView(binding.root) setSupportActionBar(binding.toolbarBinding?.toolbar) diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsActivity.kt b/app/src/main/java/fr/free/nrw/commons/settings/SettingsActivity.kt index 91c88d7b0..233e688f4 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.MenuItem import fr.free.nrw.commons.databinding.ActivitySettingsBinding import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets /** @@ -21,6 +22,7 @@ class SettingsActivity : BaseActivity() { super.onCreate(savedInstanceState) binding = ActivitySettingsBinding.inflate(layoutInflater) val view = binding.root + applyEdgeToEdgeAllInsets(view) setContentView(view) setSupportActionBar(binding.toolbarBinding.toolbar) diff --git a/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.kt b/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.kt index d2d936460..d317a7d35 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration import android.os.Bundle import android.util.DisplayMetrics import android.view.WindowManager +import androidx.activity.enableEdgeToEdge import javax.inject.Inject import javax.inject.Named import fr.free.nrw.commons.R @@ -36,6 +37,7 @@ abstract class BaseActivity : CommonsDaggerAppCompatActivity() { 1f ) adjustFontScale(resources.configuration, fontScale) + enableEdgeToEdge() } override fun onResume() { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt index 38e7dace8..74597bc14 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt @@ -38,6 +38,7 @@ import fr.free.nrw.commons.mwapi.UserClient import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.settings.Prefs import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import fr.free.nrw.commons.upload.ThumbnailsAdapter.OnThumbnailDeletedListener import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment import fr.free.nrw.commons.upload.depicts.DepictsFragment @@ -177,6 +178,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C presenter?.setupBasicKvStoreFactory { BasicKvStore(this@UploadActivity, it) } _binding = ActivityUploadBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(_binding!!.root, false) setContentView(binding.root) // Overrides the back button to make sure the user is prepared to lose their progress diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt index 3cf9d3a65..665f106e2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt @@ -10,6 +10,7 @@ import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.databinding.ActivityUploadProgressBinding import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets import javax.inject.Inject /** @@ -35,6 +36,7 @@ class UploadProgressActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityUploadProgressBinding.inflate(layoutInflater) + applyEdgeToEdgeAllInsets(binding.root) setContentView(binding.root) viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager) binding.uploadProgressViewPager.setAdapter(viewPagerAdapter) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt index 4a4c13ba7..6ece53170 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt @@ -50,6 +50,7 @@ import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK import fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.handleKeyboardInsets import timber.log.Timber import java.io.File import java.util.ArrayList @@ -153,6 +154,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentUploadMediaDetailFragmentBinding.inflate(inflater, container, false) + _binding!!.mediaDetailCardView.handleKeyboardInsets() return binding.root } @@ -821,6 +823,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra { showProgress(false) uploadItem.imageQuality = IMAGE_OK + uploadItem.hasInvalidLocation = false // Reset invalid location flag when user confirms upload }, { presenterCallback!!.deletePictureAtIndex(index) 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 c8a1d9b98..21db20f1b 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 @@ -393,6 +393,12 @@ class UploadWorker( makeWikiDataEdit(uploadResult, contribution) } showSuccessNotification(contribution) + if (appContext.contentResolver.persistedUriPermissions.any { + it.uri == contribution.contentUri }) { + appContext.contentResolver.releasePersistableUriPermission( + contribution.contentUri!!, Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + } } else { Timber.e("Stash Upload failed") showFailedNotification(contribution) diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt index 332c8d023..95fa62a20 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt @@ -12,9 +12,9 @@ object ConfigUtils { val isBetaFlavour: Boolean = BuildConfig.FLAVOR == "beta" @JvmStatic - private fun Context.getVersionName(): String = + private fun Context.getVersionName(): String? = try { - packageManager.getPackageInfo(packageName, 0).versionName + packageManager.getPackageInfo(packageName, 0).versionName ?: BuildConfig.VERSION_NAME } catch (e: PackageManager.NameNotFoundException) { BuildConfig.VERSION_NAME } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/EdgeToEdgeUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/EdgeToEdgeUtils.kt new file mode 100644 index 000000000..d0c2b12e8 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/EdgeToEdgeUtils.kt @@ -0,0 +1,229 @@ +package fr.free.nrw.commons.utils + +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsAnimationCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.marginBottom +import androidx.core.view.marginLeft +import androidx.core.view.marginRight +import androidx.core.view.marginTop +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePadding +import fr.free.nrw.commons.R + +/** + * Applies edge-to-edge system bar insets to a [View]’s margins using a custom adjustment block. + * + * Stores the initial margins to ensure inset calculations are additive, and applies the provided + * [block] with an [InsetsAccumulator] containing initial and system bar inset values. + * + * @param typeMask The type of window insets to apply. Defaults to [WindowInsetsCompat.Type.systemBars]. + * @param shouldConsumeInsets If `true`, the insets are consumed and not propagated to child views. + * @param block Lambda applied to update [MarginLayoutParams] using the accumulated insets. + */ +fun View.applyEdgeToEdgeInsets( + typeMask: Int = WindowInsetsCompat.Type.systemBars(), + shouldConsumeInsets: Boolean = true, + block: MarginLayoutParams.(InsetsAccumulator) -> Unit +) { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + val insets = windowInsets.getInsets(typeMask) + + val initialTop = if (view.getTag(R.id.initial_margin_top) != null) { + view.getTag(R.id.initial_margin_top) as Int + } else { + view.setTag(R.id.initial_margin_top, view.marginTop) + view.marginTop + } + + val initialBottom = if (view.getTag(R.id.initial_margin_bottom) != null) { + view.getTag(R.id.initial_margin_bottom) as Int + } else { + view.setTag(R.id.initial_margin_bottom, view.marginBottom) + view.marginBottom + } + + val initialLeft = if (view.getTag(R.id.initial_margin_left) != null) { + view.getTag(R.id.initial_margin_left) as Int + } else { + view.setTag(R.id.initial_margin_left, view.marginLeft) + view.marginLeft + } + + val initialRight = if (view.getTag(R.id.initial_margin_right) != null) { + view.getTag(R.id.initial_margin_right) as Int + } else { + view.setTag(R.id.initial_margin_right, view.marginRight) + view.marginRight + } + + val accumulator = InsetsAccumulator( + initialTop, + insets.top, + initialBottom, + insets.bottom, + initialLeft, + insets.left, + initialRight, + insets.right + ) + + view.updateLayoutParams { + apply { block(accumulator) } + } + + if(shouldConsumeInsets) WindowInsetsCompat.CONSUMED else windowInsets + } +} + +/** + * Applies edge-to-edge system bar insets to the top padding of the view. + * + * @param typeMask The type of window insets to apply. Defaults to [WindowInsetsCompat.Type.systemBars]. + */ +fun View.applyEdgeToEdgeTopPaddingInsets( + typeMask: Int = WindowInsetsCompat.Type.systemBars(), +) { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + val insets = windowInsets.getInsets(typeMask) + + view.updatePadding( + left = insets.left, + right = insets.right, + top = insets.top + ) + + WindowInsetsCompat.CONSUMED + } +} + +/** + * Applies edge-to-edge system bar insets to the bottom padding of the view. + * + * @param typeMask The type of window insets to apply. Defaults to [WindowInsetsCompat.Type.systemBars]. + */ +fun View.applyEdgeToEdgeBottomPaddingInsets( + typeMask: Int = WindowInsetsCompat.Type.systemBars(), +) { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + val insets = windowInsets.getInsets(typeMask) + + view.updatePadding( + left = insets.left, + right = insets.right, + bottom = insets.bottom + ) + + WindowInsetsCompat.CONSUMED + } +} + +/** + * Applies system bar insets to all margins (top, bottom, left, right) of the view. + * + * @param view The target view. + * @param shouldConsumeInsets If `true`, the insets are consumed and not propagated to child views. + */ +fun applyEdgeToEdgeAllInsets( + view: View, + shouldConsumeInsets: Boolean = true +) = view.applyEdgeToEdgeInsets(shouldConsumeInsets = shouldConsumeInsets) { insets -> + leftMargin = insets.left + rightMargin = insets.right + topMargin = insets.top + bottomMargin = insets.bottom +} + +/** + * Applies system bar insets to the top and horizontal margins of the view. + * + * @param view The target view. + */ +fun applyEdgeToEdgeTopInsets(view: View) = view.applyEdgeToEdgeInsets { insets -> + leftMargin = insets.left + rightMargin = insets.right + topMargin = insets.top +} + +/** + * Applies system bar insets to the bottom and horizontal margins of the view. + * + * @param view The target view. + */ +fun applyEdgeToEdgeBottomInsets(view: View) = view.applyEdgeToEdgeInsets { insets -> + leftMargin = insets.left + rightMargin = insets.right + bottomMargin = insets.bottom +} + +/** + * Adjusts a [View]'s bottom margin dynamically to account for the on-screen keyboard (IME), + * ensuring the view remains visible above the keyboard during transitions. + * + * Preserves the initial margin, adjusts during IME visibility changes, + * and accounts for navigation bar insets to avoid double offsets. + */ +fun View.handleKeyboardInsets() { + var existingBottomMargin = 0 + + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + existingBottomMargin = if (view.getTag(R.id.initial_margin_bottom) != null) { + view.getTag(R.id.initial_margin_bottom) as Int + } else { + view.setTag(R.id.initial_margin_bottom, view.marginBottom) + view.marginBottom + } + + WindowInsetsCompat.CONSUMED + } + + // Animate during IME transition + ViewCompat.setWindowInsetsAnimationCallback( + this, + object : WindowInsetsAnimationCompat.Callback( + DISPATCH_MODE_CONTINUE_ON_SUBTREE + ) { + override fun onProgress( + insets: WindowInsetsCompat, + runningAnimations: MutableList + ): WindowInsetsCompat { + val lp = layoutParams as MarginLayoutParams + val navBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) + val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()) + val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) + + // Avoid extra space due to system nav bar when the keyboard is shown + val imeBottomMargin = imeInsets.bottom - navBarInsets.bottom + + lp.bottomMargin = if(imeVisible && imeBottomMargin >= existingBottomMargin) + imeBottomMargin + existingBottomMargin + else existingBottomMargin + + layoutParams = lp + return WindowInsetsCompat.CONSUMED + } + } + ) +} + +/** + * Holds both initial margin values and system bar insets, providing summed values + * for each side (top, bottom, left, right) to apply in layout updates. + */ +data class InsetsAccumulator( + private val initialTop: Int, + private val insetTop: Int, + private val initialBottom: Int, + private val insetBottom: Int, + private val initialLeft: Int, + private val insetLeft: Int, + private val initialRight: Int, + private val insetRight: Int +) { + val top = initialTop + insetTop + val bottom = initialBottom + insetBottom + val left = initialLeft + insetLeft + val right = initialRight + insetRight +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.kt index ebff3d054..fa538bb21 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.kt @@ -24,6 +24,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import timber.log.Timber +import androidx.core.graphics.createBitmap /** * Created by blueSir9 on 3/10/17. @@ -307,16 +308,19 @@ object ImageUtils { * * @return */ @JvmStatic - fun addRedBorder(bitmap: Bitmap, borderSize: Int, context: Context): Bitmap { - val bmpWithBorder = Bitmap.createBitmap( - bitmap.width + borderSize * 2, - bitmap.height + borderSize * 2, - bitmap.config - ) - val canvas = Canvas(bmpWithBorder) - canvas.drawColor(ContextCompat.getColor(context, R.color.deleteRed)) - canvas.drawBitmap(bitmap, borderSize.toFloat(), borderSize.toFloat(), null) - return bmpWithBorder + fun addRedBorder(bitmap: Bitmap, borderSize: Int, context: Context): Bitmap? { + return bitmap.config?.let { config -> + val bmpWithBorder = + createBitmap( + width = bitmap.width + borderSize * 2, + height = bitmap.height + borderSize * 2, + config = config + ) + val canvas = Canvas(bmpWithBorder) + canvas.drawColor(ContextCompat.getColor(context, R.color.deleteRed)) + canvas.drawBitmap(bitmap, borderSize.toFloat(), borderSize.toFloat(), null) + return bmpWithBorder + } } /** diff --git a/app/src/main/res/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml index a8b60dea3..800c8aa0b 100644 --- a/app/src/main/res/layout/activity_notification.xml +++ b/app/src/main/res/layout/activity_notification.xml @@ -35,6 +35,7 @@ android:scrollbars="vertical" android:fadeScrollbars="false" android:scrollbarThumbVertical="@color/primaryColor" + android:clipToPadding="false" android:scrollbarSize="@dimen/dimen_6"/> diff --git a/app/src/main/res/layout/fragment_custom_selector.xml b/app/src/main/res/layout/fragment_custom_selector.xml index 03381fd24..b016b6605 100644 --- a/app/src/main/res/layout/fragment_custom_selector.xml +++ b/app/src/main/res/layout/fragment_custom_selector.xml @@ -34,6 +34,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/switchWidget" + android:clipToPadding="false" /> Περιγραφή Συζήτηση Συγγραφέας + Ανεβαστής Ημερομηνία μεταφόρτωσης Άδεια Συντεταγμένες @@ -433,7 +434,7 @@ Συνειδητοποίησα ότι είναι κακό για την ιδιωτικότητά μου Άλλαξα γνώμη, δε θέλω να προβάλλεται πλέον δημόσια Συγγνώμη, αυτή η φωτογραφία δεν είναι ενδιαφέρουσα για μια εγκυκλοπαίδεια - Ανέβηκε από εμένα στο %1$s, χρησιμοποιήθηκε σε %2$d άρθρο/α + Ανέβηκε από εμένα στο %1$s, χρησιμοποιήθηκε σε τουλάχιστον %2$d άρθρο/α. Καλώς ήρθατε στα Commons!\n\nΑνεβάστε τα πρώτα σας πολυμέσα πατώντας το κουμπί της προσθήκης. Δεν επιλέχθηκαν κατηγορίες Οι εικόνες χωρίς κατηγορίες χρησιμοποιούνται σπάνια. Θέλετε πράγματι να συνεχίσετε δίχως να επιλέξετε κατηγορίες; @@ -773,7 +774,7 @@ Απαιτούνται δικαιώματα για τη λειτουργικότητα Μάθετε πώς να γράψετε μια χρήσιμη περιγραφή Μάθετε πώς να γράψετε μια χρήσιμη λεζάντα - Δείτε τα επιτεύγματά σας + Δείτε τα επιτεύγματά σας Επεξεργασία εικόνας Επεξεργασία τοποθεσίας Η τοποθεσία ενημερώθηκε! @@ -834,4 +835,5 @@ Εμφάνιση στα Κοντινά Δημιουργήθηκε και μεταφορτώθηκε από: %1$s Δημιουργήθηκε από %1$s και μεταφορτώθηκε από %2$s + Προτάθηκε για Διαγραφή diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index e640bf981..8d66a8267 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -30,6 +30,9 @@ Poistu sijainnin valitsimesta Lähetä Lisää toinen kuvaus + Lisää uusi tiedosto + Lisää uusi tiedosto kameralla + Lisää uusi tiedosto kuvista Kuvatekstit Kielen kuvaus Kuvateksti @@ -45,7 +48,7 @@ (%1$d) (%1$d) - Aloitetaan latauksia + Aloitetaan tallennuksia Käsitellään %d tallennus Käsitellään %d tallennusta @@ -87,7 +90,7 @@ Poistetaanko akun optimointi käytöstä? Tunnistautuminen epäonnistui, kirjaudu uudelleen sisään Tallentaminen aloitettiin! - Lataus jonossa (rajoitettu yhteystila käytössä) + Tallennus on jonossa (rajoitettu yhteystila käytössä) %1$s tallennettiin! Napauta katsoaksesi tallennusta Kopioidaan palvelimelle: %s @@ -106,6 +109,8 @@ Ota kuva Lähistöllä Omat tallennukset + Kopioi linkki + Linkki on kopioitu leikepöydälle. Jaa Näytä tiedostosivu Kuvateksti (vaaditaan) @@ -122,13 +127,14 @@ Muutokset Tallenna Etsi luokkia - Hae kohteita, joita mediasi kuvaa (vuori, Taj Mahal jne.) + Hae kohteita, joita mediasi esittää (vuori, Taj Mahal jne.) Tallenna + Ylivuotovalikko Päivitä Lista (Ei vielä tallennuksia) Luokkaa %1$s ei löytynyt - Wikidata-kohteita ei löytynyt + Hakusanaa %1$s vastaavia Wikidata-kohteita ei löytynyt %1$s ei ole lapsiluokkia %1$s ei ole vanhempia luokkia Lisää luokkia tehdäksesi kuvistasi enemmän löydettäviä Wikimedia Commonssissa.\nAloita kirjoittaminen lisätäksesi luokkia. @@ -136,6 +142,7 @@ Asetukset Rekisteröidy Suositellut kuvat + Mukautettu valitsin Luokka Vertaisarviointi Tietoja @@ -151,7 +158,7 @@ Et ole vielä tallentanut kuvia. Yritä uudelleen Peruuta - Lisäämällä kuvan, ilmoitan tämän olevan oma työ ja että se ei sisällä tekijänoikeuden alaista materiaalia tai selfietä ja muuten noudattaa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. + Lisäämällä kuvan, ilmoitan tämän olevan oma työ ja että se ei sisällä tekijänoikeuden alaista materiaalia tai selfietä ja muuten noudattaa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commonsin käytäntöjä</a>. Lataa Oletuslisenssi Käytä edellistä otsikkoa ja kuvausta @@ -174,7 +181,7 @@ ÄLÄ tallenna seuraavia: Selfiet tai kuvat ystävistäsi Kuvia, jotka olet ladannut Internetistä - Kuvakaappaukset omistamistasi sovelluksista + Kuvakaappaukset muiden omistamista sovelluksista Tallennusesimerkki: Otsikko: Sydneyn oopperatalo Kuvaus: Sydneyn oopperatalo lahden toiselta puolelta katsottuna @@ -200,15 +207,17 @@ Pyydetään sijaintilupaa OK Varoitus + Vastaava tiedostonimi löytyi Tallenna Kyllä Ei Kuvateksti Otsikko - Kuvaukset + Esittää-tunnisteet Kuvaus Keskustelu Tekijä + Tallentaja Tallennuspäivämäärä Lisenssi Koordinaatit @@ -216,6 +225,7 @@ Ryhdy beetatestaajaksi Valitse beeta-kanavamme Google Playssa ja hanki varhainen pääsy uusiin ominaisuuksiin ja virheenkorjauksiin Kaksivaiheisen tunnistautumisen koodi + Sähköpostivahvistuskoodi Haluatko varmasti kirjautua ulos? Mediakuva epäonnistui Alaluokkia ei löytynyt @@ -236,6 +246,7 @@ Tietoja Asetukset Palaute + Palaute GitHubissa Kirjaudu ulos Opas Ilmoitukset @@ -270,14 +281,14 @@ Ohita Kirjaudu sisään Haluatko todella ohittaa kirjautumisen? - Sinun täytyy kirjautua sisään tallentaaksesi kuvia tulevaisuudessa. + Sinun täytyy kirjautua sisään tallentaaksesi kuvia tulevaisuudessa. Kirjaudu sisään käyttääksesi tätä ominaisuutta Kopioi wikiteksti leikepöydälle Wikiteksti kopioitiin leikepöydälle - Nearby ei välttämättä toimi, sillä sijainti ei käytettävissä. + Lähistöllä-toiminto ei välttämättä toimi kunnolla, sillä sijainti ei käytettävissä. Sijainnin käyttö kielletty. Aseta sijaintisi manuaalisesti käyttääksesi tätä ominaisuutta. - Lupa vaaditaan läheisten paikkojen luettelon näyttämiseen - Lupa vaaditaan läheltä otettujen kuvien luettelon näyttämiseen + Lupa vaaditaan lähellä olevien paikkojen luettelon näyttämiseen + Lupa vaaditaan lähellä otettujen kuvien luettelon näyttämiseen Reitit Wikidata Wikipedia @@ -312,7 +323,7 @@ Äskettäiset haut: Äskettäin haetut kyselyt Luokkia ladattaessa tapahtui virhe. - Virhe ladattaessa kuvauksia. + Virhe ladattaessa esittää-tunnisteita. Media Luokat Kohteet @@ -327,8 +338,8 @@ Onko tämä kuva OK tallennettavaksi? Kysymys Tulos - Jos jatkat poistettavien kuvien lataamista, tilisi todennäköisesti kielletään. Haluatko varmasti lopettaa tietokilpailun? - Yli %1$s tallentamistasi kuvista on poistettu. Mikäli jatkat poistamista vaativien kuvien lataamista, tilisi todennäköisesti estetään.\n\nHaluatko tutustua oppaaseen uudelleen ja tehdä sen jälkeen tietovisan oppiaksesi minkälaisia kuvia saa ja ei saa tallentaa? + Jos jatkat poistettavien kuvien tallentamista, tunnuksesi tullaan todennäköisesti estämään. Haluatko varmasti lopettaa tietokilpailun? + Yli %1$s tallentamistasi kuvista on poistettu. Mikäli jatkat poistamista vaativien kuvien tallentamista, tunnuksesi tullaan todennäköisesti estämään.\n\nHaluatko tutustua oppaaseen uudelleen ja tehdä sen jälkeen tietovisan oppiaksesi, minkälaisia kuvia saa ja ei saa tallentaa? Selfieillä ei ole paljoa arvoa tietosanakirjassa. Älä tallenna kuvaa itsestäsi, ellei sinusta jo ole Wikipedia-artikkelia. Monumenteista ja maisemista otetut kuvat ovat hyväksyttäviä tallennettavaksi useimmissa maissa. Huomaa kuitenkin että ulkotiloihin sijoitetut väliaikaiset tilataideteokset ovat usein suojattu tekijänoikeudella ja niistä otettuja kuvia ei usein saa tallentaa. Kuvakaappauksia sivustoista tulkitaan jäljennöksiksi ja ovat täten sivuston kopiosuojan piirissä. Kuvia voidaan käyttää asianmukaisella tekijältä saadulta luvalla. Ilman kyseistä lupaa, mitä tahansa heidän materiaalistaan tuottamaa tuotosta tulkitaan alkuperäisen tekijän näkökulmasta luvattomaksi kopioksi. @@ -350,17 +361,18 @@ Virhe läheisiä monumentteja haettaessa. Ei viimeaikaisia hakuja Haluatko varmasti tyhjentää hakuhistoriasi? - Haluatko varmasti peruuttaa tämän latauksen? + Haluatko varmasti peruuttaa tämän tallennuksen? Haluatko poistaa tämän haun? Hakuhistoria poistettu Ehdota poistettavaksi Poista Saavutukset Profiili + Merkit Tilastot Kiitos vastaanotettu Suositellut kuvat - Kuvia läheltä + \"Lähistöllä\"-kuvat Taso %d %s (taso %s) Kuvia tallennettu @@ -369,9 +381,9 @@ Jaa saavutuksesi ystäviesi kanssa! Tasosi nousee, kun täytät nämä vaatimukset. Tilastot-osion kohteita ei lasketa tasoosi. vähimmäisvaatimus: - Lähetettyjen kuvien määrä Commonsiin minkä tahansa latausohjelmiston kautta + Lähetettyjen kuvien määrä Commonsiin minkä tahansa tallennusohjelmiston kautta Niiden kuvien prosenttiosuus, jotka olet ladannut Commonsiin ja joita ei poistettu - Wikimedia-artikkeleissa käytettyjen Commonsiin lataamiesi kuvien määrä + Wikimedia-artikkeleissa käytettyjen Commonsiin tallentamiesi kuvien määrä Tapahtui virhe! Commons-ilmoitus Käytä mukautettua tekijän nimeä @@ -385,7 +397,7 @@ Näytä sovelluksen sisäinen ilmoitus lähinnä kuvia tarvitsevasta paikasta Lista Tallennuslupa - Tarvitsemme luvan käyttääksesi laitteen ulkoista tallennustilaa kuvien lataamista varten. + Tarvitsemme luvan käyttääksesi laitteen ulkoista tallennustilaa kuvien tallentamista varten. Et enää näe lähellä olevia paikkoja, jotka tarvitsevat kuvia. Voit kuitenkin halutessasi ottaa tämän ilmoituksen uudelleen käyttöön asetuksissa. Vaihe %1$d %2$d: %3$s Seuraava @@ -394,6 +406,7 @@ Laitteestasi ei löydy yhteensopivaa karttasovellusta. Asenna karttasovellus käyttääksesi tätä toimintoa. Kuvat Sijainnit + Luokat Lisää kirjanmerkkeihin/Poista kirjanmerkeistä Kirjanmerkit Et ole lisännyt yhtään kirjanmerkkejä @@ -408,8 +421,8 @@ Tervetuloa Commonsiin!\n\nTallenna ensimmäinen mediasi koskettamalla lisäyspainiketta. Luokkia ei valittu Kuvat, jotka eivät ole luokissa, ovat harvoin käyttökelpoisia. Haluatko varmasti jatkaa valitsematta luokkia? - Kuvauksia ei valittu - Kuvat, joissa on kuvatekstejä, löytyvät helpommin ja todennäköisemmin niitä käytetään. Haluatko varmasti jatkaa valitsematta kuvatekstejä? + Esittää-tunnisteita ei valittu + Kuvat, joissa on esittää-tunnisteita, löytyvät helpommin ja niitä käytetään todennäköisemmin. Haluatko varmasti jatkaa valitsematta esittää-tunnisteita? Peruuta tallennus Takaisin-napin painaminen peruuttaa tämän tallennuksen ja poistaa tallentamasi tiedot Jatka tallennusta @@ -467,6 +480,7 @@ Sinulla ei ole lukemattomia ilmoituksia Sinulla ei ole luettuja ilmoituksia Jaa lokit käyttämällä + Tarkista sähköpostilaatikkosi Näytä luetut Näytä lukemattomat Kuvien valinnassa tapahtui virhe @@ -485,11 +499,11 @@ Linssin malli Sarjanumerot Ohjelmisto - Lähetä valokuvia suoraan Wikimedia Commonsiin puhelimestasi. Lataa Commons-appi nyt: %1$s + Tallenna kuvia Wikimedia Commonsiin suoraan puhelimeltasi. Lataa Commons-sovellus nyt: %1$s Jaa sovellus... Kuvan tiedot Luokkia ei löytynyt - Kuvauksia ei löytynyt + Esittää-tunnisteita ei löytynyt Peruutettu tallennus Miksi %1$s tulisi poistaa? %1$s oli lähettänyt: %2$s @@ -505,6 +519,7 @@ Lehdistökuva Satunnainen kuva internetistä Logo + Panoraamavapauden rikkomus Koska se on Yritetään päivittää luokkia. Luokan päivitys @@ -515,6 +530,9 @@ Ei voitu lisätä luokkia. Päivitetään luokkia + Yritetään päivittää esittää-tunnisteita. + Muokkaa esittää-tunnisteita + Esittää-tunnisteita ei voitu lisätä. Yritetään päivittää koordinaatit. Koordinaattien päivitys Kuvaus päivitetty @@ -526,7 +544,7 @@ Koordinaatteja ei voitu lisätä. Kuvauksia ei voitu lisätä. Kuvatekstiä ei voitu lisätä. - Koordinaattien haku epäonnistui. + Kuvan koordinaatteja ei tallennettu Kuvauksia ei voitu hakea. Muokkaa kuvauksia ja kuvatekstejä Jaa kuva @@ -543,8 +561,13 @@ Silta, museo, hotelli jne. Jokin meni pieleen kirjautumisessa. Sinun on nollattava salasanasi! MEDIA - Lähipaikka löytyi - Onko tämä kuva paikasta %1$s? + ALALUOKAT + YLÄLUOKAT + ALALUOKAT + YLÄLUOKAT + Lähistöllä-paikka löytyi + Ovatko nämä kuvia paikasta %1$s? + Onko tämä kuva paikasta %1$s? Kirjanmerkit Asetukset Poistettu kirjanmerkeistä @@ -592,7 +615,7 @@ Viikoittain Koko ajalta Lähetä - Lähistöltä + Lähistöllä Käyttöjä Sijani Rajoitettu yhteystila päällä! @@ -605,9 +628,9 @@ Peruuta tallennus Rajoitettu yhteystila on päällä. Kirjoita lyhyt kuvateksti. Kerro miksi kuva on kiinnostava, tyypillinen tai harvinainen ja selitä asiayhteys, näkyy se kuvassa tai ei. Käytä mahdollisimman tarkkaa terminologiaa. - Etsi ja valitse kaikki tämän kuvan kuvaamat käsitteet. Ole mahdollisimman tarkka. Mikäli kuvattuna on monta kohdetta, valitse ne kaikki kohtuullisuuden rajoissa. Älä valitse yleisiä tunnisteita mikäli tarkempia on saatavilla. - Valitse sopivat luokat. Toisin kuin kuvaukset, luokkien nimet ovat vain englanniksi. - Kuka tahansa saa käyttää ja muokata Commonsiin lataamiasi kuvia. Haluatko luovuttaa kuviesi kaikki oikeudet? Haluatko tulla nimetyksi kuvien tekijänä? Haluatko kuviesi muokattujen versioiden julkaistavan samalla lisenssillä? + Etsi ja valitse kaikki käsitteet, joita tämä kuva esittää. Ole mahdollisimman tarkka. Mikäli kuvattuna on monta kohdetta, valitse ne kaikki kohtuullisuuden rajoissa. Älä valitse yleisiä tunnisteita, mikäli tarkempia on saatavilla. + Valitse sopivat luokat. Toisin kuin esittää-tunnisteet, luokkien nimet ovat vain englanniksi. + Kuka tahansa saa käyttää ja muokata Commonsiin tallentamiasi kuvia. Haluatko luovuttaa kuviesi kaikki oikeudet? Haluatko tulla nimetyksi kuvien tekijänä? Haluatko kuviesi muokattujen versioiden julkaistavan samalla lisenssillä? Esittää Median lisenssi Median tiedot @@ -629,18 +652,19 @@ Ei kuvia Valmis Takaisin - Mahtava + Mahtavaa Tämä kuva on jo ladattu Commonsiin. LUE LISÄÄ Tarvitaan käyttöoikeus Käyttäjän muokkaukset: %s Käyttäjän saavutukset: %s - Näytä käyttäjäsivu + Näytä käyttäjäprofiili + Muokkaa esittää-tunnisteita Muokkaa luokkia Lisäasetukset Käytä Nollaa - Sijaintitiedot auttavat wikin muokkaajia löytämään kuvasi, mikä tekee siitä paljon hyödyllisemmän.\nViimeaikaisissa tallennuksissasi ei ole sijaintia.\nSuosittelemme, että otat sijainnin käyttöön kamerasovelluksesi asetuksista.\nKiitos latauksesta! + Sijaintitiedot auttavat wikin muokkaajia löytämään kuvasi, mikä tekee siitä paljon hyödyllisemmän.\nViimeaikaisissa tallennuksissasi ei ole sijaintia.\nSuosittelemme, että otat sijainnin käyttöön kamerasovelluksesi asetuksista.\nKiitos kuvien tallentamista! Paikkaa ei löytynyt Lisää paikka Tiedot @@ -669,6 +693,7 @@ Näytä omat saavutukset Muokkaa kuvaa Muokkaa sijaintia + Sijainti päivitetty! Poista sijainti Sijainti poistettu! Kiitä tekijää @@ -681,4 +706,34 @@ %d kuva valittu %d kuvaa valittu + Keskustelu + Oletko varma, että haluat peruuttaa kaikki tallennukset? + Peruutetaan kaikki tallennukset... + Tallennukset + Odottavat + Epäonnistuneet + Poista kansio + Vahvista poisto + Oletko varma, että haluat poistaa kansion %1$s, jossa on %2$d kohdetta? + Poista + Peruuta + Kansio %1$s poistettu + Kansion %1$s poistaminen epäonnistui + Tästä paikasta ei ole vielä kuvaa. Ota ihmeessä kuva! + Tästä paikasta on jo kuva. + Tarkistetaan, onko tästä paikasta kuvaa. + Virhe ladattaessa + Commons + Muut wikit + Tiedoston käyttö + Tunnus + Hävitä käyttäjätunnus + Varoitus hävittämisestä + Hävittäminen on <b>viimeinen keino</b> ja sitä tulee <b>käyttää vain, jos haluat lopettaa muokkaamisen lopullisesti</b> ja samalla myös piilottaa mahdollisimman paljon aiemmista toimistasi.<br/><br/>Käyttäjätunnuksen poistaminen Wikimedia Commonsissa tapahtuu muuttamalla tunnuksesi nimeä niin, etteivät muut pysty tunnistamaan muokkauksiasi. Tätä toimea kutsutaan käyttäjätunnuksen hävittämiseksi. <b>Hävittäminen ei takaa täyttä anonyymiyttä, eikä se poista hankkeisiin tekemiäsi muokkauksia</b>. + Kuvateksti + Kuvateksti kopioitu leikepöydälle + Näytä Tutki-välilehdellä + Luonut ja tallentanut: %1$s + Luonut %1$s ja tallentanut %2$s + Ehdotettu poistettavaksi diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 75cb51542..8025813fc 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -271,7 +271,7 @@ Texto wiki copiado ó portapapeis A localización non está dispoñible. A identificación de sitios próximos pode non funcionar correctamente. Precísase permiso para amosar unha lista de lugares preto de aquí - COMO CHEGAR + Indicacións WIKIDATA Wikipedia COMMONS @@ -354,6 +354,7 @@ Imaxes destacadas Imaxes vía \"Lugares próximos\" Nivel %d + %s (nivel %s) Imaxes cargadas Imaxes non revertidas Imaxes usadas diff --git a/app/src/main/res/values-io/strings.xml b/app/src/main/res/values-io/strings.xml index 0c41d5492..8ba3b61d4 100644 --- a/app/src/main/res/values-io/strings.xml +++ b/app/src/main/res/values-io/strings.xml @@ -649,13 +649,18 @@ Pauzanta sendajo... Nuliganta sendajo... Cesar kargajo + Vu kapabligesis l\'uzo di limitizita konekto. Omna senduri pauzesis e durigos nur kande vu deskapabligos ta uzo. Kapabligesis por uzar limitizita konekti. Voluntez skribar kurta titulo deskriptanta quon vua imajo montras. En la deskripto, explikez pro quo la fotografuro esas interesanta, tipala o rara, ed explikez la kuntexto, videbla o ne. Skriptez tan exakta kam posibla. + Voluntez trovar e selektar omna konceptaji quan ca imajo reprezentas. Esez plu preciza kam vu povas. Se ta imajo montras diversa kozi, selektez precize omna ek li. Ne uzez nepreciza deskripturi, se specifika deskripturi existas. + Voluntez selektar la kategorii konvenanta. Diferante de deskripturi, kategorii nur existas en Angla linguo. + En Commons, vua imaji povos riuzesar ed adaptesar da omni. Ka vu deziras renuncar omna autoroyuri? Ka vu deziras ke l\'imajo atribuesos a vu? Ka vu deziras adapti por uzar la sama licenco? Montras Licencizo di \'\'media\'\' Detali pri \'\'media\'\' Vidar kategorio-pagino Vidar pagino dil arkivo + Idiomo di vua interfacio Removar titulo e deskripto Lektez pluse En omna idiomi @@ -676,6 +681,7 @@ Facita Retroirar Bonveno a personalizita selektilo di imaji + Ica selektilo montras quala imaji vu ja sendis a Commons. Ecelanta Ca imajo ja sendesis a Commons. Por teknikala motivi, l\'utensilo \'\'app\'\' ne povas fidinde sendar plua kam %1$d pikturi samatempe. La limito %1$d superesis per %2$d. @@ -767,9 +773,13 @@ %d imajo selektita %d imaji selektita + Voluntez facar kelka komenti Diskuto Dicez irgu pri l\'arkivo \'%1$s\'. Ol esos videbla publike. + \'%1$s\' ne pluse existas, nula imajo povos rekuperesar de ol. \'%1$s\' esas en diferanta loko. + \'%1$s\' esas en diferanta loko. Voluntez mencionar la korekta loko adinfre e, se posibla, skribez la korekta latitudo e longitudo. + Altra problemo od informo (voluntez explikar adinfre). Extinganta la tota sendaji... Arkivi sendita Vartanta @@ -799,6 +809,8 @@ Deskripto-texto Deskripto-texto kopiita a \'\'clipboard\'\' Gratuli! Omna imaji en ca albumo sive sendesis, sive indikesis por ne sendar. + Montrez en Proxima (\'\'Nearby\'\') Kreesis e sendesis da: %1$s Kreita da %1$s e sendita da %2$s + Indikita por Efaco diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 5e0e0c602..7ca0b0ea0 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -2,6 +2,7 @@ ಕಾಮನ್ಸ್ ಫೇಸ್ಬುಕ್ ಪುಟ + ಕಾಮನ್ಸ್‌ನ ಗಿಟ್‍ಹಬ್ ಮೂಲ ಕೋಡ್ + ಕಾಮನ್ಸ್‌ ಲಾಂಛನ ಕಾಮನ್ಸ್ ಜಾಲತಾಣ + ಸ್ಥಳ ಆಯ್ಕೆಯಿಂದ ನಿರ್ಗಮಿಸಿ ಸಲ್ಲಿಸಿ + ಇನ್ನೊಂದು ವಿವರಣೆಯನ್ನು ಸೇರಿಸಿ + ಹೊಸ ಕೊಡುಗೆಗಳನ್ನು ಸೇರಿಸಿ + ಕ್ಯಾಮೆರಾದಿಂದ ಕೊಡುಗೆಯನ್ನು ಸೇರಿಸಿ + ಫೋಟೋಗಳಿಂದ ಕೊಡುಗೆಯನ್ನು ಸೇರಿಸಿ + ಹಿಂದಿನ ಕೊಡುಗೆಗಳ ಗ್ಯಾಲರಿಯಿಂದ ಕೊಡುಗೆಯನ್ನು ಸೇರಿಸಿ + ತಲೆಬರಹ + ಭಾಷಾ ವಿವರಣೆ + ತಲೆಬರಹ + ವಿವರಣೆ + ಚಿತ್ರ + ಎಲ್ಲಾ + ಮೇಲಕ್ಕೆ ಟಾಗಲ್ ಮಾಡಿ ದಿನದ ಚಿತ್ರ %1$d ಕಡತ ಅಪ್ಲೋಡ್ ಅಗುತ್ತಿದೆ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d297d07ac..b7624f049 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -24,7 +24,7 @@ * 아라 --> - 공용 페이스북 페이지 + 공용 페이스북 문서 공용 GitHub 소스 코드 공용 로고 공용 웹사이트 @@ -35,11 +35,11 @@ 카메라에서 기여 참가 사진에서 기여 참가 이전 기여 갤러리에서 기여 참가 - 캡션 + 설명 언어 설명 캡션 설명 - 이미지 + 그림 모두 위로 전환 검색 뷰 @@ -141,7 +141,7 @@ %1$s와(과) 일치하는 분류를 찾을 수 없습니다 %1$s에 대한 위키데이터 검색 결과가 없습니다 %1$s에 자식 클래스가 없습니다 - %1$s에 부모 클래스가 없습니다 + %1$s에 상위 클래스가 없습니다 위키미디어 공용에서 그림을 더 찾기 쉽게 만들기 위해 분류를 추가합니다.\n분류를 추가하려면 입력을 시작하세요. 분류 설정 diff --git a/app/src/main/res/values-min/error.xml b/app/src/main/res/values-min/error.xml new file mode 100644 index 000000000..11f57f87d --- /dev/null +++ b/app/src/main/res/values-min/error.xml @@ -0,0 +1,10 @@ + + + + Commons bamasalah + Ups. Ado nan salah! + Caritokan apo nan sanak karajoan, sudah tu bagikan lewat email ka kami. Akan kami bantu mamelokannyo! + Tarimo kasih! + diff --git a/app/src/main/res/values-mnw/strings.xml b/app/src/main/res/values-mnw/strings.xml index a6c18bca3..4d7b2ed37 100644 --- a/app/src/main/res/values-mnw/strings.xml +++ b/app/src/main/res/values-mnw/strings.xml @@ -226,7 +226,7 @@ ဗဒင်ဏအ် ဟွံကၠောန်ကမၠောန် ဗွဲဓမ္မတာ၊ ဒၞာဲဒတန် ဟွံသၟဟ်အစောမ်။ အာတ်မိက်ဒၟံင် အခေါင် သွက်ဂွံထ္ၜး စရင်ဒၞာဲဒတန် ဗဒင်ဗဒင် စမၞောန်ဂမၠိုင် - ဝဳကဳဒါတာ + ဝဳကဳတင်ဂၞင် ဝဳကဳပဳဒဳယာ ခမ်မောန် ကဵုင္ၚုဟ် ကုပိုယ် diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index 99923ca5f..d8b764a67 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -322,6 +322,7 @@ ߖߌ߬ߦߊ߬ߓߍ߫ ߟߊߓߊ߯ߙߕߊ ߟߎ߬ ߖߌ߬ߦߊ߬ߓߍ ߞߊߕߙߍ߬ \"ߛߌ߰ߢߐ߲߰ ߦߙߐ\" ߡߊ߬ ߞߊߓߋ + %s (ߞߊߓߋ %s) ߖߌ߬ߦߊ߬ߓߍ ߓߘߊ߫ ߟߊߦߟߍ߬ ߖߌ߬ߦߊ߬ߓߍ ߡߊ߫ ߖߏ߰ߛߌ߬ ߖߌ߬ߦߊ߬ߓߍ߬ ߟߊߓߊ߯ߙߊߣߍ߲ ߠߎ߬ diff --git a/app/src/main/res/values-ps/strings.xml b/app/src/main/res/values-ps/strings.xml index 4bfaea5ab..3b28ceeb6 100644 --- a/app/src/main/res/values-ps/strings.xml +++ b/app/src/main/res/values-ps/strings.xml @@ -227,12 +227,15 @@ لارښوونيزې خبرتياوې بياکتنه - هيڅ څرگنداوی ونهٔ موندل شو + هېڅ څرگنداوی ونه موندل شو ټولگړې دوتنې مخ ويکي‌اومتوک توکی ويکيپېډيا ليکنه انځور ډېر تياره دی. انځور ډېر جړ دی. + تېرېدل + ننوتل + آیا تاسو رښتیا غواړئ چې ننوتل پرېږدئ؟ تگلوري ويکي‌اومتوک ويکيپېډيا @@ -274,15 +277,54 @@ موبايل له لارې راپورته‌شوی نخشه انځور په ویکي‌اومتوک کې %1$s ته ورگډ شو! + پوښتنه + پايله + پرله‌پورې + سم ځواب + ناسم ځواب + کاریال وېشل + تاوول + ايا تاسو ډاډه ياست چې دا راپورته‌کول ناگارل غواړئ؟ + ايا تاسو غواړئ چې دا لټون ړنگ کړئ؟ + د پلټنې پېښليک ړنگ شو + ړنگولو ته نومول + ړنگول + لاسته‌راوړنې پېژنيال مټ‌تړوني شمارنې - مننه ترلاسه‌شو + ترلاسه‌شوې مننې ټاکلی انځور انځورنه د \"څېرمه ځايونو\" له لارې + کچه %d + %s (کچه %s) انځورونه راپورته‌شول انځورونه په څټ‌گرځول‌شوي نه دي کارول‌شوي انځورونه + تېروتنه رامنځته شوه! + د ليکوال نوم دوديزول + ونډې + څېرمه + خبرتياوې + خبرتیاوې (لوستل‌شوې) + څېرمه خبرتياوې ښکاره‌کول + لړليک + زخيره کولو اجازه + راتلونکی + مخکنی + انځورونه + ځايونه + وېشنيزې + په کتاب‌نښو کې ورگډول/لرې‌کول + کتاب‌‌نښې + تاسو هېڅ کتاب‌نښې نه دې ورگډې‌کړې + کتاب‌نښې + په تېروتنې سره مې راپورته‌کړی دی + زه نه پوهېدم چې دا به ټولو ته ښکاره شي + زه پوه شوم چې دا زما د پټنتيا لپاره بد دی + زما اند توپير وکړ، زه نه غواړم چې دا نور په ټوليزه توگه ښکاره شي + په بښنې سره دا انځور د يو پوهنغونډ لپاره خواپورې نه دی + هېڅ وېشنيزې نه دې ټاکل شوې پای ته رسېږي په: ټاکنيزې‌سيالۍ ښکاره‌کول روانې ټاکنيزې‌سيالۍ وگورئ @@ -310,6 +352,113 @@ ايا دا د منلو وړ دي؟ ايا تاسو غواړئ له ونډه‌وال نه مننه وکړئ؟ که دا انځور ټولگټی نه وي؛ نو ړنگېدو ته د نوماندولو لپاره يې په نه کليک وکړئ. + بل انځور + هو، ولې نه + ليکوال + لمېسل‌رېښتې + ځای + کامرې نمونه + د لړۍ شمېرې + پوستغالی + د رسنيو ځای ته لاسرسی رد شو + کاريال د...لارې وېشل + انځور مالومات + هېڅ وېشنيزې ونه موندل شوې + نښان + ځکه چې دا + د وېشنيزو هم‌‌مهالولو هڅه‌کول. + وېشنيزه هم‌مهالول + بریالیتوب + وېشنيزې نشي ورگډېدای. + وېشنيزې هم‌مهالول + د ښودنو هم‌مهالولو هڅه‌کول. + ښودنې سمول + څرگنداوی هم‌مهالول + نيونگ هم‌مهالول + بریالیتوب + همغږيتوبونه %1$s ورگډ شول. + څرگنداوي ورگډل شول. + نيونگ ورگډ شو. + همغږيتوبونه نشي ورگډېدای. + څرگنداوی نشي ورگډېدای. + انځور وېشل په وسيله د + تياره + روښانه + ځای‌ښودنه بلول + نور بارول + تاييدول + لارښوونې + ۱. لاندې ويکي‌ليک وکاروئ: + درول‌ + بياپيلول + درول‌شوی + نور + کتاب‌‌نښې + لاسته‌راوړنې + سرمشريزه + درجه: + شمېر: + درجه + کارن + شمېر + د سرمشريزې ځان‌بڼې په توگه اوڼل + ځان‌بڼې په توگه اوڼل کېږي، مهرباني وکړئ په تمه شئ + ځان‌بڼې ټولگه + ځان‌بڼې په توگه اوڼل + کلنی + اوونيز + هرمهاله + راپورته‌کول + څېرمه + کارول‌شوی + زما رتبه + محدودې نښلېدا ونگ‌ډول چارن‌شوی! + ښه انځورونه + انځور ځی + نښکه + څرگنداوی + توکي + دوديز ټاکونکی + انځورونه نشته + وشو + پر شا کېدل + دوديزه انځور پاکوونکي ته ښه راغلاست + په‌زړه‌پورې + تړل + اجازې ته اړتيا لري + کارن پېژنيال کتل + وېشنيزې سمول + پلي‌کول + له‌سره‌اوڼل ځای اومتوکي له ويکي کارنانو سره مرسته کوي چې ستاسو انځور موندلو او لا ډېر گټور کولو کې مرسته کوي.\nستاسو وروستۍ راپورته‌کېدنې ځای نه لري.\nموږ تاسو ته سپارښته کوو چې خپل د ځای ښودنه د کامرې په کاريال په اوڼنو کې بل کړئ.\nله راپورته کولو مو مننه! + هېڅ ځای ونه موندل شو + ځای ورگډول + سپيناوی + اندرويد بلبڼه + وسيلې نوم + له غبرگون ورکولو مو مننه + انځور سمول + ځای سمول + ځای هم‌مهاله‌شو + ځای لرې‌کول + ځای گواښنه لرې‌کول + راپورته‌کېدنې + په تمه + پاتې راغلی + ځای اومتوکي نشي بارېدای + ړنگول تایید کړئ + ړنگول + ناگارل + د بارولو پرمهال تېروتنه + هېڅ کاره‌ونه ونه موندل شوه + خونديځ + نورې ويکي‌گانې + دوتنې کارېدنې + يواړخيزه‌وېب‌کتنې‌چاره گڼون + گڼون له منځه وړل + د گڼون له منځه وړلو گواښنه + نيونگ + نيونگ ټينگدړې ته ولمېسل شو + ړنگولو ته نومول‌شوې diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 659c2705a..9c242678c 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1,5 +1,6 @@ + @android:color/transparent + @android:color/transparent