Merge branch 'main' into issue-6351

This commit is contained in:
Nicolas Raoul 2025-08-23 22:23:13 +09:00 committed by GitHub
commit 72f7d12614
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 367 additions and 38 deletions

View file

@ -18,14 +18,14 @@ if (isRunningOnTravisAndIsNotPRBuild) {
android { android {
namespace = "fr.free.nrw.commons" namespace = "fr.free.nrw.commons"
compileSdk = 34 compileSdk = 35
defaultConfig { defaultConfig {
applicationId = "fr.free.nrw.commons" applicationId = "fr.free.nrw.commons"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 35
versionCode = 1055 versionCode = 1056
versionName = "5.6.1" versionName = "6.0.0"
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName()) setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

View file

@ -19,6 +19,7 @@ import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
import java.util.Collections import java.util.Collections
import androidx.core.net.toUri 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.handleWebUrl
import fr.free.nrw.commons.utils.setUnderlinedText import fr.free.nrw.commons.utils.setUnderlinedText
@ -47,6 +48,7 @@ class AboutActivity : BaseActivity() {
*/ */
binding = ActivityAboutBinding.inflate(layoutInflater) binding = ActivityAboutBinding.inflate(layoutInflater)
val view: View = binding!!.root val view: View = binding!!.root
applyEdgeToEdgeTopInsets(binding!!.toolbarLayout)
setContentView(view) setContentView(view)
setSupportActionBar(binding!!.toolbarBinding.toolbar) setSupportActionBar(binding!!.toolbarBinding.toolbar)

View file

@ -9,6 +9,7 @@ import fr.free.nrw.commons.databinding.ActivityWelcomeBinding
import fr.free.nrw.commons.databinding.PopupForCopyrightBinding import fr.free.nrw.commons.databinding.PopupForCopyrightBinding
import fr.free.nrw.commons.quiz.QuizActivity import fr.free.nrw.commons.quiz.QuizActivity
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
class WelcomeActivity : BaseActivity() { class WelcomeActivity : BaseActivity() {
@ -23,6 +24,7 @@ class WelcomeActivity : BaseActivity() {
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityWelcomeBinding.inflate(layoutInflater) binding = ActivityWelcomeBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding!!.welcomePager.rootView)
setContentView(binding!!.root) setContentView(binding!!.root)
isQuiz = intent?.extras?.getBoolean("isQuiz", false) ?: false isQuiz = intent?.extras?.getBoolean("isQuiz", false) ?: false

View file

@ -22,6 +22,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.NavUtils import androidx.core.app.NavUtils
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.R 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.databinding.ActivityLoginBinding
import fr.free.nrw.commons.di.ApplicationlessInjection import fr.free.nrw.commons.di.ApplicationlessInjection
import fr.free.nrw.commons.kvstore.JsonKvStore 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.AbstractTextWatcher
import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.SystemThemeUtils
import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard
import fr.free.nrw.commons.utils.handleKeyboardInsets
import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.utils.handleWebUrl
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber import timber.log.Timber
@ -79,7 +82,14 @@ class LoginActivity : AccountAuthenticatorActivity() {
delegate.installViewFactory() delegate.installViewFactory()
delegate.onCreate(savedInstanceState) delegate.onCreate(savedInstanceState)
WindowCompat.getInsetsController(window, window.decorView)
.isAppearanceLightStatusBars = !isDarkTheme
WindowCompat.setDecorFitsSystemWindows(window, false)
binding = ActivityLoginBinding.inflate(layoutInflater) binding = ActivityLoginBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding!!.root)
binding?.aboutPrivacyPolicy?.handleKeyboardInsets()
with(binding!!) { with(binding!!) {
setContentView(root) setContentView(root)

View file

@ -10,6 +10,7 @@ import android.widget.Toast
import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets
import timber.log.Timber import timber.log.Timber
class SignupActivity : BaseActivity() { class SignupActivity : BaseActivity() {
@ -21,6 +22,7 @@ class SignupActivity : BaseActivity() {
Timber.d("Signup Activity started") Timber.d("Signup Activity started")
webView = WebView(this) webView = WebView(this)
applyEdgeToEdgeAllInsets(webView!!)
with(webView!!) { with(webView!!) {
setContentView(this) setContentView(this)
webViewClient = MyWebViewClient() webViewClient = MyWebViewClient()

View file

@ -36,7 +36,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() {
requireDb(), projection, selection, requireDb(), projection, selection,
selectionArgs, null, null, sortOrder selectionArgs, null, null, sortOrder
).apply { ).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 return rowsUpdated
} }
@ -75,7 +75,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() {
*/ */
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? { override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
val id = requireDb().insert(TABLE_NAME, null, contentValues) val id = requireDb().insert(TABLE_NAME, null, contentValues)
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return "$BASE_URI/$id".toUri() return "$BASE_URI/$id".toUri()
} }
@ -89,7 +89,7 @@ class BookmarkItemsContentProvider : CommonsDaggerContentProvider() {
"$COLUMN_ID = ?", "$COLUMN_ID = ?",
arrayOf(uri.lastPathSegment) arrayOf(uri.lastPathSegment)
) )
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rows return rows
} }

View file

@ -36,7 +36,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() {
requireDb(), projection, selection, requireDb(), projection, selection,
selectionArgs, null, null, sortOrder selectionArgs, null, null, sortOrder
) )
cursor.setNotificationUri(requireContext().contentResolver, uri) cursor.setNotificationUri(context?.contentResolver, uri)
return cursor return cursor
} }
@ -66,7 +66,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() {
"Parameter `selection` should be empty when updating an ID" "Parameter `selection` should be empty when updating an ID"
) )
} }
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rowsUpdated return rowsUpdated
} }
@ -75,7 +75,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() {
*/ */
override fun insert(uri: Uri, contentValues: ContentValues?): Uri { override fun insert(uri: Uri, contentValues: ContentValues?): Uri {
val id = requireDb().insert(TABLE_NAME, null, contentValues) val id = requireDb().insert(TABLE_NAME, null, contentValues)
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return "$BASE_URI/$id".toUri() return "$BASE_URI/$id".toUri()
} }
@ -85,7 +85,7 @@ class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() {
"media_name = ?", "media_name = ?",
arrayOf(uri.lastPathSegment) arrayOf(uri.lastPathSegment)
) )
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rows return rows
} }

View file

@ -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.MediaDetailPagerFragment
import fr.free.nrw.commons.media.MediaDetailProvider import fr.free.nrw.commons.media.MediaDetailProvider
import fr.free.nrw.commons.theme.BaseActivity 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.utils.handleWebUrl
import fr.free.nrw.commons.wikidata.model.WikiSite import fr.free.nrw.commons.wikidata.model.WikiSite
import fr.free.nrw.commons.wikidata.model.page.PageTitle import fr.free.nrw.commons.wikidata.model.page.PageTitle
@ -57,6 +58,7 @@ class CategoryDetailsActivity : BaseActivity(),
binding = ActivityCategoryDetailsBinding.inflate(layoutInflater) binding = ActivityCategoryDetailsBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
applyEdgeToEdgeAllInsets(view)
setContentView(view) setContentView(view)
supportFragmentManager = getSupportFragmentManager() supportFragmentManager = getSupportFragmentManager()
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager) viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)

View file

@ -34,6 +34,7 @@ import fr.free.nrw.commons.quiz.QuizChecker
import fr.free.nrw.commons.settings.SettingsFragment import fr.free.nrw.commons.settings.SettingsFragment
import fr.free.nrw.commons.startWelcome import fr.free.nrw.commons.startWelcome
import fr.free.nrw.commons.theme.BaseActivity 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.UploadProgressActivity
import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest
import fr.free.nrw.commons.utils.ViewUtilWrapper import fr.free.nrw.commons.utils.ViewUtilWrapper
@ -112,6 +113,7 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = MainBinding.inflate(layoutInflater) binding = MainBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding!!.root)
setContentView(binding!!.root) setContentView(binding!!.root)
setSupportActionBar(binding!!.toolbarBinding.toolbar) setSupportActionBar(binding!!.toolbarBinding.toolbar)
tabLayout = binding!!.fragmentMainNavTabLayout tabLayout = binding!!.fragmentMainNavTabLayout

View file

@ -40,6 +40,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.ViewGroupCompat
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.customselector.database.NotForUploadStatus 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.theme.BaseActivity
import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.upload.FileUtilsWrapper
import fr.free.nrw.commons.utils.CustomSelectorUtils 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.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -198,6 +201,9 @@ class CustomSelectorActivity :
.fillMaxWidth(), .fillMaxWidth(),
) )
} }
ViewGroupCompat.installCompatInsetsDispatch(binding.root)
applyEdgeToEdgeTopInsets(toolbarBinding.toolbarLayout)
bottomSheetBinding.bottomLayout.applyEdgeToEdgeBottomPaddingInsets()
val view = binding.root val view = binding.root
setContentView(view) setContentView(view)

View file

@ -18,6 +18,7 @@ import fr.free.nrw.commons.databinding.FragmentCustomSelectorBinding
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileProcessor
import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -99,6 +100,7 @@ class FolderFragment : CommonsDaggerSupportFragment() {
selectorRV = binding?.selectorRv selectorRV = binding?.selectorRv
loader = binding?.loader loader = binding?.loader
with(binding?.selectorRv) { with(binding?.selectorRv) {
this?.applyEdgeToEdgeBottomPaddingInsets()
this?.layoutManager = gridLayoutManager this?.layoutManager = gridLayoutManager
this?.setHasFixedSize(true) this?.setHasFixedSize(true)
this?.adapter = folderAdapter this?.adapter = folderAdapter

View file

@ -41,6 +41,7 @@ import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileProcessor
import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.upload.FileUtilsWrapper
import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@ -217,6 +218,7 @@ class ImageFragment :
imageAdapter.setSingleSelection(singleSelection) imageAdapter.setSingleSelection(singleSelection)
gridLayoutManager = GridLayoutManager(context, getSpanCount()) gridLayoutManager = GridLayoutManager(context, getSpanCount())
with(binding?.selectorRv) { with(binding?.selectorRv) {
this?.applyEdgeToEdgeBottomPaddingInsets()
this?.layoutManager = gridLayoutManager this?.layoutManager = gridLayoutManager
this?.setHasFixedSize(true) this?.setHasFixedSize(true)
this?.adapter = imageAdapter this?.adapter = imageAdapter

View file

@ -7,6 +7,7 @@ import android.speech.RecognizerIntent
import android.view.View import android.view.View
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.WindowCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import fr.free.nrw.commons.CommonsApplication 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.recentlanguages.RecentLanguagesDao
import fr.free.nrw.commons.settings.Prefs import fr.free.nrw.commons.settings.Prefs
import fr.free.nrw.commons.theme.BaseActivity 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.UploadMediaDetail
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
import fr.free.nrw.commons.utils.applyEdgeToEdgeTopPaddingInsets
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -87,6 +90,10 @@ class DescriptionEditActivity :
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityDescriptionEditBinding.inflate(layoutInflater) binding = ActivityDescriptionEditBinding.inflate(layoutInflater)
applyEdgeToEdgeBottomInsets(binding.btnEditSubmit)
WindowCompat.getInsetsController(window, window.decorView)
.isAppearanceLightStatusBars = false
binding.toolbar.applyEdgeToEdgeTopPaddingInsets()
setContentView(binding.root) setContentView(binding.root)
val bundle = intent.extras val bundle = intent.extras

View file

@ -22,6 +22,7 @@ import fr.free.nrw.commons.media.MediaDetailProvider
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.utils.FragmentUtils.isFragmentUIActive import fr.free.nrw.commons.utils.FragmentUtils.isFragmentUIActive
import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard
import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import timber.log.Timber import timber.log.Timber
import java.util.Date import java.util.Date
@ -48,6 +49,7 @@ class SearchActivity : BaseActivity(), MediaDetailProvider, CategoryImagesCallba
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivitySearchBinding.inflate(layoutInflater) binding = ActivitySearchBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding!!.root)
setContentView(binding!!.root) setContentView(binding!!.root)
title = getString(R.string.title_activity_search) title = getString(R.string.title_activity_search)

View file

@ -24,6 +24,7 @@ import fr.free.nrw.commons.media.MediaDetailProvider
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.upload.structure.depictions.DepictModel import fr.free.nrw.commons.upload.structure.depictions.DepictModel
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem 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.utils.handleWebUrl
import fr.free.nrw.commons.wikidata.WikidataConstants import fr.free.nrw.commons.wikidata.WikidataConstants
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -55,6 +56,7 @@ class WikidataItemDetailsActivity : BaseActivity(), MediaDetailProvider, Categor
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityWikidataItemDetailsBinding.inflate(layoutInflater) binding = ActivityWikidataItemDetailsBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding!!.root)
setContentView(binding!!.root) setContentView(binding!!.root)
supportFragmentManager = getSupportFragmentManager() supportFragmentManager = getSupportFragmentManager()
viewPagerAdapter = ViewPagerAdapter(this, getSupportFragmentManager()) viewPagerAdapter = ViewPagerAdapter(this, getSupportFragmentManager())

View file

@ -50,7 +50,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
else -> throw IllegalArgumentException("Unknown URI$uri") else -> throw IllegalArgumentException("Unknown URI$uri")
} }
cursor.setNotificationUri(requireContext().contentResolver, uri) cursor.setNotificationUri(context?.contentResolver, uri)
return cursor return cursor
} }
@ -67,7 +67,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
else -> throw IllegalArgumentException("Unknown URI: $uri") else -> throw IllegalArgumentException("Unknown URI: $uri")
} }
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return "$BASE_URI/$id".toUri() return "$BASE_URI/$id".toUri()
} }
@ -88,7 +88,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
else -> throw IllegalArgumentException("Unknown URI - $uri") else -> throw IllegalArgumentException("Unknown URI - $uri")
} }
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rows return rows
} }
@ -108,7 +108,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
} }
sqlDB.setTransactionSuccessful() sqlDB.setTransactionSuccessful()
sqlDB.endTransaction() sqlDB.endTransaction()
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return values.size return values.size
} }
@ -147,7 +147,7 @@ class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType") else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType")
} }
requireContext().contentResolver.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rowsUpdated return rowsUpdated
} }

View file

@ -25,6 +25,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
import androidx.core.os.BundleCompat import androidx.core.os.BundleCompat
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.view.WindowCompat
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import fr.free.nrw.commons.CameraPosition import fr.free.nrw.commons.CameraPosition
import fr.free.nrw.commons.CommonsApplication 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.upload.mediaDetails.UploadMediaDetailFragment.Companion.LAST_ZOOM
import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.DialogUtil
import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL 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 fr.free.nrw.commons.utils.handleGeoCoordinates
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -330,6 +333,9 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
*/ */
private fun getToolbarUI() { private fun getToolbarUI() {
val toolbar: ConstraintLayout = findViewById(R.id.location_picker_toolbar) 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) largeToolbarText = findViewById(R.id.location_picker_toolbar_primary_text_view)
smallToolbarText = findViewById(R.id.location_picker_toolbar_secondary_text_view) smallToolbarText = findViewById(R.id.location_picker_toolbar_secondary_text_view)
toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.primaryColor)) toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.primaryColor))
@ -460,6 +466,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
*/ */
private fun addPlaceSelectedButton() { private fun addPlaceSelectedButton() {
placeSelectedButton = findViewById(R.id.location_chosen_button) placeSelectedButton = findViewById(R.id.location_chosen_button)
applyEdgeToEdgeBottomInsets(placeSelectedButton)
placeSelectedButton.setOnClickListener { placeSelected() } placeSelectedButton.setOnClickListener { placeSelected() }
} }

View file

@ -8,6 +8,7 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.core.view.ViewGroupCompat
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar 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.Notification
import fr.free.nrw.commons.notification.models.NotificationType import fr.free.nrw.commons.notification.models.NotificationType
import fr.free.nrw.commons.theme.BaseActivity 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.NetworkUtils
import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil
import fr.free.nrw.commons.utils.applyEdgeToEdgeBottomPaddingInsets
import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.utils.handleWebUrl
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -56,6 +59,9 @@ class NotificationActivity : BaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
isRead = intent.getStringExtra("title") == "read" isRead = intent.getStringExtra("title") == "read"
binding = ActivityNotificationBinding.inflate(layoutInflater) binding = ActivityNotificationBinding.inflate(layoutInflater)
ViewGroupCompat.installCompatInsetsDispatch(binding.root)
applyEdgeToEdgeTopInsets(binding.toolbar.toolbar)
binding.listView.applyEdgeToEdgeBottomPaddingInsets()
setContentView(binding.root) setContentView(binding.root)
mNotificationWorkerFragment = supportFragmentManager.findFragmentByTag( mNotificationWorkerFragment = supportFragmentManager.findFragmentByTag(
tagNotificationWorkerFragment tagNotificationWorkerFragment

View file

@ -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.achievements.AchievementsFragment
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment
import fr.free.nrw.commons.theme.BaseActivity 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.DialogUtil
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -61,6 +62,7 @@ class ProfileActivity : BaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityProfileBinding.inflate(layoutInflater) binding = ActivityProfileBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding.root)
setContentView(binding.root) setContentView(binding.root)
setSupportActionBar(binding.toolbarBinding.toolbar) setSupportActionBar(binding.toolbarBinding.toolbar)

View file

@ -3,9 +3,11 @@ package fr.free.nrw.commons.quiz
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.facebook.drawee.drawable.ProgressBarDrawable import com.facebook.drawee.drawable.ProgressBarDrawable
@ -15,6 +17,7 @@ import fr.free.nrw.commons.databinding.ActivityQuizBinding
import java.util.ArrayList import java.util.ArrayList
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets
class QuizActivity : AppCompatActivity() { class QuizActivity : AppCompatActivity() {
@ -37,7 +40,11 @@ class QuizActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityQuizBinding.inflate(layoutInflater) binding = ActivityQuizBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding.root)
WindowCompat.getInsetsController(window, window.decorView)
.isAppearanceLightStatusBars = true
setContentView(binding.root) setContentView(binding.root)
quizController.initialize(this) quizController.initialize(this)

View file

@ -12,9 +12,11 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import fr.free.nrw.commons.databinding.ActivityQuizResultBinding import fr.free.nrw.commons.databinding.ActivityQuizResultBinding
import java.io.File import java.io.File
@ -22,6 +24,7 @@ import java.io.FileOutputStream
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.contributions.MainActivity 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?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityQuizResultBinding.inflate(layoutInflater) binding = ActivityQuizResultBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding!!.root)
WindowCompat.getInsetsController(window, window.decorView)
.isAppearanceLightStatusBars = true
setContentView(binding?.root) setContentView(binding?.root)
setSupportActionBar(binding?.toolbar?.toolbar) setSupportActionBar(binding?.toolbar?.toolbar)

View file

@ -59,7 +59,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
null, null,
sortOrder sortOrder
) )
cursor.setNotificationUri(requireContext().contentResolver, uri) cursor.setNotificationUri(context?.contentResolver, uri)
return cursor return cursor
} }
@ -90,7 +90,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID") throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID")
} }
requireContext().contentResolver?.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rowsUpdated return rowsUpdated
} }
@ -105,7 +105,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
null, null,
contentValues contentValues
) )
requireContext().contentResolver?.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return "$BASE_URI/$id".toUri() return "$BASE_URI/$id".toUri()
} }
@ -119,7 +119,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
"language_code = ?", "language_code = ?",
arrayOf(uri.lastPathSegment) arrayOf(uri.lastPathSegment)
) )
requireContext().contentResolver?.notifyChange(uri, null) context?.contentResolver?.notifyChange(uri, null)
return rows return rows
} }
} }

View file

@ -16,6 +16,7 @@ import fr.free.nrw.commons.databinding.ActivityReviewBinding
import fr.free.nrw.commons.delete.DeleteHelper import fr.free.nrw.commons.delete.DeleteHelper
import fr.free.nrw.commons.media.MediaDetailFragment import fr.free.nrw.commons.media.MediaDetailFragment
import fr.free.nrw.commons.theme.BaseActivity 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.DialogUtil
import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -73,6 +74,7 @@ class ReviewActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityReviewBinding.inflate(layoutInflater) binding = ActivityReviewBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding.root)
setContentView(binding.root) setContentView(binding.root)
setSupportActionBar(binding.toolbarBinding?.toolbar) setSupportActionBar(binding.toolbarBinding?.toolbar)

View file

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import fr.free.nrw.commons.databinding.ActivitySettingsBinding import fr.free.nrw.commons.databinding.ActivitySettingsBinding
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets
/** /**
@ -21,6 +22,7 @@ class SettingsActivity : BaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
applyEdgeToEdgeAllInsets(view)
setContentView(view) setContentView(view)
setSupportActionBar(binding.toolbarBinding.toolbar) setSupportActionBar(binding.toolbarBinding.toolbar)

View file

@ -4,6 +4,7 @@ import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.enableEdgeToEdge
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
@ -36,6 +37,7 @@ abstract class BaseActivity : CommonsDaggerAppCompatActivity() {
1f 1f
) )
adjustFontScale(resources.configuration, fontScale) adjustFontScale(resources.configuration, fontScale)
enableEdgeToEdge()
} }
override fun onResume() { override fun onResume() {

View file

@ -38,6 +38,7 @@ import fr.free.nrw.commons.mwapi.UserClient
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.settings.Prefs import fr.free.nrw.commons.settings.Prefs
import fr.free.nrw.commons.theme.BaseActivity 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.ThumbnailsAdapter.OnThumbnailDeletedListener
import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment
import fr.free.nrw.commons.upload.depicts.DepictsFragment 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) } presenter?.setupBasicKvStoreFactory { BasicKvStore(this@UploadActivity, it) }
_binding = ActivityUploadBinding.inflate(layoutInflater) _binding = ActivityUploadBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(_binding!!.root, false)
setContentView(binding.root) setContentView(binding.root)
// Overrides the back button to make sure the user is prepared to lose their progress // Overrides the back button to make sure the user is prepared to lose their progress

View file

@ -10,6 +10,7 @@ import fr.free.nrw.commons.ViewPagerAdapter
import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.contributions.ContributionDao
import fr.free.nrw.commons.databinding.ActivityUploadProgressBinding import fr.free.nrw.commons.databinding.ActivityUploadProgressBinding
import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -35,6 +36,7 @@ class UploadProgressActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityUploadProgressBinding.inflate(layoutInflater) binding = ActivityUploadProgressBinding.inflate(layoutInflater)
applyEdgeToEdgeAllInsets(binding.root)
setContentView(binding.root) setContentView(binding.root)
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager) viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
binding.uploadProgressViewPager.setAdapter(viewPagerAdapter) binding.uploadProgressViewPager.setAdapter(viewPagerAdapter)

View file

@ -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.ImageUtils.getErrorMessageForResult
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
import fr.free.nrw.commons.utils.ViewUtil.showLongToast import fr.free.nrw.commons.utils.ViewUtil.showLongToast
import fr.free.nrw.commons.utils.handleKeyboardInsets
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.util.ArrayList import java.util.ArrayList
@ -153,6 +154,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View { ): View {
_binding = FragmentUploadMediaDetailFragmentBinding.inflate(inflater, container, false) _binding = FragmentUploadMediaDetailFragmentBinding.inflate(inflater, container, false)
_binding!!.mediaDetailCardView.handleKeyboardInsets()
return binding.root return binding.root
} }

View file

@ -12,9 +12,9 @@ object ConfigUtils {
val isBetaFlavour: Boolean = BuildConfig.FLAVOR == "beta" val isBetaFlavour: Boolean = BuildConfig.FLAVOR == "beta"
@JvmStatic @JvmStatic
private fun Context.getVersionName(): String = private fun Context.getVersionName(): String? =
try { try {
packageManager.getPackageInfo(packageName, 0).versionName packageManager.getPackageInfo(packageName, 0).versionName ?: BuildConfig.VERSION_NAME
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
BuildConfig.VERSION_NAME BuildConfig.VERSION_NAME
} }

View file

@ -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<MarginLayoutParams> {
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<WindowInsetsAnimationCompat>
): 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
}

View file

@ -24,6 +24,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import timber.log.Timber import timber.log.Timber
import androidx.core.graphics.createBitmap
/** /**
* Created by blueSir9 on 3/10/17. * Created by blueSir9 on 3/10/17.
@ -307,16 +308,19 @@ object ImageUtils {
* * @return * * @return
*/ */
@JvmStatic @JvmStatic
fun addRedBorder(bitmap: Bitmap, borderSize: Int, context: Context): Bitmap { fun addRedBorder(bitmap: Bitmap, borderSize: Int, context: Context): Bitmap? {
val bmpWithBorder = Bitmap.createBitmap( return bitmap.config?.let { config ->
bitmap.width + borderSize * 2, val bmpWithBorder =
bitmap.height + borderSize * 2, createBitmap(
bitmap.config width = bitmap.width + borderSize * 2,
) height = bitmap.height + borderSize * 2,
val canvas = Canvas(bmpWithBorder) config = config
canvas.drawColor(ContextCompat.getColor(context, R.color.deleteRed)) )
canvas.drawBitmap(bitmap, borderSize.toFloat(), borderSize.toFloat(), null) val canvas = Canvas(bmpWithBorder)
return bmpWithBorder canvas.drawColor(ContextCompat.getColor(context, R.color.deleteRed))
canvas.drawBitmap(bitmap, borderSize.toFloat(), borderSize.toFloat(), null)
return bmpWithBorder
}
} }
/** /**

View file

@ -35,6 +35,7 @@
android:scrollbars="vertical" android:scrollbars="vertical"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:scrollbarThumbVertical="@color/primaryColor" android:scrollbarThumbVertical="@color/primaryColor"
android:clipToPadding="false"
android:scrollbarSize="@dimen/dimen_6"/> android:scrollbarSize="@dimen/dimen_6"/>
</RelativeLayout> </RelativeLayout>

View file

@ -34,6 +34,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/switchWidget" app:layout_constraintTop_toBottomOf="@id/switchWidget"
android:clipToPadding="false"
/> />
<TextView <TextView

View file

@ -14,6 +14,7 @@
app:actualImageScaleType="fitXY" /> app:actualImageScaleType="fitXY" />
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/media_detail_card_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"

View file

@ -5,7 +5,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/location_picker_toolbar" android:id="@+id/location_picker_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="78dp" android:layout_height="wrap_content"
android:paddingVertical="@dimen/small_gap"
tools:background="@color/primaryColor"> tools:background="@color/primaryColor">
<TextView <TextView

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="initial_margin_top" type="id" />
<item name="initial_margin_bottom" type="id" />
<item name="initial_margin_left" type="id" />
<item name="initial_margin_right" type="id" />
</resources>

View file

@ -123,6 +123,9 @@
<item name="custom_selector_back">@drawable/ic_arrow_back_black</item> <item name="custom_selector_back">@drawable/ic_arrow_back_black</item>
<item name="android:windowEnableSplitTouch">false</item> <item name="android:windowEnableSplitTouch">false</item>
<item name="android:splitMotionEvents">false</item> <item name="android:splitMotionEvents">false</item>
<!--For edge to edge backward compatibility-->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style> </style>
<style name="LightMoreBottomSheetStyle" parent="LightAppTheme"> <style name="LightMoreBottomSheetStyle" parent="LightAppTheme">

View file

@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.9.1" agp = "8.12.0"
acra = "5.8.4" acra = "5.8.4"
activityCompose = "1.9.3" activityCompose = "1.9.3"
adapterdelegates = "4.3.0" adapterdelegates = "4.3.0"
@ -19,7 +19,7 @@ githubTripletPlay = "2.7.2"
kotlinxCoroutinesRx2 = "1.8.0" kotlinxCoroutinesRx2 = "1.8.0"
osmdroidAndroid = "6.1.17" osmdroidAndroid = "6.1.17"
testCore = "1.4.0" testCore = "1.4.0"
coreKtx = "1.9.0" coreKtx = "1.16.0"
coreTesting = "2.2.0" coreTesting = "2.2.0"
dagger = "2.23" dagger = "2.23"
databindingCompiler = "8.0.2" databindingCompiler = "8.0.2"
@ -57,7 +57,7 @@ preference = "1.2.1"
recyclerview = "1.2.0-alpha02" recyclerview = "1.2.0-alpha02"
recyclerviewFastscroll = "2.0.1" recyclerviewFastscroll = "2.0.1"
retrofit = "2.8.1" retrofit = "2.8.1"
robolectric = "4.11.1" robolectric = "4.14.1"
room = "2.6.1" room = "2.6.1"
rules = "1.5.0" rules = "1.5.0"
runner = "1.5.2" runner = "1.5.2"

View file

@ -1,6 +1,6 @@
#Sun Apr 23 18:22:54 IST 2023 #Sun Apr 23 18:22:54 IST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists