From 5a6b3cbf09ee3d3fe6d75a1a5e76dd0e380930b0 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 18 Aug 2025 14:02:18 +0200 Subject: [PATCH 01/22] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-el/strings.xml | 6 ++++-- app/src/main/res/values-gl/strings.xml | 2 +- app/src/main/res/values-mnw/strings.xml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index bb7a9ea98..a95b2f890 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -232,6 +232,7 @@ Περιγραφή Συζήτηση Συγγραφέας + Ανεβαστής Ημερομηνία μεταφόρτωσης Άδεια Συντεταγμένες @@ -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-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 75cb51542..1b7b19b3e 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 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 @@ ဗဒင်ဏအ် ဟွံကၠောန်ကမၠောန် ဗွဲဓမ္မတာ၊ ဒၞာဲဒတန် ဟွံသၟဟ်အစောမ်။ အာတ်မိက်ဒၟံင် အခေါင် သွက်ဂွံထ္ၜး စရင်ဒၞာဲဒတန် ဗဒင်ဗဒင် စမၞောန်ဂမၠိုင် - ဝဳကဳဒါတာ + ဝဳကဳတင်ဂၞင် ဝဳကဳပဳဒဳယာ ခမ်မောန် ကဵုင္ၚုဟ် ကုပိုယ် From a892aa6deee67651e8ff65a522175db2866f1864 Mon Sep 17 00:00:00 2001 From: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Date: Tue, 19 Aug 2025 21:16:02 +0530 Subject: [PATCH 02/22] 6357: Fix java.lang.SecurityException for multi-uploads (#6402) * Fix java.lang.SecurityException for ACTION_OPEN_DOCUMENT * Handle SecurityException in case of multi-upload * Remove unused import * Clean up code * Clean up code * Handle SecurityException for other upload methods * Release persisted URI permissions for successful uploads * Remove persistable permission for custom picker as it's not required * Remove persistable permission for in-app camera as it's not required --- .../free/nrw/commons/filepicker/FilePicker.kt | 18 ++++++++++++++---- .../nrw/commons/upload/worker/UploadWorker.kt | 6 ++++++ 2 files changed, 20 insertions(+), 4 deletions(-) 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/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) From b8a558303b67e5f627070b653135eb553edb90af Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 21 Aug 2025 14:02:13 +0200 Subject: [PATCH 03/22] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-gl/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-nqo/strings.xml | 1 + app/src/main/res/values-ps/strings.xml | 22 ++++++++ app/src/main/res/values-pt/strings.xml | 67 ++++++++++++++----------- 5 files changed, 62 insertions(+), 31 deletions(-) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 1b7b19b3e..8025813fc 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -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-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d297d07ac..8872daaa0 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -141,7 +141,7 @@ %1$s와(과) 일치하는 분류를 찾을 수 없습니다 %1$s에 대한 위키데이터 검색 결과가 없습니다 %1$s에 자식 클래스가 없습니다 - %1$s에 부모 클래스가 없습니다 + %1$s에 상위 클래스가 없습니다 위키미디어 공용에서 그림을 더 찾기 쉽게 만들기 위해 분류를 추가합니다.\n분류를 추가하려면 입력을 시작하세요. 분류 설정 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 bf757875d..f67fd0a18 100644 --- a/app/src/main/res/values-ps/strings.xml +++ b/app/src/main/res/values-ps/strings.xml @@ -302,14 +302,29 @@ انځورونه په څټ‌گرځول‌شوي نه دي کارول‌شوي انځورونه تېروتنه رامنځته شوه! + د ليکوال نوم دوديزول + ونډې + څېرمه + خبرتياوې + خبرتیاوې (لوستل‌شوې) + څېرمه خبرتياوې ښکاره‌کول + لړليک + زخيره کولو اجازه راتلونکی مخکنی انځورونه + ځايونه وېشنيزې په کتاب‌نښو کې ورگډول/لرې‌کول کتاب‌‌نښې تاسو هېڅ کتاب‌نښې نه دې ورگډې‌کړې کتاب‌نښې + په تېروتنې سره مې راپورته‌کړی دی + زه نه پوهېدم چې دا به ټولو ته ښکاره شي + زه پوه شوم چې دا زما د پټنتيا لپاره بد دی + زما اند توپير وکړ، زه نه غواړم چې دا نور په ټوليزه توگه ښکاره شي + په بښنې سره دا انځور د يو پوهنغونډ لپاره خواپورې نه دی + هېڅ وېشنيزې نه دې ټاکل شوې پای ته رسېږي په: ټاکنيزې‌سيالۍ ښکاره‌کول روانې ټاکنيزې‌سيالۍ وگورئ @@ -337,6 +352,8 @@ ايا دا د منلو وړ دي؟ ايا تاسو غواړئ له ونډه‌وال نه مننه وکړئ؟ که دا انځور ټولگټی نه وي؛ نو ړنگېدو ته د نوماندولو لپاره يې په نه کليک وکړئ. + بل انځور + هو، ولې نه ليکوال لمېسل‌رېښتې ځای @@ -348,7 +365,12 @@ انځور مالومات هېڅ وېشنيزې ونه موندل شوې نښان + نيونگ هم‌مهالول بریالیتوب + همغږيتوبونه %1$s ورگډ شول. + څرگنداوي ورگډل شول. + نيونگ ورگډ شو. + همغږيتوبونه نشي ورگډېدای. انځور وېشل په وسيله د تياره روښانه diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 659c2705a..c58323b30 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -44,29 +44,29 @@ Estado do Local Imagem do Dia - a carregar %1$d ficheiro - a carregar %1$d ficheiros + a enviar %1$d ficheiro + a enviar %1$d ficheiros (%1$d) (%1$d) - A iniciar carregamentos + A iniciar envios - A processar %d carregamento - A processar %d carregamentos + A processar %d envio + A processar %d envios - %d carregamento - %d carregamentos + %d envio + %d envios Esta imagem será licenciada com a %1$s Estas imagens serão licenciadas com a %1$s - %1$d carregamento - %1$d carregamentos + %1$d envio + %1$d envios A receber conteúdo partilhado. O processamento da imagem pode demorar algum tempo, dependendo do tamanho da mesma e do seu dispositivo @@ -83,8 +83,8 @@ Envio em progresso Nome de utilizador Palavra-passe - Entrar na sua conta da wiki Commons Beta - Entrar + Inicie a sessão na sua conta de Commons Beta + Iniciar sessão Esqueceu-se da palavra-passe? Registar-se A iniciar sessão @@ -99,27 +99,27 @@ O carregamento de mais de três imagens funciona de maneira mais fiável quando a otimização da bateria está desligada. Desligue a otimização da bateria para a aplicação Commons nas configurações, de forma a ter uma experiência de carregamento mais fluida. \n\nPossíveis passos para desativar a otimização da bateria:\n\nEtapa 1: premir o botão \'Configurações\' abaixo.\n\nEtapa 2: mudar de \'Não otimizado\' para \'Todas as aplicações\'.\n\nEtapa 3: pesquisar \"Commons\" ou \"fr.free.nrw.commons\".\n\nEtapa 4: premir esta e selecionar \'Não otimizar\'.\n\nEtapa 5: pressionar \'Concluído\'. Falha na autenticação. Por favor faça login novamente. Carregamento iniciado! - Fila de carregamento (modo de ligação limitada ativado) - %1$s carregado! - Toque para ver o seu carregamento - A carregar o ficheiro %s - A carregar %1$s - A terminar o carregamento de %1$s - O carregamento de %1$s falhou - Carregamento de %1$s em pausa + Envio em fila (modo de ligação limitada ativado) + %1$s enviado! + Toque para ver o seu envio + A enviar o ficheiro %s + A enviar %1$s + A terminar o envio de %1$s + O envio de %1$s falhou + Envio de %1$s pausado Tocar para ver Tocar para ver - Carregamentos recentes - Em espera - Falhado - %1$d%% transferido - A carregar + Meus Envios Recentes + Em fila + Falhou + %1$d%% concluído + A enviar Da galeria - Tirar foto + Tirar fotografia Nas redondezas - Carregamentos - Copiar ligação - A ligação foi copiada para a área de transferência + Meus envios + Copiar hiperligação + A hiperligação foi copiada para a área de transferência Partilhar Ver página do ficheiro Legenda (obrigatória) @@ -132,13 +132,14 @@ Tem de fornecer o seu código de autenticação de dois fatores. Foi enviado um código de verificação de autenticação para o seu endereço de correio eletrónico. Por favor, forneça o código para iniciar a sessão. O início de sessão falhou - Carregar + Enviar Dê um nome a este conjunto Modificações Carregar Pesquisar categorias Procurar elementos que o seu conteúdo multimédia retrata (montanha, o Taj Mahal, etc.) Gravar + Menu de fluxo Atualizar Lista (Ainda não foi carregado nenhum ficheiro) @@ -148,7 +149,7 @@ %1$s não tem nenhuma classe progenitora Adicione categorias para tornar as suas imagens mais fáceis de encontrar na wiki Wikimedia Commons.\nComece a escrever para adicionar categorias. Categorias - Configurações + Definições Registar-se Imagens destacadas Seletor personalizado @@ -784,17 +785,21 @@ Ficheiro guardado com sucesso Deseja abrir o ficheiro GPX? Deseja abrir o ficheiro KML? + Não foi possível guardar o ficheiro KML. + Não foi possível guardar o ficheiro GPX. Guardar Ficheiro KML Guardar Ficheiro GPX %d imagem selecionada %d imagens selecionadas + Nota sobre múltiplos envios Reporte um problema sobre este item na Wikidados Por favor, insira alguns comentários Discussão Outro problema ou informação (por favor, explique em baixo). O seu comentário é publicado na seguinte página da wiki: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile app/Feedback</a> + A cancelar todos os envios... Envios Pendente Falhou @@ -803,6 +808,8 @@ Confirmar Eliminação Eliminar Cancelar + Erro ao carregar + Não foram encontradas utilizações Commons Outras wikis Utilização de ficheiro From 718c466505317e14b8d03301e35d590ddd83b4f3 Mon Sep 17 00:00:00 2001 From: Rohit Verma <101377978+rohit9625@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:27:37 +0530 Subject: [PATCH 04/22] Bump target sdk to API 35 and make the app UI compatible with edge to edge (#6393) * chore: upgrade target SDK and refactor function signatures to resolve build issues * chore: bump android gradle plugin version * chore(ui): add extension functions for applying edge to edge insets * fix: apply system bar top and bottom insets for edge to edge * fix: force edge to edge for backward compatibility and consistent UI * fix: apply top bar insets as padding and make the status bar color white Since the toolbars have primary color as bg, we should make the status bar white * chore: bump robolectric version for API 35 compatibility * fix: preserve existing margins when adding new insets * feat(customselector): improve RecyclerView edge-to-edge inset handling It allows the last item to sits above the navigation bar while preserving edge-to-edge appearance. * feat(notification): improve RecyclerView edge-to-edge insets handling Also, refactor LocationPicker and DescriptionEdit activities to use extension functions and reduce duplication * fix(quiz): enable and handle edge-to-edge insets and status icon colors * fix: bottom insets not dispatched on all API versions consistently Upgraded core-ktx version installCompatInsetsDispatch wasn't available on current version * fix: return fallback value when versionName is null Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: resolve compilation errors * docs: add KDoc for edge-to-edge insets utility functions * fix(SearchActivity): apply insets for system bars * fix(util): add utility function to handle keyboard insets with animation * fix(upload): handle keyboard insets for upload media detail card view * fix(login): hadle IME insets and make edge-to-edge backward compatible --------- Co-authored-by: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- app/build.gradle.kts | 4 +- .../java/fr/free/nrw/commons/AboutActivity.kt | 2 + .../fr/free/nrw/commons/WelcomeActivity.kt | 2 + .../fr/free/nrw/commons/auth/LoginActivity.kt | 10 + .../free/nrw/commons/auth/SignupActivity.kt | 2 + .../category/CategoryDetailsActivity.kt | 2 + .../nrw/commons/contributions/MainActivity.kt | 2 + .../ui/selector/CustomSelectorActivity.kt | 6 + .../ui/selector/FolderFragment.kt | 2 + .../ui/selector/ImageFragment.kt | 2 + .../description/DescriptionEditActivity.kt | 7 + .../nrw/commons/explore/SearchActivity.kt | 2 + .../depictions/WikidataItemDetailsActivity.kt | 2 + .../locationpicker/LocationPickerActivity.kt | 7 + .../notification/NotificationActivity.kt | 6 + .../nrw/commons/profile/ProfileActivity.kt | 2 + .../fr/free/nrw/commons/quiz/QuizActivity.kt | 7 + .../nrw/commons/quiz/QuizResultActivity.kt | 7 + .../free/nrw/commons/review/ReviewActivity.kt | 2 + .../nrw/commons/settings/SettingsActivity.kt | 2 + .../fr/free/nrw/commons/theme/BaseActivity.kt | 2 + .../free/nrw/commons/upload/UploadActivity.kt | 2 + .../commons/upload/UploadProgressActivity.kt | 2 + .../mediaDetails/UploadMediaDetailFragment.kt | 2 + .../fr/free/nrw/commons/utils/ConfigUtils.kt | 4 +- .../free/nrw/commons/utils/EdgeToEdgeUtils.kt | 229 ++++++++++++++++++ .../fr/free/nrw/commons/utils/ImageUtils.kt | 24 +- .../main/res/layout/activity_notification.xml | 1 + .../res/layout/fragment_custom_selector.xml | 1 + .../fragment_upload_media_detail_fragment.xml | 1 + .../res/layout/toolbar_location_picker.xml | 3 +- app/src/main/res/values/ids.xml | 7 + app/src/main/res/values/styles.xml | 3 + gradle/libs.versions.toml | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 35 files changed, 348 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/EdgeToEdgeUtils.kt create mode 100644 app/src/main/res/values/ids.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 674a6473f..32f2ee415 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,12 +18,12 @@ if (isRunningOnTravisAndIsNotPRBuild) { android { namespace = "fr.free.nrw.commons" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "fr.free.nrw.commons" minSdk = 21 - targetSdk = 34 + targetSdk = 35 versionCode = 1055 versionName = "5.6.1" 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/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/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/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/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/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/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/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 43a9c3236..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 } 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" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 02c314a4a..cdc8ce387 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -123,6 +123,9 @@ @drawable/ic_arrow_back_black false false + + @android:color/transparent + @android:color/transparent