From 4befff8f422efe335a09d784b3d754b8d2617536 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 3 Jul 2025 14:02:08 +0200 Subject: [PATCH 1/3] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-az/strings.xml | 235 +++++++++++++++++++++++++ app/src/main/res/values-ia/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-mk/strings.xml | 3 +- app/src/main/res/values-sh/error.xml | 3 +- app/src/main/res/values-sh/strings.xml | 15 +- app/src/main/res/values-uk/strings.xml | 53 +++++- app/src/main/res/values-zh/strings.xml | 3 +- 8 files changed, 301 insertions(+), 14 deletions(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index c498f23f5..b5f736415 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -135,31 +135,266 @@ Siyahı (Hələ yükləmə yoxdur) %1$s axtarışına uyğun kateqoriya tapılmadı + %1$s uyğun heç bir Vikidata elementi tapılmadı + %1$s alt sinifləri yoxdur + %1$s üst sinfi yoxdur + Şəkillərinizi Vikianbarda daha tapıla bilən etmək üçün kateqoriyalar əlavə edin.\nKateqoriyalar əlavə etmək üçün yazmağa başlayın. Kateqoriyalar Nizamlamalar Qeydiyyatdan keç Seçilmiş şəkillər + Xüsusi Seçici Kateqoriya + Yoxlanış Haqqında + Vikianbar proqramı Vikimedia icmasının qrant alanları və könüllüləri tərəfindən yaradılmış və dəstəklənən açıq mənbəli proqramdır. Vikimedia Fondu proqramın yaradılması, inkişafı və ya texniki xidmətində iştirak etmir. + Xəta hesabatları və təkliflər üçün yeni <a href=\"%1$s\">GitHub sorğusu</a> yaradın. Məxfilik siyasəti + Töhfə verənlər Haqqında + Rəy göndər (e-poçt vasitəsilə) + Heç bir e-poçt tətbiqi quraşdırılmayıb + Son istifadə olunan kateqoriyalar + İlk sinxronizasiya gözlənilir… + Siz hələ heç bir şəkil yükləməmisiniz. Yenidən cəhd edin + İmtina + Bu şəkli yükləməklə bildirirəm ki, bu mənim şəxsi işimdir, onda müəllif hüququ ilə qorunan material və ya selfilər yoxdur və <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Vikianbar qaydalarına</a> riayət edir. Endir + Defolt lisenziya + Əvvəlki başlıq və təsvirdən istifadə et + Tema CC BY 3.0 + Vikianbar Vikipediyada istifadə olunan şəkillərin əksəriyyətinə ev sahibliyi edir. + Şəkilləriniz bütün dünyada insanları maarifləndirməyə kömək edir! + Tamamilə özünüz tərəfindən çəkilmiş və ya yaradılmış şəkilləri yükləyin: + Təbii obyektlər (çiçəklər, heyvanlar, dağlar) + Faydalı obyektlər (velosipedlər, qatar stansiyaları) + Məşhur insanlar (icra başçınız, tanış olduğunuz olimpiya idmançıları) + Zəhmət olmasa, YÜKLƏMƏYİN: + Selfi və ya dostlarınızın şəkilləri + İnternetdə tapdığınız şəkillər + Özəl proqramların ekran görüntüləri + Yükləmə nümunəsi: + Başlıq: Sidney Opera Evi + Təsvir: Sidney Opera Evinin körfəzdən görünüşü + Kateqoriyalar: Sidney Opera Evi Qərb istiqamətindən, Sidney Opera Evinin uzaqdan mənzərələri + Şəkilləriniz ilə töhfə verin. Vikipediya məqalələrinin canlanmasına kömək edin! + Vikipediyadakı şəkillər Vikianbardan götürülüb. + Şəkilləriniz bütün dünyada insanları maarifləndirməyə kömək edir. + İnternetdən tapdığınız müəllif hüquqları ilə qorunan materialları, eləcə də afişaların, kitab üzlüklərinin və s. şəkillərini yükləməkdən çəkinin. + Sizcə bunu başa düşdünüz? + Bəli! Əlavə məlumat Kateqoriyalar Yüklənir... Heç biri seçilməmişdir + Başlıq yoxdur + Təsvir yoxdur + Müzakirə yoxdur Naməlum lisenziya Yenilə + Yaddaşa giriş icazəsi tələb olunur + Tələb olunan icazə: Xarici yaddaşı oxumaq. Bu olmadan proqram qalereyanıza daxil ola bilməz. + Tələb olunan icazə: Xarici yaddaşa yazmaq. Bu olmadan proqram kameranıza/qalereyanıza daxil ola bilməz. + Məkan icazəsi tələb olunur + Tətbiqdaxili çəkilişlər üçün məkanı qeyd et + Cihaz kamerası onu qeyd etmədiyi halda, məkanı tətbiqdaxili çəkilişlərlə əlavə etmək üçün bunu aktiv edin + Oldu + Xəbərdarlıq + Dublikat fayl adı tapıldı + Yüklə Bəli Xeyr + Başlıq Başlıq + Təsvirlər + Təsvir Müzakirə Müəllif + Yüklənmə tarixi Lisenziya + Koordinatlar + Heç biri göstərilməyib + Beta Tester ol + Google Play-də beta kanalımıza qoşulun və yeni funksiyalara və xəta həllərinə erkən giriş əldə edin + 2FA kodu + E-poçt doğrulama kodu + Həqiqətən çıxış etmək istəyirsiniz? + Media şəkli uğursuz oldu + Heç bir alt kateqoriya tapılmadı + Heç bir üst kateqoriya tapılmadı + Zao dağı + Lamalar + Göy qurşağı körpüsü + Lalə + Vikipediyaya xoş gəlmisiniz + Müəlliflik hüquqlarına xoş gəlmisiniz + Sidney Opera Evi + İmtina + + Bağla + Ana səhifə + Yüklə + Yaxınlıqdakılar + Haqqında + Parametrlər + Rəy + GitHub vasitəsilə rəy + Çıxış + Təlimat + Bildirişlər + Yoxla + təsvir tapılmadı + Commons fayl səhifəsi + Vikidata elementi + Vikipediya məqaləsi + Zəhmət olmasa, medianı mümkün qədər təsvir edin: Harada çəkilib? Nəyi təsvir edir? Kontekst nədir? Obyektləri və ya şəxsləri təsvir edin. Asanlıqla təxmin edilə bilməyən məlumatları, məsələn, bir mənzərədirsə, günün saatını qeyd edin. Əgər media qeyri-adi bir şey göstərirsə, zəhmət olmasa, onu izah edin. + Zəhmət olmasa şəklin qısa təsvirini yazın. İlk başlıq şəkil üçün Başlıq kimi istifadə olunacaq. 255 simvolla məhdudlaşır. + Bu şəkil ilə bağlı potensial problemlər: + Şəkil çox qaranlıqdır. + Şəkil bulanıqdır. + Şəkil artıq Vikianbarda var. + Bu şəkil başqa yerdə çəkilib. + Zəhmət olmasa, yalnız özünüz çəkdiyiniz şəkilləri yükləyin. Başqalarının Facebook hesablarında tapdığınız şəkilləri yükləməyin. + Hələ də bu şəkli yükləmək istəyirsiniz? + Bağlantı xətası + Yükləmə prosesi aktiv internetə çıxış tələb edir. Şəbəkə bağlantınızı yoxlayın. + Şəkildə tapılan problemlər + Zəhmət olmasa, yalnız özünüz çəkdiyiniz şəkilləri yükləyin. İnternetdən tapdığınız şəkilləri yükləməyin. + Tətbiqdaxili kadrları yadda saxla + Tətbiqdaxili kamera ilə çəkilmiş şəkilləri cihazın yaddaşında saxla + Hesabınıza daxil olun + Jurnal faylını göndərin + Proqramla bağlı problemlərin aradan qaldırılmasına kömək etmək üçün jurnal faylını tərtibatçılara e-poçt vasitəsilə göndərin. Qeyd: jurnallar potensial olaraq şəxsi məlumatları ehtiva edə bilər + URL-i açmaq üçün heç bir veb brauzer tapılmadı + Xəta! URL tapılmadı + Silinməyə namizəd göstər + Bu şəkil silinməyə namizəddir. + Ətraflı məlumat üçün veb səhifəsinə bax + Ötür + Daxil ol + Daxil olma mərhələsini keçmək istədiyinizə əminsiniz? + Gələcəkdə şəkilləri yükləmək üçün daxil olmalısınız. + Bu funksiyadan istifadə etmək üçün zəhmət olmasa daxil olun + Vikimətni mübadilə buferinə köçür + Vikimətn mübadilə buferinə köçürüldü + Yaxınlıqdakılar düzgün işləməyə bilər, məkan əlçatan deyil. + İnternet əlçatan deyil. Yalnız keşlənmiş yerlər göstərilir. + Məkan girişi rədd edildi. Bu funksiyadan istifadə etmək üçün yerinizi manual təyin edin. + Yaxınlıqdakı yerlərin siyahısını göstərmək üçün icazə tələb olunur + Yaxınlıqdakı şəkillərin siyahısını göstərmək üçün icazə tələb olunur + İstiqamətlər Vikidata Vikipediya + Vikianbar + Bizi qiymətləndir + TSS + İstifadəçi təlimatı + Təlimatı ötür + İnternet əlçatan deyil + Bildirişləri əldə edərkən xəta baş verdi + Şəkli nəzərdən keçirmək üçün gətirilərkən xəta baş verdi. Yenidən cəhd etmək üçün yeniləmə düyməsini basın. + Heç bir bildiriş tapılmadı + Tərcümə et + Dillər + Tərcümələri təqdim etmək istədiyiniz dili seçin + Davam et + İmtina + Yenidən cəhd et + Bunlar sizə yaxın yerlərdir və onların Vikipediya məqalələrini təsvir etmək üçün şəkillərə ehtiyac duyurlar.\n\n\'BU SAHƏDƏ AXTAR\' düyməsinə klikləməklə, xəritə kilidləyib həmin məkan ətrafında yaxınlıqda axtarış başlaya bilərsiniz. + Bu yerə şəkil lazımdır. + Bu yerin artıq şəkli var. + Bu yer artıq mövcud deyil. + Şəkil tapılmadı! + Şəkillər yüklənərkən xəta baş verdi. + %1$s tərəfindən yüklənilib + Bloklanıb + Sizə Vikianbarı redaktə etmək qadağan edilib + Günün Şəkli + Axtar + Vikianbarda axtar + Axtar + Son axtarışlar: + Bu yaxınlarda axtarılanlar + Son dil sorğuları + Kateqoriyalar yüklənərkən xəta baş verdi. + Təsvirləri yükləyərkən xəta baş verdi. + Media + Kateqoriyalar + Elementlər + Seçilmiş + Mobil tətbiq vasitəsilə yüklənib + Xəritə + Şəkil Vikidatada %1$s elementinə əlavə edildi + Müvafiq Vikidata obyektini yeniləmək alınmadı! + Divar kağızı kimi təyin et + Divar kağızı uğurla təyin edildi! + Sorğu + Bu şəkli yükləmək olar? + Sual + Nəticə + Silinməyi tələb edilən şəkilləri yükləməyə davam etsəniz, hesabınız blok olunacaq. Sorğunu bitirmək istədiyinizə əminsiniz? + Yüklədiyiniz şəkillərin %1$s+ ədədi silinib. Silinməyi tələb edilən şəkilləri yükləməyə davam etsəniz, hesabınız blok olunacaq.\n\nTəlimata yenidən baxmaq və sonra hansı növ şəkilləri yükləməli və ya yükləməməli olduğunuzu öyrənməyə kömək etmək üçün testdən keçmək istəyirsiniz? + Selfielərin o qədər də məlumatlandırıcı dəyəri yoxdur. Haqqınızda Vikipediya məqaləsi yoxdursa, zəhmət olmasa, şəklinizi yükləməyin. + Abidələrin və mənzərələrin şəkillərini əksər ölkələrdə yükləmək olar. Nəzərə alın ki, xaricdəki müvəqqəti incəsənət abidələri tez-tez müəllif hüquqları ilə qorunur və yükləmək düzgün deyil. + Veb saytların skrinşotları törəmə əsərlər hesab edilir və veb-saytdakı müəllif hüququna tabedir. Bunlar müəllifin icazəsindən sonra istifadə edilə bilər. Belə icazə olmadan, onların işi əsasında yaratdığınız hər hansı əsər qanuni olaraq orijinal müəllifə məxsus lisenziyasız nüsxə sayılır. + Vikianbarın məqsədlərindən biri keyfiyyətli şəkillər toplamaqdır. Ona görə də bulanıq şəkillər yüklənməməlidir. Həmişə yaxşı işıqlandırma ilə gözəl şəkillər çəkməyə çalışın. + Texnologiya və ya mədəniyyəti təsvir edən şəkillər üçün Vikianbarda həmişə yer var. + Cavabların %1$s ədədi düzgündür. Təbriklər! + Suala cavab vermək üçün iki variantdan birini seçin + Giriş müddəti bitib. Zəhmət olmasa, yenidən daxil olun. + Bunu dostlarınızla paylaşın! + Davam et + Düzgün Cavab + Yanlış Cavab + Bu skrinşotu yükləmək olar? + Proqramı paylaş + Fırlat + Yaxınlıqdakı yerləri yükləmək mümkün olmadı + Bu ərazidə şəkillər yoxdur + Yaxınlıqda yer tapılmadı + Yaxınlıqdakı abidələri əldə edərkən xəta baş verdi. + Son axtarışlar yoxdur + Axtarış tarixçəsini silmək istədiyindən əminsiniz? + Bu yükləməni ləğv etmək istədiyinizə əminsiniz? + Bu axtarışı silmək istəyirsiniz? + Axtarış tarixçəsi silindi + Silinməyə namizəd göstər + Sil + Nailiyyətlər + Profil + Rozetlər + Statistika + Qəbul edilən təşəkkürlər + Seçilmiş şəkillər + \"Yaxınlıqdakı yerlər\" vasitəsilə şəkillər + Səviyyə %d + %s (Səviyyə %s) + Yüklənən şəkillər + Geri qaytarılan şəkillər + İstifadə olunan şəkillər + Nailiyyətlərinizi dostlarınızla paylaşın! + Bu tələbləri yerinə yetirdikcə səviyyəniz yüksəlir. \"Statistika\" bölməsindəki elementlər sizin səviyyənizi nəzərə almır. + minimum tələb olunur: + İstənilən yükləmə proqramı vasitəsilə Vikianbara yüklədiyiniz şəkillərin sayı + Vikianbara yüklədiyiniz və silinməyən şəkillərin faizi + Vikianbara yüklədiyiniz və Vikimedia məqalələrində istifadə olunan şəkillərin sayı + Xəta baş verdi! + Vikianbar bildirişi + Fərdi müəllif adından istifadə et + Fotoşəkilləri yükləyərkən istifadəçi adınız əvəzinə fərdi müəllif adından istifadə edin + Fərdi müəllif adı + Töhfələr + Yaxınlıqdakılar + Bildirişlər + Bildirişlər (oxunmuş) + Yaxınlıqdakı bildirişini göstər + Şəkillərə ehtiyacı olan ən yaxın yerlər üçün tətbiqdaxili bildiriş göstərin + Siyahı + Yaddaş icazəsi + Şəkilləri yükləmək üçün cihazınızın xarici yaddaşına giriş icazəsi lazımdır. + Artıq şəkillərə ehtiyacı olan ən yaxın yeri görməyəcəksiniz. Bununla belə, istəyirsinizsə, bu bildirişi Parametrlərdə yenidən aktivləşdirə bilərsiniz. Təşəkkür uğurla göndərildi Təşəkkür göndərilə bilmədi %1$s Təşəkkür göndərilir: Xəta diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml index 452f4f9b6..9e169f8f6 100644 --- a/app/src/main/res/values-ia/strings.xml +++ b/app/src/main/res/values-ia/strings.xml @@ -816,4 +816,5 @@ Monstrar in A proximitate Create e incargate per: %1$s Create per %1$s e incargate per %2$s + Nominate pro deletion diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 9ca05c344..ce121ab5d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -772,7 +772,7 @@ Impara come scrivere una didascalia utile Vedi i tuoi risultati Modifica Immagine - Modifica Posizione + Modifica posizione Posizione aggiornata! Rimuovi posizione Rimuovi avviso di posizione diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index a0d1dd6ef..6a06f04e7 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -413,7 +413,7 @@ Сфатив дека ќе ми ја наруши приватноста Се премислив. Не сакам повеќе да биде видлива За жал, сликава не е соодветна за енциклопедија - Подигнато од мене %1$s, кое се користи во %2$d статии. + Подигнато од мене на %1$s, и се користи во барем {{PLURAL:%2$d|one=една статија|%2$d статии}}. Добре дојдовте на Ризницата!\n\nПодигнете ја вашата прва слика или снимка допирајќи го копчето за додавање. Нема избрано категории Некатегоризираните слики се слабо употребливи. Дали сигурно сакате да продолжите без да ставите категории? @@ -825,4 +825,5 @@ Прикажи во „Во близина“ Создал: %1$s Создал %1$s, а подигнал %2$s + Предложено за бришење diff --git a/app/src/main/res/values-sh/error.xml b/app/src/main/res/values-sh/error.xml index ea9b8e670..28d2bbe0a 100644 --- a/app/src/main/res/values-sh/error.xml +++ b/app/src/main/res/values-sh/error.xml @@ -1,9 +1,10 @@ - Commons se srušio + Aplikacija Ostava prestala je raditi Ups. Nešto nije u redu! Recite nam šta radite, pa podijelite s nama putem e-pošte. Pomoći će nam da ih popravimo! Hvala vam! diff --git a/app/src/main/res/values-sh/strings.xml b/app/src/main/res/values-sh/strings.xml index 5997677ef..97271b945 100644 --- a/app/src/main/res/values-sh/strings.xml +++ b/app/src/main/res/values-sh/strings.xml @@ -1,5 +1,6 @@ @@ -93,12 +94,12 @@ Pretraži kategorije Snimi Preučitaj - Popis + Lista (Još uvijek nema postavljenih datoteka) Nema kategorija što odgovoraju %1$s Stavite kategorije slikama kako biste olakšali korisnicima njihovo pronalaženje na Ostavi.\n\nDa biste stavili kategoriju, počnite sa pisanjem njenog imena. Kategorije - Podešavanja + Postavke Registracija Izabrane slike Kategorija @@ -125,7 +126,7 @@ Autorstvo 4.0 Autorstvo-Dijeliti pod istim uslovima 3.0 Autorstvo 3.0 - Ostava je udomljač najvećine slika što se koriste na Wikipediji. + Wikimedijina ostava skladišti većinu slika koje se upotrebljavaju na Wikipediji. Vaše slike pomažu u obrazovanju ljudi širom svijeta! Postavljajte slike koje su u potpunosti Vaše djelo: Objekti iz prirode (cvijeće, životinje, planine) @@ -188,7 +189,7 @@ Postavi U blizini O privitku - Podešavanja + Postavke Povratna informacija Odjava Uputstva @@ -214,7 +215,7 @@ U budućnosti ćete se morati prijaviti kako biste postavili slike. Prijavite se da biste koristili ovu funkciju „U blizini“ možda ne radi kako treba. Lokacija nije dostupna. - Potrebna je dozvola za popis liste lokacija u blizini + Potrebna je dozvola za prikaz liste lokacija u blizini Upute Wikidata Wikipedia @@ -226,7 +227,7 @@ Nema obavijesti Prevedi Jezici - Odaberite za koji jezik bi želeli da pravite prijevode + Izaberite jezik na koji biste željeli prevoditi Nastavi Otkaži Pokušaj ponovo @@ -261,6 +262,6 @@ Poništi Podaci o lokaciji pomažu Wikiurednicima da pronađu vašu sliku, čineći je mnogo korisnijom.\nVaše nedavne postavljene slike nemaju lokaciju.\nPredlažemo da uključite lokaciju u postavkama priloga kamere.\nHvala vam na učitavanju! Detalji - Razina priložnika + Nivo API-ja Android verzija diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index f005f2969..8eeceec22 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -132,6 +132,8 @@ Оберіть фото Поблизу Мої завантаження + Скопіювати посилання + Посилання скопійовано в буфер обміну Поширити Переглянути сторінку файлу Підпис (обов\'язково) @@ -142,6 +144,7 @@ Надто багато невдалих спроб. Будь ласка, спробуйте знову через кілька хвилин. Вибачте, цього користувача було заблоковано на Вікісховищі Ви повинні надати код двофакторної автентифікації. + Код підтвердження входу надіслано на вашу адресу електронної пошти. Будь ласка, введіть код для входу. Не вдалося увійти Завантажити Назвіть цю серію @@ -150,6 +153,7 @@ Пошук категорій Пошук об\'єктів, що зображено у цьому медіа (напр. гори, Тадж-Махал, тощо) Зберегти + Додаткове меню Оновити Список (Ще нема завантажень) @@ -246,6 +250,7 @@ Станьте бета-тестером Підпишіться на наш бета-канал на Google Play і отримайте ранній доступ до нових функцій та виправлень баґів Код 2FA + Код підтвердження електронної пошти Ви справді хочете вийти із системи? Помилка медіазображення Підкатегорій не знайдено @@ -306,6 +311,7 @@ Скопіювати вікі-текст у буфер обміну Вікі-текст скопійовано у буфер обміну Функція «Поблизу» може працювати некоректно, «Розташування» недоступне. + Інтернет недоступний. Показуються лише кешовані місця. Доступ до місцезнаходження заборонено. Щоб скористатися цією функцією, будь ласка, вкажіть своє місцезнаходження вручну. Потрібний дозвіл для показу списку місць поблизу Потрібний дозвіл для показу зображень місць поблизу @@ -389,11 +395,13 @@ Вилучити Досягнення Профіль + Значки Статистика Отримані подяки Вибрані зображення Зображення місць поблизу - Рівень + Рівень %d + %s (Рівень %s) Завантажені зображення Не відхилені зображення Використані зображення @@ -425,6 +433,7 @@ На Вашому пристрої не знайдено сумісного додатка з картами. Будь ласка, встановіть додаток з картами, якщо хочете скористатись цією функцією. Зображення Місця + Категорії Додати/вилучити закладки Закладки Ви не додали жодної закладки @@ -435,7 +444,7 @@ Мені стало зрозуміло, що це шкодить моїй приватності Моя думка змінилась, я не хочу, щоб це було доступно публічно Перепрошую, це зображення нецікаве для енциклопедії - Завантажено мною на сайт «%1$s» та використано у {{PLURAL:%2$d|one=одній статті|%2$d статтях}}. + Завантажено мною на сайт «%1$s» та використано у принаймні {{PLURAL:%2$d|one=одній статті|%2$d статтях}}. Ласкаво просимо до Вікісховища!\n\nЗавантажте Ваш перший медіафайл, натиснувши кнопку додавання. Жодної категорії не вибрано Зображення без категорій рідко використовуються. Ви впевнені, що хочете продовжити без вказаних категорій? @@ -506,6 +515,7 @@ У вас немає непрочитаних сповіщень Немає прочитаних сповіщень Поширення журналів + Перевірте свою скриньку електронної пошти Перегляд прочитаних Перегляд непрочитаних Сталася помилка при завантаженні зображень @@ -725,7 +735,7 @@ Для належної роботи мапи поблизу мають відображати стан PHONE STATE Внесок користувача: %s Досягнення користувача: %s - Переглянути сторінку користувача + Переглянути профіль користувача Редагувати описи Редагувати категорії Розширені параметри @@ -810,8 +820,45 @@ Повідомити у Вікідані про проблему з цим елементом Будь ласка, додайте якийсь коментар Обговорення + Напишіть щось про елемент «%1$s». Це буде видно публічно. «%1$s» більше не існує, фотографій цього більше не можливо зробити. + «%1$s» розташовано в іншому місці. «%1$s» — це інше місце. Будь ласка, вкажіть правильне місце нижче і, якщо можна, напишіть правильні широту і довготу. Інша проблема або інформація (поясніть нижче). Ваші відгуки розміщуються на ось цій вікісторінці: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile app/Feedback</a> + Ви впевнені, що бажаєте скасувати усі вивантаження? + Скасування усіх вивантажень… + Вивантаження + В очікуванні + Не вдалося + Не вдалося завантажити дані про місце + Вилучити папку + Підтвердити вилучення + Ви впевнені, що бажаєте вилучити папку %1$s, у якій міститься %2$d елемент(ів)? + Вилучити + Скасувати + Папку %1$s успішно вилучено + Не вдалося вилучити папку %1$s + Помилка при вилученні вмісту папки: %1$s + Не вдалося отримати шлях до папки для ID контейнера: %1$d + У цього місця ще немає зображень, сфотографуйте його! + Це місце уже має зображення. + Перевіряємо, чи це місце має зображення. + Помилка при завантаженні + Не знайдено використань + Вікісховище + Інші вікі + Використання файлу + Обліковий запис + Знищити обліковий запис + Попередження про знищення облікового запису + Зникнення є <b>останнім заходом</b>, і <b>його слід використовувати лише тоді, коли ви хочете назавжди припинити редагування</b>, а також щоб приховати якомога більше своїх минулих зв\'язків.<br/><br/>Вилучення облікового запису у Вікісховищі робиться шляхом зміни імені вашого облікового запису, щоб інші не могли розпізнати ваші внески; це називають зникненням облікового запису. <b>Зникнення не гарантує повної анонімності і не вилучає внесок у проєкти</b>. + Підпис + Підпис скопійовано до буферу обміну + Вітаємо, всі зображення в цьому альбомі або завантажені, або позначені як не для завантаження. + Показати в розділі «Дослідити» + Показати в розділі «Поблизу» + Створено та завантажено: %1$s + Створено %1$s та завантажено %2$s + Номіновано на вилучення diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 452e54563..8244bdc30 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -470,7 +470,7 @@ 我意识到这对我的隐私不利 我改变了主意,我不想再让公众看到它了 对不起,这幅图对百科全书没什么意思 - 由我自己上传在%1$s,使用于%2$d个条目。 + 由我自己上传在%1$s,使用于至少%2$d个条目。 欢迎使用共享资源!\n\n通过点击添加按钮以上传您的首个媒体。 未提交分类 不带分类的图片很难有机会被利用到,您确定您要不选择分类来继续吗? @@ -880,4 +880,5 @@ 显示在附近 创建并上传者: %1$s 由%1$s创建并由%2$s上传 + 已提名删除 From 3bd0ec446610ab9df1ee17360a3840522382dd60 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Fri, 4 Jul 2025 06:18:52 -0500 Subject: [PATCH 2/3] Convert top level "Utils" class to kotlin (#6364) * Unused class removed * Convert BasePresenter to kotlin * Removed redundent class * Move the Utils class into the utils package * Inline the creation of a page title object * Move license utilities into their own file * Inline app rating since its only ever used in 1 place * Moved GeoCoordinates utilities into their own class * Moved Monuments related utils into their own class * Moved screen capture into its own util class * Moved handleWebUrl to its own utility class * Moved fixExtension to its own class * Moved clipboard copy into its own utility class * Renames class to match remaining utility method * Convert UnderlineUtils to kotlin * Converted the copy-to-clipboard utility to kotlin * Converted license name and url lookup to kotlin * Converted fixExtension to kotlin * Convert handleGeoCoordinates to kotlin * Monument utils converted to kotlin * Convert then inline screeen capture in kotlin * Convert handleWebUrl to kotlin --- .../java/fr/free/nrw/commons/AboutActivity.kt | 66 ++--- .../fr/free/nrw/commons/BasePresenter.java | 18 -- .../java/fr/free/nrw/commons/BasePresenter.kt | 10 + .../java/fr/free/nrw/commons/License.java | 79 ------ .../main/java/fr/free/nrw/commons/Media.kt | 5 +- .../java/fr/free/nrw/commons/MvpView.java | 8 - .../main/java/fr/free/nrw/commons/Utils.java | 264 ------------------ .../free/nrw/commons/WelcomePagerAdapter.java | 7 +- .../fr/free/nrw/commons/auth/LoginActivity.kt | 6 +- .../nrw/commons/campaigns/CampaignView.kt | 4 +- .../commons/campaigns/CampaignsPresenter.kt | 2 +- .../nrw/commons/campaigns/ICampaignsView.kt | 3 +- .../category/CategoryDetailsActivity.kt | 10 +- .../contributions/ContributionsContract.kt | 1 - .../contributions/ContributionsFragment.kt | 14 +- .../ContributionsListContract.kt | 2 +- .../ContributionsListFragment.kt | 8 +- .../WikidataItemDetailsActivity.java | 9 +- .../explore/map/ExploreMapFragment.java | 7 +- .../locationpicker/LocationPickerActivity.kt | 6 +- .../nrw/commons/media/MediaDetailFragment.kt | 19 +- .../media/MediaDetailPagerFragment.java | 6 +- .../fragments/CommonPlaceClickActions.kt | 7 +- .../nearby/fragments/NearbyParentFragment.kt | 31 +- .../notification/NotificationActivity.kt | 4 +- .../nrw/commons/profile/ProfileActivity.kt | 28 +- .../achievements/AchievementsFragment.kt | 4 +- .../nrw/commons/settings/SettingsFragment.kt | 10 +- .../nrw/commons/upload/PageContentsCreator.kt | 4 +- .../fr/free/nrw/commons/upload/UploadItem.kt | 4 +- .../upload/license/MediaLicenseFragment.kt | 16 +- .../upload/license/MediaLicensePresenter.kt | 10 +- .../UploadMediaDetailsContract.kt | 2 +- .../free/nrw/commons/utils/ClipboardUtils.kt | 20 ++ .../fr/free/nrw/commons/utils/FixExtension.kt | 38 +++ .../free/nrw/commons/utils/GeoCoordinates.kt | 27 ++ .../fr/free/nrw/commons/utils/Licenses.kt | 31 ++ .../fr/free/nrw/commons/utils/Monuments.kt | 39 +++ .../free/nrw/commons/utils/UnderlineUtils.kt | 19 ++ .../fr/free/nrw/commons/utils/UrlUtils.kt | 33 +++ .../kotlin/fr/free/nrw/commons/UtilsTest.kt | 7 +- .../ContributionsFragmentUnitTests.kt | 7 - .../upload/MediaLicensePresenterTest.kt | 10 +- .../commons/utils/UtilsFixExtensionTest.kt | 1 - 44 files changed, 387 insertions(+), 519 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/BasePresenter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/BasePresenter.kt delete mode 100644 app/src/main/java/fr/free/nrw/commons/License.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/MvpView.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/Utils.java create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt 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 143f5e569..ebbb4097a 100644 --- a/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt @@ -1,7 +1,9 @@ package fr.free.nrw.commons import android.annotation.SuppressLint +import android.content.ActivityNotFoundException import android.content.Intent +import android.content.Intent.ACTION_VIEW import android.net.Uri import android.os.Bundle import android.view.Menu @@ -16,6 +18,9 @@ import fr.free.nrw.commons.theme.BaseActivity 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.handleWebUrl +import fr.free.nrw.commons.utils.setUnderlinedText /** * Represents about screen of this app @@ -59,30 +64,12 @@ class AboutActivity : BaseActivity() { binding!!.aboutImprove.setHtmlText(improveText) binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha() - Utils.setUnderlinedText( - binding!!.aboutFaq, R.string.about_faq, - applicationContext - ) - Utils.setUnderlinedText( - binding!!.aboutRateUs, R.string.about_rate_us, - applicationContext - ) - Utils.setUnderlinedText( - binding!!.aboutUserGuide, R.string.user_guide, - applicationContext - ) - Utils.setUnderlinedText( - binding!!.aboutPrivacyPolicy, R.string.about_privacy_policy, - applicationContext - ) - Utils.setUnderlinedText( - binding!!.aboutTranslate, R.string.about_translate, - applicationContext - ) - Utils.setUnderlinedText( - binding!!.aboutCredits, R.string.about_credits, - applicationContext - ) + binding!!.aboutFaq.setUnderlinedText(R.string.about_faq) + binding!!.aboutRateUs.setUnderlinedText(R.string.about_rate_us) + binding!!.aboutUserGuide.setUnderlinedText(R.string.user_guide) + binding!!.aboutPrivacyPolicy.setUnderlinedText(R.string.about_privacy_policy) + binding!!.aboutTranslate.setUnderlinedText(R.string.about_translate) + binding!!.aboutCredits.setUnderlinedText(R.string.about_credits) /* To set listeners, we can create a separate method and use lambda syntax. @@ -106,47 +93,56 @@ class AboutActivity : BaseActivity() { fun launchFacebook(view: View?) { val intent: Intent try { - intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL)) + intent = Intent(ACTION_VIEW, Urls.FACEBOOK_APP_URL.toUri()) intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME) startActivity(intent) } catch (e: Exception) { - Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL)) + handleWebUrl(this, Urls.FACEBOOK_WEB_URL.toUri()) } } fun launchGithub(view: View?) { val intent: Intent try { - intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL)) + intent = Intent(ACTION_VIEW, Urls.GITHUB_REPO_URL.toUri()) intent.setPackage(Urls.GITHUB_PACKAGE_NAME) startActivity(intent) } catch (e: Exception) { - Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL)) + handleWebUrl(this, Urls.GITHUB_REPO_URL.toUri()) } } fun launchWebsite(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL)) + handleWebUrl(this, Urls.WEBSITE_URL.toUri()) } fun launchRatings(view: View?) { - Utils.rateApp(this) + try { + startActivity( + Intent( + ACTION_VIEW, + (Urls.PLAY_STORE_PREFIX + packageName).toUri() + ) + ) + } catch (_: ActivityNotFoundException) { + handleWebUrl(this, (Urls.PLAY_STORE_URL_PREFIX + packageName).toUri()) + } } fun launchCredits(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL)) + handleWebUrl(this, Urls.CREDITS_URL.toUri()) } fun launchUserGuide(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL)) + handleWebUrl(this, Urls.USER_GUIDE_URL.toUri()) } fun launchPrivacyPolicy(view: View?) { - Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) + handleWebUrl(this, BuildConfig.PRIVACY_POLICY_URL.toUri()) } fun launchFrequentlyAskedQuesions(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL)) + handleWebUrl(this, Urls.FAQ_URL.toUri()) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -193,7 +189,7 @@ class AboutActivity : BaseActivity() { val positiveButtonRunnable = Runnable { val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition] - Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode)) + handleWebUrl(this@AboutActivity, (Urls.TRANSLATE_WIKI_URL + langCode).toUri()) } showAlertDialog( this, diff --git a/app/src/main/java/fr/free/nrw/commons/BasePresenter.java b/app/src/main/java/fr/free/nrw/commons/BasePresenter.java deleted file mode 100644 index 2653b3711..000000000 --- a/app/src/main/java/fr/free/nrw/commons/BasePresenter.java +++ /dev/null @@ -1,18 +0,0 @@ -package fr.free.nrw.commons; - -import androidx.annotation.NonNull; - -/** - * Base presenter, enforcing contracts to atach and detach view - */ -public interface BasePresenter { - /** - * Until a view is attached, it is open to listen events from the presenter - */ - void onAttachView(@NonNull T view); - - /** - * Detaching a view makes sure that the view no more receives events from the presenter - */ - void onDetachView(); -} diff --git a/app/src/main/java/fr/free/nrw/commons/BasePresenter.kt b/app/src/main/java/fr/free/nrw/commons/BasePresenter.kt new file mode 100644 index 000000000..085307c3e --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/BasePresenter.kt @@ -0,0 +1,10 @@ +package fr.free.nrw.commons + +/** + * Base presenter, enforcing contracts to attach and detach view + */ +interface BasePresenter { + fun onAttachView(view: T) + + fun onDetachView() +} diff --git a/app/src/main/java/fr/free/nrw/commons/License.java b/app/src/main/java/fr/free/nrw/commons/License.java deleted file mode 100644 index 1fea236ee..000000000 --- a/app/src/main/java/fr/free/nrw/commons/License.java +++ /dev/null @@ -1,79 +0,0 @@ -package fr.free.nrw.commons; - -import androidx.annotation.Nullable; - -/** - * represents Licence object - */ -public class License { - private String key; - private String template; - private String url; - private String name; - - /** - * Constructs a new instance of License. - * - * @param key license key - * @param template license template - * @param url license URL - * @param name licence name - * - * @throws RuntimeException if License.key or Licence.template is null - */ - public License(String key, String template, String url, String name) { - if (key == null) { - throw new RuntimeException("License.key must not be null"); - } - if (template == null) { - throw new RuntimeException("License.template must not be null"); - } - this.key = key; - this.template = template; - this.url = url; - this.name = name; - } - - /** - * Gets the license key. - * @return license key as a String. - */ - public String getKey() { - return key; - } - - /** - * Gets the license template. - * @return license template as a String. - */ - public String getTemplate() { - return template; - } - - /** - * Gets the license name. If name is null, return license key. - * @return license name as string. if name null, license key as String - */ - public String getName() { - if (name == null) { - // hack - return getKey(); - } else { - return name; - } - } - - /** - * Gets the license URL - * - * @param language license language - * @return URL - */ - public @Nullable String getUrl(String language) { - if (url == null) { - return null; - } else { - return url.replace("$lang", language); - } - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index 7bd8e95fd..dbe722e91 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -1,7 +1,9 @@ package fr.free.nrw.commons import android.os.Parcelable +import fr.free.nrw.commons.BuildConfig.COMMONS_URL import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.wikidata.model.WikiSite import fr.free.nrw.commons.wikidata.model.page.PageTitle import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @@ -173,7 +175,8 @@ class Media constructor( * Gets file page title * @return New media page title */ - val pageTitle: PageTitle get() = Utils.getPageTitle(filename!!) + val pageTitle: PageTitle + get() = PageTitle(filename!!, WikiSite(COMMONS_URL)) /** * Returns wikicode to use the media file on a MediaWiki site diff --git a/app/src/main/java/fr/free/nrw/commons/MvpView.java b/app/src/main/java/fr/free/nrw/commons/MvpView.java deleted file mode 100644 index 7485b2aaf..000000000 --- a/app/src/main/java/fr/free/nrw/commons/MvpView.java +++ /dev/null @@ -1,8 +0,0 @@ -package fr.free.nrw.commons; - -/** - * Base interface for all the views - */ -public interface MvpView { - void showMessage(String message); -} diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java deleted file mode 100644 index 8d0f8b530..000000000 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ /dev/null @@ -1,264 +0,0 @@ -package fr.free.nrw.commons; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.text.SpannableString; -import android.text.style.UnderlineSpan; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.browser.customtabs.CustomTabColorSchemeParams; -import androidx.browser.customtabs.CustomTabsIntent; -import androidx.core.content.ContextCompat; - -import java.util.Calendar; -import java.util.Date; -import fr.free.nrw.commons.wikidata.model.WikiSite; -import fr.free.nrw.commons.wikidata.model.page.PageTitle; - -import java.util.Locale; -import java.util.regex.Pattern; - -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.settings.Prefs; -import fr.free.nrw.commons.utils.ViewUtil; -import timber.log.Timber; - -public class Utils { - - public static PageTitle getPageTitle(@NonNull String title) { - return new PageTitle(title, new WikiSite(BuildConfig.COMMONS_URL)); - } - - /** - * Generates licence name with given ID - * @param license License ID - * @return Name of license - */ - public static int licenseNameFor(String license) { - switch (license) { - case Prefs.Licenses.CC_BY_3: - return R.string.license_name_cc_by; - case Prefs.Licenses.CC_BY_4: - return R.string.license_name_cc_by_four; - case Prefs.Licenses.CC_BY_SA_3: - return R.string.license_name_cc_by_sa; - case Prefs.Licenses.CC_BY_SA_4: - return R.string.license_name_cc_by_sa_four; - case Prefs.Licenses.CC0: - return R.string.license_name_cc0; - } - throw new IllegalStateException("Unrecognized license value: " + license); - } - - /** - * Generates license url with given ID - * @param license License ID - * @return Url of license - */ - - - @NonNull - public static String licenseUrlFor(String license) { - switch (license) { - case Prefs.Licenses.CC_BY_3: - return "https://creativecommons.org/licenses/by/3.0/"; - case Prefs.Licenses.CC_BY_4: - return "https://creativecommons.org/licenses/by/4.0/"; - case Prefs.Licenses.CC_BY_SA_3: - return "https://creativecommons.org/licenses/by-sa/3.0/"; - case Prefs.Licenses.CC_BY_SA_4: - return "https://creativecommons.org/licenses/by-sa/4.0/"; - case Prefs.Licenses.CC0: - return "https://creativecommons.org/publicdomain/zero/1.0/"; - default: - throw new IllegalStateException("Unrecognized license value: " + license); - } - } - - /** - * Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected - * @param title File name - * @param extension Correct extension - * @return File with correct extension - */ - public static String fixExtension(String title, String extension) { - Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE); - - // People are used to ".jpg" more than ".jpeg" which the system gives us. - if (extension != null && extension.toLowerCase(Locale.ENGLISH).equals("jpeg")) { - extension = "jpg"; - } - title = jpegPattern.matcher(title).replaceFirst(".jpg"); - if (extension != null && !title.toLowerCase(Locale.getDefault()) - .endsWith("." + extension.toLowerCase(Locale.ENGLISH))) { - title += "." + extension; - } - - // If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228) - // If title has an extension in it, if won't be true - if (extension == null && title.lastIndexOf(".")<=0) { - extension = "jpg"; - title += "." + extension; - } - - return title; - } - - /** - * Launches intent to rate app - * @param context - */ - public static void rateApp(Context context) { - final String appPackageName = context.getPackageName(); - try { - context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.PLAY_STORE_PREFIX + appPackageName))); - } - catch (android.content.ActivityNotFoundException anfe) { - handleWebUrl(context, Uri.parse(Urls.PLAY_STORE_URL_PREFIX + appPackageName)); - } - } - - /** - * Opens Custom Tab Activity with in-app browser for the specified URL. - * Launches intent for web URL - * @param context - * @param url - */ - public static void handleWebUrl(Context context, Uri url) { - Timber.d("Launching web url %s", url.toString()); - - final CustomTabColorSchemeParams color = new CustomTabColorSchemeParams.Builder() - .setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor)) - .setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor)) - .build(); - - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); - builder.setDefaultColorSchemeParams(color); - builder.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right); - CustomTabsIntent customTabsIntent = builder.build(); - // Clear previous browser tasks, so that back/exit buttons work as intended. - customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - customTabsIntent.launchUrl(context, url); - } - - /** - * Util function to handle geo coordinates. It no longer depends on google maps and any app - * capable of handling the map intent can handle it - * - * @param context The context for launching intent - * @param latLng The latitude and longitude of the location - */ - public static void handleGeoCoordinates(final Context context, final LatLng latLng) { - handleGeoCoordinates(context, latLng, 16); - } - - /** - * Util function to handle geo coordinates with specified zoom level. It no longer depends on - * google maps and any app capable of handling the map intent can handle it - * - * @param context The context for launching intent - * @param latLng The latitude and longitude of the location - * @param zoomLevel The zoom level - */ - public static void handleGeoCoordinates(final Context context, final LatLng latLng, - final double zoomLevel) { - final Intent mapIntent = new Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel)); - if (mapIntent.resolveActivity(context.getPackageManager()) != null) { - context.startActivity(mapIntent); - } else { - ViewUtil.showShortToast(context, context.getString(R.string.map_application_missing)); - } - } - - /** - * To take screenshot of the screen and return it in Bitmap format - * - * @param view - * @return - */ - public static Bitmap getScreenShot(View view) { - View screenView = view.getRootView(); - screenView.setDrawingCacheEnabled(true); - Bitmap drawingCache = screenView.getDrawingCache(); - if (drawingCache != null) { - Bitmap bitmap = Bitmap.createBitmap(drawingCache); - screenView.setDrawingCacheEnabled(false); - return bitmap; - } - return null; - } - - /* - *Copies the content to the clipboard - * - */ - public static void copy(String label,String text, Context context){ - ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(label, text); - clipboard.setPrimaryClip(clip); - } - - /** - * This method sets underlined string text to a TextView - * - * @param textView TextView associated with string resource - * @param stringResourceName string resource name - * @param context - */ - public static void setUnderlinedText(TextView textView, int stringResourceName, Context context) { - SpannableString content = new SpannableString(context.getString(stringResourceName)); - content.setSpan(new UnderlineSpan(), 0, content.length(), 0); - textView.setText(content); - } - - /** - * For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt - * @param date - * @return - */ - public static boolean isMonumentsEnabled(final Date date) { - if (date.getMonth() == 8) { - return true; - } - return false; - } - - /** - * Util function to get the start date of wlm monument - * For this release we are hardcoding it to be 1st September - * @return - */ - public static String getWLMStartDate() { - return "1 Sep"; - } - - /*** - * Util function to get the end date of wlm monument - * For this release we are hardcoding it to be 31st October - * @return - */ - public static String getWLMEndDate() { - return "30 Sep"; - } - - /*** - * Function to get the current WLM year - * It increments at the start of September in line with the other WLM functions - * (No consideration of locales for now) - * @param calendar - * @return - */ - public static int getWikiLovesMonumentsYear(Calendar calendar) { - int year = calendar.get(Calendar.YEAR); - if (calendar.get(Calendar.MONTH) < Calendar.SEPTEMBER) { - year -= 1; - } - return year; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java index 8fd3fc704..a9b7381df 100644 --- a/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; + import android.net.Uri; import android.view.LayoutInflater; import android.view.View; @@ -7,6 +9,7 @@ import android.view.ViewGroup; import android.widget.TextView; import androidx.viewpager.widget.PagerAdapter; +import fr.free.nrw.commons.utils.UnderlineUtils; public class WelcomePagerAdapter extends PagerAdapter { private static final int[] PAGE_LAYOUTS = new int[]{ @@ -46,8 +49,8 @@ public class WelcomePagerAdapter extends PagerAdapter { if (position == PAGE_LAYOUTS.length - 1) { // Add link to more information TextView moreInfo = layout.findViewById(R.id.welcomeInfo); - Utils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text, container.getContext()); - moreInfo.setOnClickListener(view -> Utils.handleWebUrl( + UnderlineUtils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text); + moreInfo.setOnClickListener(view -> handleWebUrl( container.getContext(), Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents") )); 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 840bc7ca3..a606d639f 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 @@ -25,7 +25,6 @@ import androidx.core.content.ContextCompat import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.login.LoginCallback import fr.free.nrw.commons.auth.login.LoginClient import fr.free.nrw.commons.auth.login.LoginResult @@ -38,6 +37,7 @@ 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.handleWebUrl import io.reactivex.disposables.CompositeDisposable import timber.log.Timber import java.util.Locale @@ -254,10 +254,10 @@ class LoginActivity : AccountAuthenticatorActivity() { } private fun forgotPassword() = - Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL)) + handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL)) private fun onPrivacyPolicyClicked() = - Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) + handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) private fun signUp() = startActivity(Intent(this, SignupActivity::class.java)) diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt index 7a4720177..7a30ff5c4 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt @@ -7,7 +7,6 @@ import android.view.LayoutInflater import android.view.View import androidx.core.content.ContextCompat import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.campaigns.models.Campaign import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.databinding.LayoutCampaginBinding @@ -16,6 +15,7 @@ import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort import fr.free.nrw.commons.utils.DateUtil.getExtraShortDateString import fr.free.nrw.commons.utils.SwipableCardView import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.handleWebUrl import timber.log.Timber import java.text.ParseException @@ -74,7 +74,7 @@ class CampaignView : SwipableCardView { if (it.isWLMCampaign) { ((context) as MainActivity).showNearby() } else { - Utils.handleWebUrl(context, Uri.parse(it.link)) + handleWebUrl(context, Uri.parse(it.link)) } } } diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt index 4743e0e54..53013c1ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt @@ -26,7 +26,7 @@ class CampaignsPresenter @Inject constructor( private val okHttpJsonApiClient: OkHttpJsonApiClient?, @param:Named(IO_THREAD) private val ioScheduler: Scheduler, @param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler -) : BasePresenter { +) : BasePresenter { private var view: ICampaignsView? = null private var disposable: Disposable? = null private var campaign: Campaign? = null diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt index 62a19aaac..1cbf7da1f 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt @@ -1,11 +1,10 @@ package fr.free.nrw.commons.campaigns -import fr.free.nrw.commons.MvpView import fr.free.nrw.commons.campaigns.models.Campaign /** * Interface which defines the view contracts of the campaign view */ -interface ICampaignsView : MvpView { +interface ICampaignsView { fun showCampaigns(campaign: Campaign?) } 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 a42d26fd6..e15d9baf3 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 @@ -13,9 +13,9 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import fr.free.nrw.commons.BuildConfig.COMMONS_URL import fr.free.nrw.commons.Media import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.databinding.ActivityCategoryDetailsBinding import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment @@ -23,6 +23,9 @@ import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.wikidata.model.WikiSite +import fr.free.nrw.commons.wikidata.model.page.PageTitle import kotlinx.coroutines.launch import javax.inject.Inject @@ -199,8 +202,9 @@ class CategoryDetailsActivity : BaseActivity(), override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_browser_current_category -> { - val title = Utils.getPageTitle(CATEGORY_PREFIX + categoryName) - Utils.handleWebUrl(this, Uri.parse(title.canonicalUri)) + val title = PageTitle(CATEGORY_PREFIX + categoryName, WikiSite(COMMONS_URL)) + + handleWebUrl(this, Uri.parse(title.canonicalUri)) true } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt index 7027950e3..0e039729f 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt @@ -9,7 +9,6 @@ import fr.free.nrw.commons.BasePresenter interface ContributionsContract { interface View { - fun showMessage(localizedMessage: String) fun getContext(): Context? } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt index 3992d35dd..541cc6e56 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt @@ -30,7 +30,6 @@ import androidx.work.WorkManager import fr.free.nrw.commons.MapController.NearbyPlacesInfo import fr.free.nrw.commons.Media import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.campaigns.CampaignView import fr.free.nrw.commons.campaigns.CampaignsPresenter @@ -64,6 +63,9 @@ import fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished import fr.free.nrw.commons.utils.PermissionUtils.hasPermission import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.isMonumentsEnabled +import fr.free.nrw.commons.utils.wLMEndDate +import fr.free.nrw.commons.utils.wLMStartDate import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -242,8 +244,8 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On private fun initWLMCampaign() { wlmCampaign = Campaign( getString(R.string.wlm_campaign_title), - getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(), - Utils.getWLMEndDate().toString(), NearbyParentFragment.WLM_URL, true + getString(R.string.wlm_campaign_description), wLMStartDate, + wLMEndDate, NearbyParentFragment.WLM_URL, true ) } @@ -729,7 +731,7 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On * of campaigns on the campaigns card */ private fun fetchCampaigns() { - if (Utils.isMonumentsEnabled(Date())) { + if (isMonumentsEnabled) { if (binding != null) { binding!!.campaignsView.setCampaign(wlmCampaign) binding!!.campaignsView.visibility = View.VISIBLE @@ -743,10 +745,6 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On } } - override fun showMessage(message: String) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show() - } - override fun showCampaigns(campaign: Campaign?) { if (campaign != null && !isUserProfile) { if (binding != null) { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt index c6b8dd8a8..0c8c822ad 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt @@ -15,7 +15,7 @@ class ContributionsListContract { fun showNoContributionsUI(shouldShow: Boolean) } - interface UserActionListener : BasePresenter { + interface UserActionListener : BasePresenter { fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt index 9ecb35b24..b86cd6dc9 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt @@ -29,7 +29,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator import fr.free.nrw.commons.Media import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance import fr.free.nrw.commons.databinding.FragmentContributionsListBinding @@ -41,6 +40,8 @@ import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.showShortToast +import fr.free.nrw.commons.utils.copyToClipboard +import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.model.WikiSite import org.apache.commons.lang3.StringUtils import javax.inject.Inject @@ -527,14 +528,13 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL */ override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) { if (copyWikicode) { - val wikicode = contribution!!.media.wikiCode - Utils.copy("wikicode", wikicode, context) + requireContext().copyToClipboard("wikicode", contribution!!.media.wikiCode) } val url = languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace ?.getWikipediaPageTitle()) - Utils.handleWebUrl(context, Uri.parse(url)) + handleWebUrl(requireContext(), Uri.parse(url)) } fun getContributionStateAt(position: Int): Int { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java index cf7269123..89593d07e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons.explore.depictions; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; + import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -8,16 +10,11 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.FrameLayout; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; -import androidx.viewpager.widget.ViewPager; import com.google.android.material.snackbar.Snackbar; -import com.google.android.material.tabs.TabLayout; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.ViewPagerAdapter; import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao; import fr.free.nrw.commons.category.CategoryImagesCallback; @@ -249,7 +246,7 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe case R.id.browser_actions_menu_items: String entityId=getIntent().getStringExtra("entityId"); Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId); - Utils.handleWebUrl(this, uri); + handleWebUrl(this, uri); return true; case R.id.menu_bookmark_current_item: diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java index 2b3aa9f3c..364f4d53a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java @@ -2,7 +2,9 @@ package fr.free.nrw.commons.explore.map; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; +import static fr.free.nrw.commons.utils.GeoCoordinatesKt.handleGeoCoordinates; import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; import android.Manifest.permission; import android.annotation.SuppressLint; @@ -36,7 +38,6 @@ import fr.free.nrw.commons.BaseMarker; import fr.free.nrw.commons.MapController; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.databinding.FragmentExploreMapBinding; @@ -639,13 +640,13 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment */ private void passInfoToSheet(final Place place) { binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener( - view -> Utils.handleGeoCoordinates(getActivity(), + view -> handleGeoCoordinates(requireActivity(), place.getLocation(), binding.mapView.getZoomLevelDouble())); binding.bottomSheetDetailsBinding.commonsButton.setVisibility( place.hasCommonsLink() ? View.VISIBLE : View.GONE); binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener( - view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); + view -> handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); int index = 0; for (Media media : mediaList) { 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 2a7b7713b..a8b6ddf26 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 @@ -30,7 +30,6 @@ import fr.free.nrw.commons.CameraPosition import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.Media import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.csrf.CsrfTokenClient import fr.free.nrw.commons.coordinates.CoordinateEditHelper @@ -45,6 +44,7 @@ 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.handleGeoCoordinates import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.osmdroid.tileprovider.tilesource.TileSourceFactory @@ -432,8 +432,8 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { position?.let { mapView?.zoomLevelDouble?.let { zoomLevel -> - Utils.handleGeoCoordinates(this, it, zoomLevel) - } ?: Utils.handleGeoCoordinates(this, it) + handleGeoCoordinates(this, it, zoomLevel) + } ?: handleGeoCoordinates(this, it) } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt index f371b733f..5980e1fb5 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt @@ -77,7 +77,7 @@ import fr.free.nrw.commons.CommonsApplication.Companion.instance import fr.free.nrw.commons.Media import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.utils.UnderlineUtils import fr.free.nrw.commons.actions.ThanksClient import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException @@ -119,6 +119,10 @@ import fr.free.nrw.commons.utils.PermissionUtils.hasPermission import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil.showShortToast import fr.free.nrw.commons.utils.ViewUtilWrapper +import fr.free.nrw.commons.utils.copyToClipboard +import fr.free.nrw.commons.utils.handleGeoCoordinates +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.setUnderlinedText import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision import io.reactivex.Observable import io.reactivex.Single @@ -316,8 +320,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C _binding = FragmentMediaDetailBinding.inflate(inflater, container, false) val view: View = binding.root - - Utils.setUnderlinedText(binding.seeMore, R.string.nominated_see_more, requireContext()) + binding.seeMore.setUnderlinedText(R.string.nominated_see_more) if (isCategoryImage) { binding.authorLinearLayout.visibility = View.VISIBLE @@ -909,7 +912,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C private fun onMediaDetailLicenceClicked() { val url: String? = media!!.licenseUrl if (!StringUtils.isBlank(url) && activity != null) { - Utils.handleWebUrl(activity, Uri.parse(url)) + handleWebUrl(requireContext(), Uri.parse(url)) } else { viewUtil.showShortToast(requireActivity(), getString(R.string.null_url)) } @@ -917,17 +920,17 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C private fun onMediaDetailCoordinatesClicked() { if (media!!.coordinates != null && activity != null) { - Utils.handleGeoCoordinates(activity, media!!.coordinates) + handleGeoCoordinates(requireContext(), media!!.coordinates!!) } } private fun onCopyWikicodeClicked() { val data: String = "[[" + media!!.filename + "|thumb|" + media!!.fallbackDescription + "]]" - Utils.copy("wikiCode", data, context) + requireContext().copyToClipboard("wikiCode", data) Timber.d("Generated wikidata copy code: %s", data) - Toast.makeText(context, getString(R.string.wikicode_copied), Toast.LENGTH_SHORT) + Toast.makeText(requireContext(), getString(R.string.wikicode_copied), Toast.LENGTH_SHORT) .show() } @@ -1765,7 +1768,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C private fun onSeeMoreClicked() { if (binding.nominatedDeletionBanner.visibility == View.VISIBLE && activity != null) { - Utils.handleWebUrl(activity, Uri.parse(media!!.pageTitle.mobileUri)) + handleWebUrl(requireContext(), Uri.parse(media!!.pageTitle.mobileUri)) } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index cba582a35..324d5867b 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -1,6 +1,6 @@ package fr.free.nrw.commons.media; -import static fr.free.nrw.commons.Utils.handleWebUrl; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; import android.os.Handler; import android.os.Looper; @@ -31,7 +31,7 @@ import com.google.android.material.snackbar.Snackbar; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.utils.ClipboardUtils; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.bookmarks.models.Bookmark; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider; @@ -216,7 +216,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple return true; case R.id.menu_copy_link: String uri = m.getPageTitle().getCanonicalUri(); - Utils.copy("shareLink", uri, requireContext()); + ClipboardUtils.copy("shareLink", uri, requireContext()); Timber.d("Copied share link to clipboard: %s", uri); Toast.makeText(requireContext(), getString(R.string.menu_link_copied), Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt index 202f2c305..5f4d0ab13 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt @@ -10,12 +10,13 @@ import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.PopupMenu import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.LoginActivity import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.utils.ActivityUtils +import fr.free.nrw.commons.utils.handleGeoCoordinates +import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.WikidataConstants import timber.log.Timber import javax.inject.Inject @@ -104,7 +105,7 @@ class CommonPlaceClickActions fun onDirectionsClicked(): (Place) -> Unit = { - Utils.handleGeoCoordinates(activity, it.getLocation()) + handleGeoCoordinates(activity, it.getLocation()) } private fun storeSharedPrefs(selectedPlace: Place) { @@ -113,7 +114,7 @@ class CommonPlaceClickActions } private fun openWebView(link: Uri): Boolean { - Utils.handleWebUrl(activity, link) + handleWebUrl(activity, link) return true } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt index 26875927e..a0dcead07 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt @@ -58,12 +58,10 @@ import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.MapController.NearbyPlacesInfo import fr.free.nrw.commons.Media import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment -import fr.free.nrw.commons.customselector.ui.selector.ImageLoader import fr.free.nrw.commons.databinding.FragmentNearbyParentBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment import fr.free.nrw.commons.filepicker.FilePicker @@ -76,7 +74,6 @@ import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType import fr.free.nrw.commons.location.LocationUpdateListener import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.MediaDetailPagerFragment -import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider import fr.free.nrw.commons.navtab.NavTab import fr.free.nrw.commons.nearby.BottomSheetAdapter import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener @@ -105,6 +102,10 @@ import fr.free.nrw.commons.utils.NearbyFABUtils.removeAnchorFromFAB import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.copyToClipboard +import fr.free.nrw.commons.utils.handleGeoCoordinates +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.isMonumentsEnabled import fr.free.nrw.commons.wikidata.WikidataConstants import fr.free.nrw.commons.wikidata.WikidataEditListener import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener @@ -140,7 +141,6 @@ import java.util.UUID import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Named -import javax.sql.DataSource import kotlin.concurrent.Volatile @@ -467,7 +467,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), } } _isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true - if (Utils.isMonumentsEnabled(Date())) { + if (isMonumentsEnabled) { binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE } else { binding?.rlContainerWlmMonthMessage?.visibility = View.GONE @@ -836,7 +836,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), loadAnimations() setBottomSheetCallbacks() addActionToTitle() - if (!Utils.isMonumentsEnabled(Date())) { + if (!isMonumentsEnabled) { NearbyFilterState.setWlmSelected(false) } } @@ -1017,11 +1017,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), */ private fun addActionToTitle() { binding!!.bottomSheetDetails.title.setOnLongClickListener { view -> - Utils.copy( - "place", binding!!.bottomSheetDetails.title.text.toString(), - context + requireContext().copyToClipboard( + "place", binding!!.bottomSheetDetails.title.text.toString() ) - Toast.makeText(context, fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT) + Toast.makeText(requireContext(), fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT) .show() true } @@ -1580,7 +1579,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), searchLatLng, false, true, - Utils.isMonumentsEnabled(Date()), + isMonumentsEnabled, customQuery ) } @@ -1633,7 +1632,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), searchLatLng, false, true, - Utils.isMonumentsEnabled(Date()), + isMonumentsEnabled, customQuery ) } @@ -2854,14 +2853,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), R.drawable.ic_directions_black_24dp -> { selectedPlace?.let { - Utils.handleGeoCoordinates(this.context, it.getLocation()) + handleGeoCoordinates(requireContext(), it.getLocation()) binding?.map?.zoomLevelDouble ?: 0.0 } } R.drawable.ic_wikidata_logo_24dp -> { selectedPlace?.siteLinks?.wikidataLink?.let { - Utils.handleWebUrl(this.context, it) + handleWebUrl(requireContext(), it) } } @@ -2879,13 +2878,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), R.drawable.ic_wikipedia_logo_24dp -> { selectedPlace?.siteLinks?.wikipediaLink?.let { - Utils.handleWebUrl(this.context, it) + handleWebUrl(requireContext(), it) } } R.drawable.ic_commons_icon_vector -> { selectedPlace?.siteLinks?.commonsLink?.let { - Utils.handleWebUrl(this.context, it) + handleWebUrl(requireContext(), it) } } 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 1547f89ad..76975964b 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 @@ -13,7 +13,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.databinding.ActivityNotificationBinding @@ -22,6 +21,7 @@ import fr.free.nrw.commons.notification.models.NotificationType import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.utils.NetworkUtils import fr.free.nrw.commons.utils.ViewUtil +import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -197,7 +197,7 @@ class NotificationActivity : BaseActivity() { private fun handleUrl(url: String?) { if (url.isNullOrEmpty()) return - Utils.handleWebUrl(this, Uri.parse(url)) + handleWebUrl(this, Uri.parse(url)) } private fun setItems(notificationList: List?) { 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 164842c9a..105cf1860 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 @@ -3,16 +3,16 @@ package fr.free.nrw.commons.profile import android.content.Context import android.content.Intent import android.graphics.Bitmap -import android.net.Uri import android.os.Bundle import android.util.Log -import android.view.* +import android.view.Menu +import android.view.MenuItem +import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.FileProvider import androidx.fragment.app.Fragment import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.ContributionsFragment @@ -23,7 +23,7 @@ import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.utils.DialogUtil import java.io.File import java.io.FileOutputStream -import java.util.* +import java.util.Locale import javax.inject.Inject /** @@ -133,7 +133,7 @@ class ProfileActivity : BaseActivity() { return when (item.itemId) { R.id.share_app_icon -> { val rootView = window.decorView.findViewById(android.R.id.content) - val screenShot = Utils.getScreenShot(rootView) + val screenShot = getScreenShot(rootView) if (screenShot == null) { Log.e("ERROR", "ScreenShot is null") return false @@ -212,6 +212,24 @@ class ProfileActivity : BaseActivity() { binding.tabLayout.visibility = if (isVisible) View.VISIBLE else View.GONE } + /** + * To take screenshot of the screen and return it in Bitmap format + * + * @param view + * @return + */ + fun getScreenShot(view: View): Bitmap? { + val screenView = view.rootView + screenView.isDrawingCacheEnabled = true + val drawingCache = screenView.drawingCache + if (drawingCache != null) { + val bitmap = Bitmap.createBitmap(drawingCache) + screenView.isDrawingCacheEnabled = false + return bitmap + } + return null + } + companion object { const val KEY_USERNAME = "username" const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions" diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt index f967b8619..8f23674ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt @@ -15,7 +15,6 @@ import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeUtils import com.google.android.material.badge.ExperimentalBadgeUtils import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.databinding.FragmentAchievementsBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment @@ -27,6 +26,7 @@ import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.apache.commons.lang3.StringUtils @@ -524,7 +524,7 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){ getString(R.string.ok), getString(R.string.read_help_link), {}, - { Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) }, + { handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) }, null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt index 092f057e9..387dd4672 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt @@ -33,9 +33,7 @@ import com.karumi.dexter.MultiplePermissionsReport import com.karumi.dexter.PermissionToken import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.multi.MultiplePermissionsListener -import fr.free.nrw.commons.BuildConfig.MOBILE_META_URL import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.activity.SingleWebViewActivity import fr.free.nrw.commons.campaigns.CampaignView import fr.free.nrw.commons.contributions.ContributionController @@ -53,6 +51,7 @@ import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.PermissionUtils import fr.free.nrw.commons.utils.StringUtil import fr.free.nrw.commons.utils.ViewUtil +import fr.free.nrw.commons.utils.handleWebUrl import java.util.Locale import javax.inject.Inject import javax.inject.Named @@ -239,7 +238,10 @@ class SettingsFragment : PreferenceFragmentCompat() { val betaTesterPreference: Preference? = findPreference("becomeBetaTester") betaTesterPreference?.setOnPreferenceClickListener { - Utils.handleWebUrl(requireActivity(), Uri.parse(getString(R.string.beta_opt_in_link))) + handleWebUrl( + requireActivity(), + Uri.parse(getString(R.string.beta_opt_in_link)) + ) true } @@ -296,7 +298,7 @@ class SettingsFragment : PreferenceFragmentCompat() { getString(R.string.ok), getString(R.string.read_help_link), { }, - { Utils.handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) }, + { handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) }, null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt b/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt index 0c4ded8b2..2de17f849 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt @@ -1,11 +1,11 @@ package fr.free.nrw.commons.upload import android.content.Context -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource import fr.free.nrw.commons.settings.Prefs.Licenses import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha +import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear import org.apache.commons.lang3.StringUtils import java.text.SimpleDateFormat import java.util.Calendar @@ -49,7 +49,7 @@ class PageContentsCreator @Inject constructor(private val context: Context) { String.format( Locale.ENGLISH, "{{Wiki Loves Monuments %d|1= %s}}\n", - Utils.getWikiLovesMonumentsYear(Calendar.getInstance()), + getWikiLovesMonumentsYear(Calendar.getInstance()), contribution.countryCode ) ) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt index f357cd112..6d2321def 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt @@ -1,10 +1,10 @@ package fr.free.nrw.commons.upload import android.net.Uri -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper.Companion.getExtensionFromMimeType import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.utils.ImageUtils +import fr.free.nrw.commons.utils.fixExtension class UploadItem( var mediaUri: Uri?, @@ -32,7 +32,7 @@ class UploadItem( * languages have been entered, the first language is used. */ val filename: String - get() = Utils.fixExtension( + get() = fixExtension( uploadMediaDetails[0].captionText, getExtensionFromMimeType(mimeType) ) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt index 0415d3270..65826a505 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt @@ -16,11 +16,13 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.TextView import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.databinding.FragmentMediaLicenseBinding import fr.free.nrw.commons.upload.UploadActivity import fr.free.nrw.commons.upload.UploadBaseFragment import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.toLicenseName +import fr.free.nrw.commons.utils.toLicenseUrl import timber.log.Timber import javax.inject.Inject @@ -126,20 +128,20 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View { } override fun setSelectedLicense(license: String?) { - var position = licenses!!.indexOf(getString(Utils.licenseNameFor(license))) + var position = license?.let { licenses!!.indexOf(getString(it.toLicenseName())) } ?: -1 // Check if position is valid if (position < 0) { Timber.d("Invalid position: %d. Using default licenses", position) position = licenses!!.size - 1 - } else { - Timber.d("Position: %d %s", position, getString(Utils.licenseNameFor(license))) } binding.spinnerLicenseList.setSelection(position) } override fun updateLicenseSummary(selectedLicense: String?, numberOfItems: Int) { - val licenseHyperLink = "" + - getString(Utils.licenseNameFor(selectedLicense)) + "
" + if (selectedLicense == null) return + + val licenseHyperLink = "" + + getString(selectedLicense.toLicenseName()) + "
" setTextViewHTML( binding.tvShareLicenseSummary, resources @@ -184,7 +186,7 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View { } private fun launchBrowser(hyperLink: String) = - Utils.handleWebUrl(context, Uri.parse(hyperLink)) + handleWebUrl(requireContext(), Uri.parse(hyperLink)) override fun onDestroyView() { presenter.onDetachView() diff --git a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt index 25d1a2324..df75019b2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons.upload.license -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.settings.Prefs +import fr.free.nrw.commons.utils.toLicenseName import timber.log.Timber import java.lang.reflect.Method import java.lang.reflect.Proxy @@ -34,12 +34,14 @@ class MediaLicensePresenter @Inject constructor( val licenses = repository.getLicenses() view.setLicenses(licenses) - var selectedLicense = defaultKVStore.getString( + //CC_BY_SA_4 is the default one used by the commons web app + var selectedLicense: String = defaultKVStore.getString( Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_4 - ) //CC_BY_SA_4 is the default one used by the commons web app + ) ?: Prefs.Licenses.CC_BY_SA_4 + try { //I have to make sure that the stored default license was not one of the deprecated one's - Utils.licenseNameFor(selectedLicense) + selectedLicense.toLicenseName() } catch (exception: IllegalStateException) { Timber.e(exception) selectedLicense = Prefs.Licenses.CC_BY_SA_4 diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt index c368b96ac..d6d774208 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt @@ -54,7 +54,7 @@ interface UploadMediaDetailsContract { fun showBadImagePopup(errorCode: Int, index: Int, uploadItem: UploadItem) } - interface UserActionListener : BasePresenter { + interface UserActionListener : BasePresenter { fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore) fun receiveImage( diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt new file mode 100644 index 000000000..64d3636f0 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt @@ -0,0 +1,20 @@ +package fr.free.nrw.commons.utils + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Context.CLIPBOARD_SERVICE + +object ClipboardUtils { + // Convenience for Java usages - remove when they are converted. + @JvmStatic + fun copy(label: String?, text: String?, context: Context) { + context.copyToClipboard(label, text) + } +} + +fun Context.copyToClipboard(label: String?, text: String?) { + with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) { + setPrimaryClip(ClipData.newPlainText(label, text)) + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt b/app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt new file mode 100644 index 000000000..b9e3988a3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt @@ -0,0 +1,38 @@ +package fr.free.nrw.commons.utils + +import java.util.Locale +import java.util.regex.Pattern + +private val jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE) + +/** + * Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected + * @param theTitle File name + * @param ext Correct extension + * @return File with correct extension + */ +fun fixExtension(theTitle: String, ext: String?): String { + var result = theTitle + var extension = ext + + // People are used to ".jpg" more than ".jpeg" which the system gives us. + if (extension != null && extension.lowercase() == "jpeg") { + extension = "jpg" + } + + result = jpegPattern.matcher(result).replaceFirst(".jpg") + if (extension != null && + !result.lowercase(Locale.getDefault()).endsWith("." + extension.lowercase()) + ) { + result += ".$extension" + } + + // If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228) + // If title has an extension in it, if won't be true + if (extension == null && result.lastIndexOf(".") <= 0) { + extension = "jpg" + result += ".$extension" + } + + return result +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt b/app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt new file mode 100644 index 000000000..513e36f10 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt @@ -0,0 +1,27 @@ +package fr.free.nrw.commons.utils + +import android.content.Context +import android.content.Intent +import fr.free.nrw.commons.R +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.utils.ViewUtil.showShortToast + +/** + * Util function to handle geo coordinates with specified zoom level. It no longer depends on + * google maps and any app capable of handling the map intent can handle it + * + * @param context The context for launching intent + * @param latLng The latitude and longitude of the location + * @param zoomLevel The zoom level + */ +fun handleGeoCoordinates( + context: Context, latLng: LatLng, + zoomLevel: Double = 16.0 +) { + val mapIntent = Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel)) + if (mapIntent.resolveActivity(context.packageManager) != null) { + context.startActivity(mapIntent) + } else { + showShortToast(context, context.getString(R.string.map_application_missing)) + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt b/app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt new file mode 100644 index 000000000..065a14718 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt @@ -0,0 +1,31 @@ +package fr.free.nrw.commons.utils + +import fr.free.nrw.commons.R +import fr.free.nrw.commons.settings.Prefs + +/** + * Generates licence name with given ID + * @return Name of license + */ +fun String.toLicenseName(): Int = when (this) { + Prefs.Licenses.CC_BY_3 -> R.string.license_name_cc_by + Prefs.Licenses.CC_BY_4 -> R.string.license_name_cc_by_four + Prefs.Licenses.CC_BY_SA_3 -> R.string.license_name_cc_by_sa + Prefs.Licenses.CC_BY_SA_4 -> R.string.license_name_cc_by_sa_four + Prefs.Licenses.CC0 -> R.string.license_name_cc0 + else -> throw IllegalStateException("Unrecognized license value: $this") +} + +/** + * Generates license url with given ID + * @return Url of license + */ +fun String.toLicenseUrl(): String = when (this) { + Prefs.Licenses.CC_BY_3 -> "https://creativecommons.org/licenses/by/3.0/" + Prefs.Licenses.CC_BY_4 -> "https://creativecommons.org/licenses/by/4.0/" + Prefs.Licenses.CC_BY_SA_3 -> "https://creativecommons.org/licenses/by-sa/3.0/" + Prefs.Licenses.CC_BY_SA_4 -> "https://creativecommons.org/licenses/by-sa/4.0/" + Prefs.Licenses.CC0 -> "https://creativecommons.org/publicdomain/zero/1.0/" + else -> throw IllegalStateException("Unrecognized license value: $this") +} + diff --git a/app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt b/app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt new file mode 100644 index 000000000..d5f5736f5 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt @@ -0,0 +1,39 @@ +package fr.free.nrw.commons.utils + +import java.util.Calendar +import java.util.Date + +/** + * Get the start date of wlm monument + * For this release we are hardcoding it to be 1st September + * @return + */ +const val wLMStartDate: String = "1 Sep" + +/*** + * Get the end date of wlm monument + * For this release we are hardcoding it to be 31st October + * @return + */ +const val wLMEndDate: String = "30 Sep" + +/** + * For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt + */ +val isMonumentsEnabled: Boolean + get() = Date().month == 8 + +/*** + * Function to get the current WLM year + * It increments at the start of September in line with the other WLM functions + * (No consideration of locales for now) + * @param calendar + * @return + */ +fun getWikiLovesMonumentsYear(calendar: Calendar): Int { + var year = calendar[Calendar.YEAR] + if (calendar[Calendar.MONTH] < Calendar.SEPTEMBER) { + year -= 1 + } + return year +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt new file mode 100644 index 000000000..75760d4ab --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt @@ -0,0 +1,19 @@ +package fr.free.nrw.commons.utils + +import android.widget.TextView +import androidx.core.text.buildSpannedString +import androidx.core.text.underline + +object UnderlineUtils { + // Convenience method for Java usages - remove when those classes are converted + @JvmStatic + fun setUnderlinedText(textView: TextView, stringResourceName: Int) { + textView.setUnderlinedText(stringResourceName) + } +} + +fun TextView.setUnderlinedText(stringResourceName: Int) { + text = buildSpannedString { + underline { append(context.getString(stringResourceName)) } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt new file mode 100644 index 000000000..4843cf0aa --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt @@ -0,0 +1,33 @@ +package fr.free.nrw.commons.utils + +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.content.ContextCompat +import fr.free.nrw.commons.R +import timber.log.Timber + +/** + * Opens Custom Tab Activity with in-app browser for the specified URL. + * Launches intent for web URL + */ +fun handleWebUrl(context: Context, url: Uri) { + Timber.d("Launching web url %s", url.toString()) + + val color = CustomTabColorSchemeParams.Builder() + .setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor)) + .setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor)) + .build() + + val customTabsIntent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams(color) + .setExitAnimations( + context, android.R.anim.slide_in_left, android.R.anim.slide_out_right + ).build() + + // Clear previous browser tasks, so that back/exit buttons work as intended. + customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + customTabsIntent.launchUrl(context, url) +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt index e9e68a3ad..d9151335a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt @@ -1,5 +1,6 @@ package fr.free.nrw.commons +import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear import org.junit.Test import org.junit.jupiter.api.Assertions import java.util.Calendar @@ -9,20 +10,20 @@ class UtilsTest { fun wikiLovesMonumentsYearBeforeSeptember() { val cal = Calendar.getInstance() cal.set(2022, Calendar.FEBRUARY, 1) - Assertions.assertEquals(2021, Utils.getWikiLovesMonumentsYear(cal)) + Assertions.assertEquals(2021, getWikiLovesMonumentsYear(cal)) } @Test fun wikiLovesMonumentsYearInSeptember() { val cal = Calendar.getInstance() cal.set(2022, Calendar.SEPTEMBER, 1) - Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) + Assertions.assertEquals(2022, getWikiLovesMonumentsYear(cal)) } @Test fun wikiLovesMonumentsYearAfterSeptember() { val cal = Calendar.getInstance() cal.set(2022, Calendar.DECEMBER, 1) - Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) + Assertions.assertEquals(2022, getWikiLovesMonumentsYear(cal)) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt index 4fd5689da..e3f1c86cc 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt @@ -248,13 +248,6 @@ class ContributionsFragmentUnitTests { fragment.onDestroyView() } - @Test - @Throws(Exception::class) - fun testShowMessage() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - fragment.showMessage("") - } - @Test @Throws(Exception::class) fun testShowCampaigns() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt index 68b1c95cb..b448df6d2 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt @@ -1,11 +1,12 @@ package fr.free.nrw.commons.upload import com.nhaarman.mockitokotlin2.verify -import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.utils.UnderlineUtils import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.upload.license.MediaLicenseContract import fr.free.nrw.commons.upload.license.MediaLicensePresenter +import fr.free.nrw.commons.utils.toLicenseName import org.junit.After import org.junit.Before import org.junit.Test @@ -25,7 +26,7 @@ import org.robolectric.RobolectricTestRunner */ @RunWith(RobolectricTestRunner::class) -@PrepareForTest(Utils::class) +@PrepareForTest(UnderlineUtils::class) class MediaLicensePresenterTest { @Mock internal lateinit var repository: UploadRepository @@ -39,7 +40,7 @@ class MediaLicensePresenterTest { @InjectMocks lateinit var mediaLicensePresenter: MediaLicensePresenter - private lateinit var mockedUtil: MockedStatic + private lateinit var mockedUtil: MockedStatic /** * initial setup test environemnt @@ -49,8 +50,7 @@ class MediaLicensePresenterTest { fun setUp() { MockitoAnnotations.openMocks(this) mediaLicensePresenter.onAttachView(view) - mockedUtil = Mockito.mockStatic(Utils::class.java) - `when`(Utils.licenseNameFor(ArgumentMatchers.anyString())).thenReturn(1) + mockedUtil = Mockito.mockStatic(UnderlineUtils::class.java) } @After diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt index 24e9b233a..f4e6a771b 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.utils -import fr.free.nrw.commons.Utils.fixExtension import org.junit.Assert.assertEquals import org.junit.Test From f98b49608e00331e9f659e104393b7cba35af525 Mon Sep 17 00:00:00 2001 From: Sonal Yadav Date: Sat, 5 Jul 2025 09:45:57 +0530 Subject: [PATCH 3/3] fix popup from appearing in nearby (#6359) Co-authored-by: Nicolas Raoul --- .../nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt index 77999cf2f..4d565adb2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt @@ -107,7 +107,10 @@ class UploadMediaPresenter @Inject constructor( view.showProgress(false) val gpsCoords = uploadItem.gpsCoords val hasImageCoordinates = gpsCoords != null && gpsCoords.imageCoordsExists - if (hasImageCoordinates && place == null) { + + // Only check for nearby places if image has coordinates AND no place was pre-selected + // This prevents the popup from appearing when uploading from Nearby feature + if (hasImageCoordinates && place == null && uploadItem.place == null) { checkNearbyPlaces(uploadItem) } }, { throwable: Throwable? ->