diff --git a/go/res/drawable/ic_widget.xml b/go/res/drawable/ic_widget.xml new file mode 100644 index 0000000000..53368760ee --- /dev/null +++ b/go/res/drawable/ic_widget.xml @@ -0,0 +1,25 @@ + + + + diff --git a/go/res/values-az/strings.xml b/go/res/values-az/strings.xml new file mode 100644 index 0000000000..c4b8cb7801 --- /dev/null +++ b/go/res/values-az/strings.xml @@ -0,0 +1,26 @@ + + + + + "Qısayolu seçmək üçün toxunub saxlayın." + "Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin." + "Qısayollar" + "%1$s qısayolları" + diff --git a/go/res/values-be/strings.xml b/go/res/values-be/strings.xml new file mode 100644 index 0000000000..4189e35fa0 --- /dev/null +++ b/go/res/values-be/strings.xml @@ -0,0 +1,26 @@ + + + + + "Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго." + "Дакраніцеся двойчы і ўтрымлівайце, каб выбраць ярлык або выкарыстоўваць спецыяльныя дзеянні." + "Ярлыкі" + "Ярлыкі %1$s" + diff --git a/go/res/values-bg/strings.xml b/go/res/values-bg/strings.xml new file mode 100644 index 0000000000..1b85992d99 --- /dev/null +++ b/go/res/values-bg/strings.xml @@ -0,0 +1,26 @@ + + + + + "Докоснете и задръжте за избор на пряк път." + "Докоснете двукратно и задръжте за избор на пряк път или използвайте персонализирани действия." + "Преки пътища" + "Преки пътища за %1$s" + diff --git a/go/res/values-bn/strings.xml b/go/res/values-bn/strings.xml new file mode 100644 index 0000000000..c56c925a29 --- /dev/null +++ b/go/res/values-bn/strings.xml @@ -0,0 +1,26 @@ + + + + + "কোনও শর্টকাট বেছে নিতে টাচ করে ধরে রাখুন।" + "কোনও শর্টকাট বেছে নিতে ডাবল ট্যাপ করে ধরে রাখুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন।" + "শর্টকাট" + "%1$s এর শর্টকাট" + diff --git a/go/res/values-bs/strings.xml b/go/res/values-bs/strings.xml new file mode 100644 index 0000000000..7042468b47 --- /dev/null +++ b/go/res/values-bs/strings.xml @@ -0,0 +1,26 @@ + + + + + "Dodirnite i držite da uzmete prečicu." + "Dvaput dodirnite i držite da uzmete prečicu ili koristite prilagođene akcije." + "Prečice" + "Prečice aplikacije %1$s" + diff --git a/go/res/values-ca/strings.xml b/go/res/values-ca/strings.xml new file mode 100644 index 0000000000..3b5c3f7b56 --- /dev/null +++ b/go/res/values-ca/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén premuda una drecera per seleccionar-la." + "Fes doble toc i mantén premut per seleccionar una drecera o per utilitzar accions personalitzades." + "Dreceres" + "Dreceres de l\'aplicació %1$s" + diff --git a/go/res/values-cs/strings.xml b/go/res/values-cs/strings.xml new file mode 100644 index 0000000000..e4018f2c0f --- /dev/null +++ b/go/res/values-cs/strings.xml @@ -0,0 +1,26 @@ + + + + + "Zkratku vyberete podržením." + "Dvojitým klepnutím a podržením vyberte zkratku, případně použijte vlastní akce." + "Zkratky" + "Zkratky aplikace %1$s" + diff --git a/go/res/values-de/strings.xml b/go/res/values-de/strings.xml new file mode 100644 index 0000000000..43a1b3a9e1 --- /dev/null +++ b/go/res/values-de/strings.xml @@ -0,0 +1,26 @@ + + + + + "Doppeltippen und halten, um eine Verknüpfung auszuwählen." + "Doppeltippen und halten, um eine Verknüpfung auszuwählen oder benutzerdefinierte Aktionen zu nutzen." + "Verknüpfungen" + "%1$s-Verknüpfungen" + diff --git a/go/res/values-el/strings.xml b/go/res/values-el/strings.xml new file mode 100644 index 0000000000..ae59907d16 --- /dev/null +++ b/go/res/values-el/strings.xml @@ -0,0 +1,26 @@ + + + + + "Αγγίξτε παρατεταμένα για να σηκώσετε μια συντόμευση." + "Πατήσετε δύο φορές παρατεταμένα για να σηκώσετε μια συντόμευση ή για να χρησιμοποιήσετε προσαρμοσμένες ενέργειες." + "Συντομεύσεις" + "Συντομεύσεις %1$s" + diff --git a/go/res/values-es-rUS/strings.xml b/go/res/values-es-rUS/strings.xml new file mode 100644 index 0000000000..5212c035d3 --- /dev/null +++ b/go/res/values-es-rUS/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén presionado para elegir un acceso directo." + "Presiona dos veces y mantén presionado para elegir un acceso directo o usar acciones personalizadas." + "Accesos directos" + "Accesos directos de %1$s" + diff --git a/go/res/values-es/strings.xml b/go/res/values-es/strings.xml new file mode 100644 index 0000000000..3ae45889cf --- /dev/null +++ b/go/res/values-es/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén pulsado el acceso directo que quieras." + "Toca dos veces y mantén pulsado el acceso directo o utiliza acciones personalizadas." + "Accesos directos" + "Accesos directos de %1$s" + diff --git a/go/res/values-et/strings.xml b/go/res/values-et/strings.xml new file mode 100644 index 0000000000..2513e65a33 --- /dev/null +++ b/go/res/values-et/strings.xml @@ -0,0 +1,26 @@ + + + + + "Otsetee valimiseks puudutage seda pikalt." + "Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks." + "Otseteed" + "Rakenduse %1$s otseteed" + diff --git a/go/res/values-eu/strings.xml b/go/res/values-eu/strings.xml new file mode 100644 index 0000000000..9949ef091d --- /dev/null +++ b/go/res/values-eu/strings.xml @@ -0,0 +1,26 @@ + + + + + "Eduki sakatuta lasterbide bat aukeratzeko." + "Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko." + "Lasterbideak" + "%1$s aplikazioaren lasterbidea" + diff --git a/go/res/values-fi/strings.xml b/go/res/values-fi/strings.xml new file mode 100644 index 0000000000..da9b0e1c42 --- /dev/null +++ b/go/res/values-fi/strings.xml @@ -0,0 +1,26 @@ + + + + + "Valitse pikakuvake painamalla sitä pitkään." + "Valitse pikakuvake tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla pitkään." + "Pikakuvakkeet" + "Kohteen %1$s pikakuvakkeet" + diff --git a/go/res/values-fr-rCA/strings.xml b/go/res/values-fr-rCA/strings.xml new file mode 100644 index 0000000000..c7fd6d6423 --- /dev/null +++ b/go/res/values-fr-rCA/strings.xml @@ -0,0 +1,26 @@ + + + + + "Maintenez un doigt sur le raccourci pour l\'ajouter" + "Touchez 2x un raccourci et maintenez doigt dessus pour l’aj. ou utiliser des actions personnalisées." + "Raccourcis" + "Raccourcis : %1$s" + diff --git a/go/res/values-fr/strings.xml b/go/res/values-fr/strings.xml new file mode 100644 index 0000000000..238fe73d75 --- /dev/null +++ b/go/res/values-fr/strings.xml @@ -0,0 +1,26 @@ + + + + + "Appui prolongé pour sélectionner raccourci." + "Appuyez 2 fois et maintenez pression pour sélectionner raccourci ou utilisez actions personnalisées." + "Raccourcis" + "Raccourcis %1$s" + diff --git a/go/res/values-gl/strings.xml b/go/res/values-gl/strings.xml new file mode 100644 index 0000000000..31621d5c2c --- /dev/null +++ b/go/res/values-gl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén premido un atallo para seleccionalo." + "Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas." + "Atallos" + "Atallos da aplicación %1$s" + diff --git a/go/res/values-gu/strings.xml b/go/res/values-gu/strings.xml new file mode 100644 index 0000000000..bdb549ff7f --- /dev/null +++ b/go/res/values-gu/strings.xml @@ -0,0 +1,26 @@ + + + + + "એક શૉર્ટકટ ચૂંટવા ટૅપ કરી રાખો." + "એક શૉર્ટકટ ચૂંટવા અથવા કોઈ કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટૅપ કરી રાખો." + "શૉર્ટકટ" + "%1$s શૉર્ટકટ" + diff --git a/go/res/values-hi/strings.xml b/go/res/values-hi/strings.xml new file mode 100644 index 0000000000..bc057760fb --- /dev/null +++ b/go/res/values-hi/strings.xml @@ -0,0 +1,26 @@ + + + + + "शॉर्टकट चुनने के लिए छूकर रखें." + "शॉर्टकट चुनने के लिए डबल टैप करके रखें या कस्टम कार्रवाइयों का उपयोग करें." + "शॉर्टकट" + "%1$s शॉर्टकट" + diff --git a/go/res/values-hu/strings.xml b/go/res/values-hu/strings.xml new file mode 100644 index 0000000000..369c22745a --- /dev/null +++ b/go/res/values-hu/strings.xml @@ -0,0 +1,26 @@ + + + + + "Felvételhez tartsa nyomva a parancsikont." + "Parancsikon felvételéhez koppintson rá duplán és tartsa nyomva, vagy használjon egyéni műveleteket." + "Parancsikonok" + "%1$s-parancsikonok" + diff --git a/go/res/values-hy/strings.xml b/go/res/values-hy/strings.xml new file mode 100644 index 0000000000..4747f6df35 --- /dev/null +++ b/go/res/values-hy/strings.xml @@ -0,0 +1,26 @@ + + + + + "Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար։" + "Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար կամ օգտվեք հարմարեցրած գործողություններից:" + "Դյուրանցումներ" + "%1$s դյուրանցումներ" + diff --git a/go/res/values-is/strings.xml b/go/res/values-is/strings.xml new file mode 100644 index 0000000000..b8bb923746 --- /dev/null +++ b/go/res/values-is/strings.xml @@ -0,0 +1,26 @@ + + + + + "Haltu fingri á flýtileið til að grípa hana." + "Ýttu tvisvar og haltu fingri á flýtileið til að grípa hana eða notaðu sérsniðnar aðgerðir." + "Flýtileiðir" + "%1$s flýtileiðir" + diff --git a/go/res/values-iw/strings.xml b/go/res/values-iw/strings.xml new file mode 100644 index 0000000000..f541d4d316 --- /dev/null +++ b/go/res/values-iw/strings.xml @@ -0,0 +1,26 @@ + + + + + "כדי להוסיף קיצור דרך, מקישים עליו פעמיים ומחזיקים." + "כדי להוסיף קיצור דרך או להשתמש בפעולות מותאמות אישית, מקישים על קיצור הדרך פעמיים ומחזיקים." + "קיצורי דרך" + "קיצורי דרך לאפליקציה %1$s" + diff --git a/go/res/values-ka/strings.xml b/go/res/values-ka/strings.xml new file mode 100644 index 0000000000..1b46534788 --- /dev/null +++ b/go/res/values-ka/strings.xml @@ -0,0 +1,26 @@ + + + + + "შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად." + "ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად." + "მალსახმობები" + "%1$s-ის მალსახმობები" + diff --git a/go/res/values-kk/strings.xml b/go/res/values-kk/strings.xml new file mode 100644 index 0000000000..e909818afc --- /dev/null +++ b/go/res/values-kk/strings.xml @@ -0,0 +1,26 @@ + + + + + "Таңбашаны таңдау үшін оны түртіп, ұстап тұрыңыз." + "Таңбашаны таңдау немесе арнаулы әрекеттерді пайдалану үшін екі рет түртіп, ұстап тұрыңыз." + "Таңбашалар" + "%1$s таңбаша" + diff --git a/go/res/values-km/strings.xml b/go/res/values-km/strings.xml new file mode 100644 index 0000000000..40082a4b9d --- /dev/null +++ b/go/res/values-km/strings.xml @@ -0,0 +1,26 @@ + + + + + "ប៉ះ ហើយចុចឲ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ។" + "ប៉ះពីរដង ហើយចុចឱ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។" + "ផ្លូវកាត់" + "ផ្លូវកាត់សម្រាប់ %1$s" + diff --git a/go/res/values-kn/strings.xml b/go/res/values-kn/strings.xml new file mode 100644 index 0000000000..9c121fd294 --- /dev/null +++ b/go/res/values-kn/strings.xml @@ -0,0 +1,26 @@ + + + + + "ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ." + "ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ." + "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು" + "%1$s ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು" + diff --git a/go/res/values-ko/strings.xml b/go/res/values-ko/strings.xml new file mode 100644 index 0000000000..60f925e36b --- /dev/null +++ b/go/res/values-ko/strings.xml @@ -0,0 +1,26 @@ + + + + + "바로가기를 선택하려면 길게 터치하세요." + "바로가기를 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다." + "바로가기" + "%1$s 바로가기" + diff --git a/go/res/values-ky/strings.xml b/go/res/values-ky/strings.xml new file mode 100644 index 0000000000..4c7e973cef --- /dev/null +++ b/go/res/values-ky/strings.xml @@ -0,0 +1,26 @@ + + + + + "Кыска жолду тандоо үчүн басып туруңуз." + "Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз." + "Кыска жолдор" + "%1$s кыска жол" + diff --git a/go/res/values-lo/strings.xml b/go/res/values-lo/strings.xml new file mode 100644 index 0000000000..7864884acc --- /dev/null +++ b/go/res/values-lo/strings.xml @@ -0,0 +1,26 @@ + + + + + "ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ." + "ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ." + "ປຸ່ມລັດ" + "ປຸ່ມລັດ %1$s" + diff --git a/go/res/values-lv/strings.xml b/go/res/values-lv/strings.xml new file mode 100644 index 0000000000..04315ebaa0 --- /dev/null +++ b/go/res/values-lv/strings.xml @@ -0,0 +1,26 @@ + + + + + "Lai izvēlētos saīsni, pieskarieties un turiet to." + "Lai atlasītu saīsni, veiciet dubultskārienu uz tās un turiet to vai arī veiciet pielāgotas darbības." + "Saīsnes" + "Lietotnes %1$s saīsnes" + diff --git a/go/res/values-mk/strings.xml b/go/res/values-mk/strings.xml new file mode 100644 index 0000000000..52d66b5e28 --- /dev/null +++ b/go/res/values-mk/strings.xml @@ -0,0 +1,26 @@ + + + + + "Допрете двапати и задржете за да изберете кратенка." + "Допрете двапати и задржете за да изберете кратенка или да користите приспособени дејства." + "Кратенки" + "Кратенки за %1$s" + diff --git a/go/res/values-ml/strings.xml b/go/res/values-ml/strings.xml new file mode 100644 index 0000000000..b3c12e16e1 --- /dev/null +++ b/go/res/values-ml/strings.xml @@ -0,0 +1,26 @@ + + + + + "ഒരു കുറുക്കുവഴി ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക." + "ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക." + "കുറുക്കുവഴികൾ" + "%1$s കുറുക്കുവഴികൾ" + diff --git a/go/res/values-mn/strings.xml b/go/res/values-mn/strings.xml new file mode 100644 index 0000000000..c89dfd1bdc --- /dev/null +++ b/go/res/values-mn/strings.xml @@ -0,0 +1,26 @@ + + + + + "Товчлол авах бол удаан дарна уу." + "Товчлол авах болон тохируулсан үйлдлийг ашиглахын тулд хоёр товшоод хүлээнэ үү." + "Товчлол" + "%1$s-н товчлол" + diff --git a/go/res/values-mr/strings.xml b/go/res/values-mr/strings.xml new file mode 100644 index 0000000000..2c767b4f19 --- /dev/null +++ b/go/res/values-mr/strings.xml @@ -0,0 +1,26 @@ + + + + + "शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." + "शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा." + "शॉर्टकट" + "%1$s शॉर्टकट" + diff --git a/go/res/values-ms/strings.xml b/go/res/values-ms/strings.xml new file mode 100644 index 0000000000..42add9a197 --- /dev/null +++ b/go/res/values-ms/strings.xml @@ -0,0 +1,26 @@ + + + + + "Sentuh & tahan untuk mengambil pintasan." + "Ketik dua kali & tahan untuk mengambil pintasan atau menggunakan tindakan tersuai." + "Pintasan" + "Pintasan %1$s" + diff --git a/go/res/values-my/strings.xml b/go/res/values-my/strings.xml new file mode 100644 index 0000000000..5784df63bf --- /dev/null +++ b/go/res/values-my/strings.xml @@ -0,0 +1,26 @@ + + + + + "လက်ကွက်ဖြတ်လမ်းတစ်ခုကို ရွေးရန် ထိပြီး ဖိထားပါ" + "လက်ကွက်ဖြတ်လမ်းကို ရွေးရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန်နှစ်ချက်တို့ပြီး ဖိထားပါ။" + "ဖြတ်လမ်းလင့်ခ်များ" + "%1$s ဖြတ်လမ်းလင့်ခ်များ" + diff --git a/go/res/values-nb/strings.xml b/go/res/values-nb/strings.xml new file mode 100644 index 0000000000..2a5ffb6e7c --- /dev/null +++ b/go/res/values-nb/strings.xml @@ -0,0 +1,26 @@ + + + + + "Trykk og hold for å velge en snarvei." + "Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger." + "Snarveier" + "%1$s-snarveier" + diff --git a/go/res/values-ne/strings.xml b/go/res/values-ne/strings.xml new file mode 100644 index 0000000000..0be0375f3a --- /dev/null +++ b/go/res/values-ne/strings.xml @@ -0,0 +1,26 @@ + + + + + "कुनै सटकर्ट छनौट गर्न छोइराख्नुहोस्।" + "कुनै सर्टकट छनौट गर्न दुईपटक ट्याप गरेर होल्ड गर्नुहोस् वा रोजेका कारबाहीहरू प्रयोग गर्नुहोस्।" + "सर्टकटहरू" + "%1$s सर्टकटहरू" + diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml new file mode 100644 index 0000000000..f3982ab71b --- /dev/null +++ b/go/res/values-pa/strings.xml @@ -0,0 +1,26 @@ + + + + + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਡਬਲ-ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" + "ਸ਼ਾਰਟਕੱਟ" + "%1$s ਸ਼ਾਰਟਕੱਟ" + diff --git a/go/res/values-ro/strings.xml b/go/res/values-ro/strings.xml new file mode 100644 index 0000000000..75d1796b57 --- /dev/null +++ b/go/res/values-ro/strings.xml @@ -0,0 +1,26 @@ + + + + + "Atingeți și țineți apăsat pentru a selecta o comandă rapidă." + "Atingeți de două ori și țineți apăsat pentru comandă rapidă sau folosiți acțiuni personalizate." + "Comenzi rapide" + "Comenzi rapide pentru %1$s" + diff --git a/go/res/values-ru/strings.xml b/go/res/values-ru/strings.xml new file mode 100644 index 0000000000..9c5c8cd340 --- /dev/null +++ b/go/res/values-ru/strings.xml @@ -0,0 +1,26 @@ + + + + + "Чтобы выбрать ярлык, нажмите на него и удерживайте." + "Чтобы выбрать ярлык или использовать специальные действия, нажмите на него дважды и не отпускайте." + "Ярлыки" + "%1$s: ярлыки" + diff --git a/go/res/values-si/strings.xml b/go/res/values-si/strings.xml new file mode 100644 index 0000000000..4b25c90b0b --- /dev/null +++ b/go/res/values-si/strings.xml @@ -0,0 +1,26 @@ + + + + + "කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න." + "විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න." + "කෙටි මං" + "කෙටි මං %1$s" + diff --git a/go/res/values-sq/strings.xml b/go/res/values-sq/strings.xml new file mode 100644 index 0000000000..bb74db6b55 --- /dev/null +++ b/go/res/values-sq/strings.xml @@ -0,0 +1,26 @@ + + + + + "Prek dhe mbaj prekur për të zgjedhur një shkurtore." + "Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara." + "Shkurtoret" + "%1$s shkurtore" + diff --git a/go/res/values-ta/strings.xml b/go/res/values-ta/strings.xml new file mode 100644 index 0000000000..50059b65f9 --- /dev/null +++ b/go/res/values-ta/strings.xml @@ -0,0 +1,26 @@ + + + + + "குறுக்குவழியைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்." + "குறுக்குவழியை சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்." + "குறுக்குவழிகள்" + "%1$s குறுக்குவழிகள்" + diff --git a/go/res/values-te/strings.xml b/go/res/values-te/strings.xml new file mode 100644 index 0000000000..0bdf743342 --- /dev/null +++ b/go/res/values-te/strings.xml @@ -0,0 +1,26 @@ + + + + + "సత్వరమార్గాన్ని ఎంచుకోవడానికి తాకి & నొక్కి ఉంచండి." + "సత్వరమార్గాన్ని ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి &ఉంచండి." + "సత్వరమార్గాలు" + "%1$s సత్వరమార్గాలు" + diff --git a/go/res/values-th/strings.xml b/go/res/values-th/strings.xml new file mode 100644 index 0000000000..e73d89fb0e --- /dev/null +++ b/go/res/values-th/strings.xml @@ -0,0 +1,26 @@ + + + + + "แตะค้างไว้เพื่อเลือกทางลัด" + "แตะสองครั้งค้างไว้เพื่อเลือกทางลัดหรือใช้การกระทำที่กำหนดเอง" + "ทางลัด" + "ทางลัด %1$s" + diff --git a/go/res/values-tl/strings.xml b/go/res/values-tl/strings.xml new file mode 100644 index 0000000000..8f44ec5742 --- /dev/null +++ b/go/res/values-tl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Pindutin nang matagal upang kumuha ng shortcut." + "I-double tap nang matagal upang kumuha ng shortcut o gumamit ng mga custom na pagkilos." + "Mga Shortcut" + "Mga shortcut sa %1$s" + diff --git a/go/res/values-ur/strings.xml b/go/res/values-ur/strings.xml new file mode 100644 index 0000000000..46bd823d8e --- /dev/null +++ b/go/res/values-ur/strings.xml @@ -0,0 +1,26 @@ + + + + + "کوئی شارٹ کٹ منتخب کرنے کیلئے ٹچ کریں اور دبائے رکھیں۔" + "کوئی شارٹ کٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور دبائے رکھیں۔" + "شارٹ کٹس" + "%1$s شارٹ کٹس" + diff --git a/go/res/values-uz/strings.xml b/go/res/values-uz/strings.xml new file mode 100644 index 0000000000..318bc15722 --- /dev/null +++ b/go/res/values-uz/strings.xml @@ -0,0 +1,26 @@ + + + + + "Yorliqni tanlab olish uchun bosib turing." + "Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning." + "Yorliqlar" + "%1$s ilovasi yorliqlari" + diff --git a/go/res/values-vi/strings.xml b/go/res/values-vi/strings.xml new file mode 100644 index 0000000000..1197619c57 --- /dev/null +++ b/go/res/values-vi/strings.xml @@ -0,0 +1,26 @@ + + + + + "Chạm và giữ để chọn lối tắt." + "Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng hành động tùy chỉnh." + "Lối tắt" + "Lối tắt %1$s" + diff --git a/go/res/values-zh-rCN/strings.xml b/go/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..57351d37f5 --- /dev/null +++ b/go/res/values-zh-rCN/strings.xml @@ -0,0 +1,26 @@ + + + + + "触摸并按住快捷方式即可选择快捷方式。" + "点按两次并按住快捷方式即可选择快捷方式,您也可以使用自定义操作。" + "快捷方式" + "%1$s快捷方式" + diff --git a/go/res/values-zh-rHK/strings.xml b/go/res/values-zh-rHK/strings.xml new file mode 100644 index 0000000000..dea7749f68 --- /dev/null +++ b/go/res/values-zh-rHK/strings.xml @@ -0,0 +1,26 @@ + + + + + "按住捷徑即可選取捷徑。" + "扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。" + "捷徑" + "%1$s 捷徑" + diff --git a/go/res/values-zh-rTW/strings.xml b/go/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..07ae2ed5b4 --- /dev/null +++ b/go/res/values-zh-rTW/strings.xml @@ -0,0 +1,26 @@ + + + + + "按住捷徑即可選取。" + "輕觸兩下並按住捷徑即可選取,你也可以使用自訂動作。" + "捷徑" + "「%1$s」捷徑" + diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml new file mode 100644 index 0000000000..094fc745c4 --- /dev/null +++ b/go/res/xml/device_profiles.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/go/src_flags/com/android/launcher3/config/FeatureFlags.java b/go/src_flags/com/android/launcher3/config/FeatureFlags.java index 5bb2ac9a2e..b11bb7c6aa 100644 --- a/go/src_flags/com/android/launcher3/config/FeatureFlags.java +++ b/go/src_flags/com/android/launcher3/config/FeatureFlags.java @@ -25,4 +25,5 @@ public final class FeatureFlags extends BaseFlags { // Features to control Launcher3Go behavior public static final boolean GO_DISABLE_WIDGETS = true; + public static final boolean LAUNCHER3_SPRING_ICONS = false; } diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml index 957c331741..6a68f84e98 100644 --- a/res/layout-land/all_apps_fast_scroller.xml +++ b/res/layout-land/all_apps_fast_scroller.xml @@ -22,7 +22,8 @@ style="@style/FastScrollerPopup" android:layout_alignParentEnd="true" android:layout_alignTop="@+id/apps_list_view" - android:layout_marginEnd="-5dp" /> + android:layout_marginTop="-5dp" + android:layout_marginEnd="-45dp" /> \ No newline at end of file diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index dab074391d..ac440fc01e 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -42,12 +42,7 @@ android:layout_gravity="center" launcher:pageIndicator="@id/page_indicator" /> - + - + - + + + + \ No newline at end of file diff --git a/res/layout/notification_pref_warning.xml b/res/layout/notification_pref_warning.xml index 795699e99d..100fac5e41 100644 --- a/res/layout/notification_pref_warning.xml +++ b/res/layout/notification_pref_warning.xml @@ -17,8 +17,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="48dp" android:layout_height="match_parent" - android:background="?android:attr/selectableItemBackgroundBorderless" - android:contentDescription="@string/title_missing_notification_access" + android:importantForAccessibility="no" android:scaleType="center" android:src="@drawable/ic_warning" android:tint="?android:attr/textColorSecondary" /> diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index a8791b7353..18f87bcf33 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -35,9 +35,9 @@ "%2$d hündürlük %1$d enində" "Manual olaraq yerləşdirmək üçün toxunaraq basıb saxlayın" "Avtomatik əlavə edin" - "Tətbiq Axtarın" - "Tətbiqlər endirilir..." - "\"%1$s\" sorğusuna uyğun Tətbiqlər tapılmadı" + "Tətbiqləri axtarın" + "Tətbiqlər yüklənir…" + "%1$s sorğusuna uyğun tətbiq tapılmadı" "Daha çox tətbiq üçün axtarış edin" "Bildirişlər" "Bu Əsas ekranda boş yer yoxdur." @@ -79,17 +79,18 @@ "Bildiriş nişanı" "Aktiv" "Deaktiv" + "Bildiriş girişi tələb edilir" + "Bildiriş Nöqtələrini göstərmək üçün %1$s bildirişlərini aktiv edin" + "Ayarları dəyişin" "Əsas ekrana ikona əlavə edin" "Yeni tətbiqlər üçün" - - + "İkona formasını dəyişin" "Sistem defoltu istifadə edin" "Kvadrat" "Kənarları dairəvi kvadrat" "Çevrə" "Gözyaşı damlası" - - + "İkona formasına etdiyiniz dəyişikliklər tətbiq edilir" "Naməlum" "Yığışdır" "Axtarış" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 615fd57041..4b4e95f1fc 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -31,9 +31,11 @@ "Дакраніцеся двойчы і ўтрымлівайце, каб выбраць віджэт або выкарыстоўваць карыстальніцкія дзеянні." "%1$d × %2$d" "Шырына: %1$d, вышыня: %2$d" - "Пошук у Праграмах" - "Ідзе загрузка праграм…" - "Праграм, якія адпавядаюць запыту \"%1$s\", не знойдзена" + "Каб размясціць уручную, дакраніцеся і ўтрымлівайце" + "Дадаць аўтаматычна" + "Пошук праграм" + "Праграмы загружаюцца…" + "Праграм, якія адпавядаюць запыту \"%1$s\", не знойдзена" "Шукаць іншыя праграмы" "На гэтым Галоўным экране больш няма месца." "У латку \"Абранае\" больш няма месца" @@ -74,17 +76,18 @@ "Значкі апавяшчэнняў" "Уключана" "Выключана" + "Патрабуецца доступ да апавяшчэнняў" + "Каб паказваліся значкі апавяшчэнняў, уключыце апавяшчэнні праграм для %1$s" + "Змяніць налады" "Дадаць значок на Галоўны экран" "Для новых праграм" - - + "Змяніць форму значка" "Выкарыстоўваць стандартныя формы" "Квадрат" "Прамавугольнік са скругленымі вугламі" "Круг" "Сляза" - - + "Змены формы значка прымяняюцца" "Невядома" "Выдаліць" "Шукаць" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index d4c5210593..471602f47d 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -27,7 +27,7 @@ "ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে" "সুরক্ষিত মোডে উইজেট নিষ্ক্রিয় থাকে" "শর্টকাটগুলি অনুপলব্ধ" - "হোম স্ক্রীন" + "হোম স্ক্রিন" "কাস্টম অ্যাকশন" "একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷" "কোনো উইজেট বেছে নিতে দুবার-আলতো চেপে ধরে থাকুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন৷" @@ -35,9 +35,9 @@ "%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ" "নিজে যোগ করতে টাচ করে ধরে রাখুন" "স্বয়ংক্রিয়ভাবে যোগ করুন" - "অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন" - "অ্যাপ্লিকেশানগুলি লোড হচ্ছে..." - "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি" + "অ্যাপ অনুসন্ধান করুন" + "অ্যাপ লোড হচ্ছে…" + "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি" "আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন" "বিজ্ঞপ্তি" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" @@ -60,7 +60,7 @@ "নামবিহীন ফোল্ডার" "%1$s অক্ষম করা হয়েছে" "%2$dটির মধ্যে %1$dটি পৃষ্ঠা" - "%2$dটির %1$d নম্বর হোম স্ক্রীন" + "%2$dটির %1$d নম্বর হোম স্ক্রিন" "নতুন হোম স্ক্রীনের পৃষ্ঠা" "ফোল্ডার খোলা হয়েছে, %1$d বাই %2$d" "ফোল্ডার বন্ধ করতে আলতো চাপ দিন" @@ -79,6 +79,9 @@ "বিজ্ঞপ্তি ডট" "চালু হয়েছে" "বন্ধ আছে" + "বিজ্ঞপ্তিতে অ্যাক্সেস প্রয়োজন" + "বিজ্ঞপ্তির ডটগুলি দেখানোর জন্য, %1$s এর অ্যাপ বিজ্ঞপ্তি চালু করুন" + "সেটিংস পরিবর্তন করুন" "হোম স্ক্রিনে আইকন যোগ করুন" "নতুন অ্যাপ্লিকেশানগুলির জন্যে" "আইকনের আকৃতি পরিবর্তন করুন" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index d91106bb20..49c862a8cd 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -35,9 +35,9 @@ "Širina %1$d, visina %2$d" "Dodirnite i držite da postavite ručno" "Dodaj automatski" - "Pretraži aplikacije" - "Aplikacije se učitavaju…" - "Nije pronađena nijedna aplikacija koja odgovara upitu \"%1$s\"" + "Pretražite aplikacije" + "Aplikacije se učitavaju…" + "Nije pronađena nijedna aplikacija za upit \"%1$s\"" "Pretraži više aplikacija" "Obavještenja" "Na ovom početnom ekranu nema više prostora." @@ -79,17 +79,18 @@ "Tačke za obavještenja" "Uključeno" "Isključeno" + "Potreban je pristup obavještenjima" + "Za prikaz tačaka obavještenja, uključite obavještenja za aplikacije za aplikaciju %1$s" + "Promijeni postavke" "Dodajte ikonu na početni ekran" "Za nove aplikacije" - - + "Promjena oblika ikona" "Koristite sistemski zadano" "Kvadrat" "Zaobljeni kvadrat" "Krug" "Suza" - - + "Primjenjivanje promjena oblika ikona" "Nepoznato" "Ukloni" "Traži" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index abc44c7142..e6dee3c3b3 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -76,11 +76,11 @@ "Povolit otáčení plochy" "Při otočení telefonu" "Aktuální nastavení displeje neumožňuje otáčení" - "Puntíky s oznámeními" + "Puntíky s oznámením" "Zapnuto" "Vypnuto" "Je třeba udělit přístup k oznámením" - "Chcete-li zobrazovat puntíky s oznámeními, zapněte oznámení z aplikace %1$s" + "Chcete-li zobrazovat puntíky s oznámením, zapněte oznámení z aplikace %1$s" "Změnit nastavení" "Přidat ikonu na plochu" "Pro nové aplikace" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 6eca89e5ce..4c90bdc5e3 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -31,9 +31,11 @@ "Topeltpuudutage ja hoidke vidina valimiseks või kohandatud toimingute kasutamiseks." "%1$d × %2$d" "%1$d lai ja %2$d kõrge" - "Otsige rakendustest" - "Rakenduste laadimine ..." - "Päringule „%1$s” ei vastanud ükski rakendus" + "Puudutage pikalt, et käsitsi asetada" + "Lisa automaatselt" + "Otsige rakendusi" + "Rakenduste laadimine …" + "Päringule „%1$s” ei vastanud ükski rakendus" "Otsi rohkem rakendusi" "Sellel avaekraanil pole enam ruumi." "Salves Lemmikud pole rohkem ruumi" @@ -74,17 +76,18 @@ "Märguandetäpid" "Sees" "Väljas" + "Vaja on juurdepääsu märguannetele" + "Märguandetäppide kuvamiseks lülitage sisse rakenduse %1$s märguanded" + "Seadete muutmine" "Lisa ikoon avaekraanile" "Uute rakenduste puhul" - - + "Ikooni kuju muutmine" "Kasuta süsteemi vaikeseadet" "Ruut" "Ümarate nurkadega ruut" "Ring" "Tilk" - - + "Ikooni kuju muudatuste rakendamine" "Teadmata" "Eemalda" "Otsing" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 59fc221566..b6082fadab 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -35,9 +35,9 @@ "%1$d zabal eta %2$d luze" "Eduki sakatuta eskuz gehitzeko" "Gehitu automatikoki" - "Bilatu aplikazioetan" - "Aplikazioak kargatzen…" - "Ez da aurkitu \"%1$s\" bilaketarekin bat datorren aplikaziorik" + "Bilatu aplikazioetan" + "Aplikazioak kargatzen…" + "Ez da aurkitu \"%1$s\" bilaketaren emaitzarik" "Bilatu aplikazio gehiago" "Jakinarazpenak" "Hasierako pantaila honetan ez dago toki gehiago." @@ -79,17 +79,18 @@ "Jakinarazteko biribiltxoak" "Aktibatuta" "Desaktibatuta" + "Jakinarazpenetarako sarbidea behar da" + "Jakinarazteko biribiltxoak ikusteko, aktibatu %1$s aplikazioaren jakinarazpenak" + "Aldatu ezarpenak" "Gehitu ikonoa hasierako pantailan" "Aplikazio berrietan" - - + "Aldatu ikonoaren forma" "Erabili sistemaren balio lehenetsiak" "Karratua" "Ertz biribilduko karratua" "Zirkulua" "Malkoa" - - + "Ikonoaren forman egindako aldaketak aplikatzen" "Ezezaguna" "Kendu" "Bilatu" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 9211ba1a15..d55514d1e9 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -35,9 +35,9 @@ "%1$d de largo por %2$d de alto" "Mantén premido o elemento para colocalo manualmente" "Engadir automaticamente" - "Aplicacións de busca" - "Cargando aplicacións..." - "Non se atoparon aplicacións que coincidan con \"%1$s\"" + "Buscar aplicacións" + "Cargando aplicacións…" + "Non se atoparon aplicacións que coincidan con \"%1$s\"" "Buscar máis aplicacións" "Notificacións" "Non hai máis espazo nesta pantalla de inicio." @@ -79,17 +79,18 @@ "Puntos de notificacións" "Activado" "Desactivado" + "Necesítase acceso ás notificacións" + "Para que se mostren os puntos de notificacións, activa as notificacións da aplicación %1$s" + "Cambiar configuración" "Engadir icona á pantalla de inicio" "Para novas aplicacións" - - + "Cambiar forma das iconas" "Usar valores predeterminados do sistema" "Cadrado" "Cadrado de bordos redondeados" "Círculo" "Bágoa" - - + "Aplicando cambios na forma das iconas" "Descoñecido" "Eliminar" "Buscar" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index b491e42984..7096a3ee0a 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -29,15 +29,15 @@ "શૉર્ટકટ ઉપલબ્ધ નથી" "હોમ સ્ક્રીન" "કસ્ટમ ક્રિયાઓ" - "વિજેટ ચૂંટવા માટે ટચ કરો અને પકડી રાખો." + "વિજેટ ચૂંટવા માટે સ્પર્શ કરો અને પકડી રાખો." "વિજેટ ચૂંટવા અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટેપ કરો અને પકડી રાખો." "%1$d × %2$d" "%1$d પહોળાઈ X %2$d ઊંચાઈ" "મેન્યુઅલી મૂકવા માટે ટચ કરી દબાવી રાખો" "આપમેળે ઉમેરો" - "શોધ ઍપ્લિકેશનો" - "ઍપ્લિકેશનો લોડ કરી રહ્યું છે…" - "\"%1$s\" થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" + "શોધ ઍપ્લિકેશનો" + "ઍપ્લિકેશનો લોડ કરી રહ્યું છે…" + "\"%1$s\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" "વધુ ઍપ્લિકેશનો શોધો" "સૂચનાઓ" "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." @@ -72,13 +72,16 @@ "વૉલપેપર્સ" "હોમ સેટિંગ્સ" "તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ" - "વિહંગાવલોકન" + "ઝલક" "હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો" "જ્યારે ફોન ફેરવવામાં આવે ત્યારે" "વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી" "સૂચના બિંદુઓ" "ચાલુ" "બંધ" + "નોટિફિકેશનનો ઍક્સેસની જરૂરી છે" + "નોટિફિકેશન માટેનું ચિહ્ન બતાવવા હેતુ, %1$s માટેની ઍપ્લિકેશન નોટિફિકેશન ચાલુ કરો" + "સેટિંગ્સ બદલો" "હોમ સ્ક્રીન પર આઇકન ઉમેરો" "નવી ઍપ્લિકેશનો માટે" "આઇકનનો આકાર બદલો" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 02c470ba07..f1ee0f2366 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -72,7 +72,7 @@ "वॉलपेपर" "होम सेटिंग" "आपके व्यवस्थापक द्वारा अक्षम" - "अवलोकन" + "खास जानकारी" "होमस्क्रीन घुमाने की अनुमति दें" "फ़ोन घुुमाए जाने पर" "वर्तमान प्रदर्शन सेटिंग घुमाने की अनुमति नहीं देती" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 1646f9e33e..2b55e966a4 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -35,9 +35,9 @@ "Լայնությունը՝ %1$d, բարձրությունը՝ %2$d" "Հպեք և պահեք՝ ձեռքով տեղադրելու համար" "Ավելացնել ավտոմատ կերպով" - "Հավելվածների որոնում" - "Հավելվածների բեռնում…" - %1$s» հարցմանը համապատասխանող հավելվածներ չեն գտնվել" + "Որոնել հավելվածներ" + "Հավելվածների բեռնում…" + %1$s» հարցմանը համապատասխանող հավելվածներ չեն գտնվել" "Որոնել այլ հավելվածներ" "Ծանուցումներ" "Այլևս տեղ չկա այս հիմնական էկրանին:" @@ -79,17 +79,18 @@ "Ծանուցման կետեր" "Միացված է" "Անջատված է" + "Անհրաժեշտ է ծանուցման թույլտվություն" + "Ծանուցման կետերը ցուցադրելու համար միացրեք հավելվածի ծանուցումները %1$s-ի համար" + "Փոխել կարգավորումները" "Ավելացնել պատկերակը Հիմնական էկրանին" "Նոր հավելվածների համար" - - + "Փոխել պատկերակների տեսքը" "Օգտագործել համակարգի կանխադրված կարգավորումը" "Քառակուսի" "Քառանկյուն" "Օղակ" "Արցունքաձև" - - + "Պատկերակների տեսքի փոփոխությունները կիրառվում են" "Անհայտ է" "Հեռացնել" "Գտնել" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 853cd3b41c..cf178a56ad 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -35,9 +35,9 @@ "%1$d á breidd og %2$d á hæð" "Haltu inni til að staðsetja handvirkt" "Bæta sjálfkrafa við" - "Leita í forritum" - "Hleður forrit…" - "Ekki fundust forrit sem samsvara „%1$s“" + "Leita í forritum" + "Hleður forrit…" + "Ekki fundust forrit sem samsvara „%1$s“" "Leita að fleiri forritum" "Tilkynningar" "Ekki meira pláss á þessum heimaskjá." @@ -79,17 +79,18 @@ "Tilkynningapunktar" "Kveikt" "Slökkt" + "Aðgangs að tilkynningum er krafist" + "Til að sýna tilkynningarpunkta skaltu kveikja á forritstilkynningum fyrir %1$s" + "Breyta stillingum" "Bæta tákni á heimaskjáinn" "Fyrir ný forrit" - - + "Breyta formi tákns" "Nota sjálfgildi kerfis" "Ferningur" "Ferhringur" "Hringur" "Dropi" - - + "Breytir formi tákns" "Óþekkt" "Fjarlægja" "Leita" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 2634cdf431..7e8b46ce88 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -35,9 +35,9 @@ "სიგრძე: %1$d, სიგანე: %2$d" "ხანგრძლივად შეეხეთ ხელით განსათავსებლად" "ავტომატურად დამატება" - "აპების ძიება" - "აპები იტვირთება..." - "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" + "აპების ძიება" + "აპები იტვირთება…" + "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" "მეტი აპის პოვნა" "შეტყობინებები" "ამ მთავარ ეკრანზე ადგილი აღარ არის." @@ -79,17 +79,18 @@ "შეტყობინების ნიშნულები" "ჩართული" "გამორთული" + "საჭიროა შეტყობინებებზე წვდომა" + "შეტყობინებათა ნიშნულების საჩვენებლად, ჩართეთ აპის შეტყობინებები %1$s-ისთვის" + "პარამეტრების შეცვლა" "ხატულას მთავარ ეკრანზე დამატება" "ახალი აპებისთვის" - - + "ხატულას ფორმის შეცვლა" "ნაგულისხმევი სისტემური პარამეტრების გამოყენება" "კვადრატი" "წრეკუთხედი" "წრე" "წვეთი" - - + "მიმდინარეობს ხატულას ფორმის ცვლილებების მიყენება" "უცნობი" "ამოშლა" "ძიება" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 66766b6dba..50ed9aa680 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -35,9 +35,9 @@ "Ені: %1$d, биіктігі: %2$d" "Қолмен қою үшін басып тұрыңыз" "Автоматты енгізу" - "Қолданбаларды іздеу" - "Қолданбалар жүктелуде…" - %1$s» сұрауына сәйкес келетін қолданбалар жоқ" + "Қолданбаларды іздеу" + "Қолданбалар жүктелуде…" + "\"%1$s\" сұрауына сәйкес келетін қолданбалар жоқ" "Қосымша қолданбалар іздеу" "Хабарландырулар" "Бұл Негізгі экранда орын қалмады." @@ -79,17 +79,18 @@ "Хабарландыру белгілері" "Қосулы" "Өшірулі" + "Хабарландыруға кіру рұқсаты қажет" + "Хабарландыру белгілерін көрсету үшін %1$s қолданбасының қолданба хабарландыруларын қосыңыз" + "Параметрлерді өзгерту" "Негізгі экранға белгіше енгізу" "Жаңа қолданбаларға арналған" - - + "Белгіше пішінін өзгерту" "Жүйенің әдепкі параметрін пайдалану" "Шаршы" "Жұмыр төртбұрыш" "Шеңбер" "Тамшы" - - + "Белгіше пішіні өзгерістері күшіне енуде" "Белгісіз" "Алып тастау" "Іздеу" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index ee44302eaf..7028f7e3ae 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -35,9 +35,9 @@ "ទទឺង %1$d គុណនឹងកម្ពស់ %2$d" "ចុច​ឲ្យជាប់​ដើម្បី​បញ្ចូលវា​ដោយផ្ទាល់" "បញ្ចូល​ដោយ​ស្វ័យ​ប្រវត្តិ" - "ស្វែងរកកម្មវិធី" - "កំពុងដំណើរការកម្មវិធី..." - "គ្មានកម្មវិធីដែលត្រូវជាមួយ \"%1$s\" ទេ" + "ស្វែងរក​កម្មវិធី" + "កំពុងផ្ទុកកម្មវិធី…" + "រកមិនឃើញកម្មវិធី​ដែលត្រូវគ្នាជាមួយ \"%1$s\" ទេ" "ស្វែងរកកម្មវិធីច្រើនទៀត" "ការ​ជូនដំណឹង" "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" @@ -79,17 +79,18 @@ "ស្លាកជូនដំណឹង" "បើក" "បិទ" + "តម្រូវ​ឲ្យមាន​សិទ្ធិចូល​ប្រើប្រាស់​ការជូនដំណឹង" + "ដើម្បីបង្ហាញស្លាកជូនដំណឹង សូមបើកការជូនដំណឹងកម្មវិធីសម្រាប់ %1$s" + "ប្ដូរ​ការកំណត់" "បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម" "សម្រាប់កម្មវិធីថ្មី" - - + "ប្តូររូបរាងរូបតំណាង" "ប្រើលំនាំដើមរបស់ប្រព័ន្ធ" "ការ៉េ" "ការ៉េជ្រុងកោង" "រង្វង់" "តំណក់​ទឹកភ្នែក" - - + "កំពុងអនុវត្ត​ការប្តូររូបរាងរូបតំណាង" "មិន​ស្គាល់" "លុបចេញ" "ស្វែងរក" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 6c89b7bd13..9a31649be3 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -35,9 +35,9 @@ "%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ" "ಹಸ್ತಚಾಲಿತವಾಗಿ ಸೇರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ" "ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಿ" - "ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ" - "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..." - "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" + "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ" + "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..." + "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" "ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ" "ಅಧಿಸೂಚನೆಗಳು" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." @@ -79,6 +79,9 @@ "ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳು" "ಆನ್" "ಆಫ್" + "ಅಧಿಸೂಚನೆ ಪ್ರವೇಶ ಅಗತ್ಯವಿದೆ" + "ಅಧಿಸೂಚನೆ ಚುಕ್ಕೆಗಳನ್ನು ತೋರಿಸಲು, %1$s ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್‌ ಮಾಡಿ" + "ಸೆಟ್ಟಿಂಗ್‌‌ಗಳನ್ನು ಬದಲಾಯಿಸಿ" "ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ" "ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ" "ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index cc15f04479..85ba8be3c3 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -35,9 +35,9 @@ "Туурасы: %1$d, бийиктиги: %2$d" "Кол менен жайгаштыруу үчүн басып туруп, таштаңыз" "Автоматтык түрдө кошуу" - "Колдонмолорду издөө" - "Колдонмолор жүктөлүүдө…" - "\"%1$s\" дал келген колдонмолор табылган жок" + "Колдонмолорду издөө" + "Колдонмолор жүктөлүүдө…" + "\"%1$s\" сурамына дал келген колдонмолор табылган жок" "Көбүрөөк колдонмолорду издөө" "Эскертмелер" "Бул Үй экранында бош орун жок." @@ -79,17 +79,18 @@ "Эскертме белгилери" "Күйүк" "Өчүк" + "Эскертмелерге уруксат берилиши керек" + "Эскертме белгилерин көрсөтүү максатында, %1$s үчүн колдонмонун эскертмелерин күйгүзүү керек" + "Жөндөөлөрдү өзгөртүү" "Башкы экранга сүрөтчө кошуу" "Жаңы колдонмолор үчүн" - - + "Сүрөтчөнүн формасын өзгөртүү" "Тутум сушунтаган демейкисин колдонуу" "Чарчы" "Бурчтары жумуру төрт бурчтук" "Тегерек" "Тамчы" - - + "Өзгөртүлгөн сүрөтчөнүн формасы колдонулууда" "Белгисиз" "Алып салуу" "Издөө" diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml index 1b58e17754..92420a2f12 100644 --- a/res/values-land/dimens.xml +++ b/res/values-land/dimens.xml @@ -30,12 +30,24 @@ 4dp 0dp + 5.5dp + 2dp + + 50dp + 74dp + + + 0dp + + 50dp + 44dp 18dp + + 56dp + 32dp 6dp - 24dp - 32dp diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 0b294344e7..9a50c4b517 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -35,9 +35,9 @@ "ກວ້າງ %1$d ຄູນສູງ %2$d" "ແຕະຄ້າງໄວ້ເພື່ອວາງດ້ວຍຕົນເອງ" "ເພີ່ມໂດຍອັດຕະໂນມັດ" - "ຊອກຫາແອັບ" - "​ກຳ​ລັງ​ໂຫລດ​ແອັບ..." - "ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"%1$s\"" + "ຊອກຫາແອັບ" + "ກໍາລັງໂຫຼດແອັບ…" + "ບໍ່ພົບແອັບທີ່ກົງກັບ \"%1$s\"" "ຊອກຫາແອັບເພີ່ມເຕີມ" "ການແຈ້ງເຕືອນ" "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." @@ -79,17 +79,18 @@ "ຈຸດການແຈ້ງເຕືອນ" "ເປີດ" "ປິດ" + "ຕ້ອງໃຊ້ການເຂົ້າເຖິງການແຈ້ງເຕືອນ" + "ເພື່ອສະແດງຈຸດການແຈ້ງເຕືອນ, ໃຫ້ເປີດການແຈ້ງເຕືອນສຳລັບ %1$s" + "ບັນທຶກການຕັ້ງຄ່າ" "ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ" "ສຳລັບແອັບໃໝ່" - - + "ປ່ຽນຮູບຮ່າງໄອຄອນ" "ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ" "ສີ່ຫຼ່ຽມຈັດຕຸລັດ" "ສີ່ຫຼ່ຽມຂອບມົນ" "ວົງມົນ" "ນ້ຳຢອດ" - - + "ນຳໃຊ້ການປ່ຽນແປງຮູບຮ່າງໄອຄອນ" "​ບໍ່​ຮູ້​ຈັກ" "ລຶບ​" "ຊອກຫາ" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index abd1d35dde..7d95a23a6c 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -35,9 +35,9 @@ "%1$d широк на %2$d висок" "Допрете и задржете за рачно поставување" "Додај автоматски" - "Пребарување апликации" - "Се вчитуваат апликации…" - "Не се најдени апликации што одговараат на „%1$s“" + "Пребарувајте апликации" + "Се вчитуваат апликации…" + "Не се најдени апликации што одговараат на „%1$s“" "Пребарај други апликации" "Известувања" "Нема повеќе простор на овој екран на почетната страница." @@ -79,17 +79,18 @@ "Точки за известување" "Вклучено" "Исклучено" + "Потребен е пристап до известувањата" + "За да се прикажуваат „Точки за известување“, вклучете ги известувањата за апликацијата %1$s" + "Промени ги поставките" "Додајте икона на почетниот екран" "За нови апликации" - - + "Променете ја формата на иконата" "Користи ја стандардната поставка на системот" "Квадрат" "Заоблен квадрат" "Круг" "Солза" - - + "Се применуваат промените на формата на иконата" "Непознато" "Отстрани" "Барај" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index bad7e480a1..0cc7aa023c 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -35,9 +35,9 @@ "%1$d വീതിയും %2$d ഉയരവും" "സ്വമേധയാ സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക" "സ്വയമേവ ചേർക്കുക" - "ആപ്പുകളെ തിരയുക" - "ആപ്പുകൾ ലോഡുചെയ്യുന്നു..." - "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല" + "ആപ്പുകൾ തിരയുക" + "ആപ്പുകൾ ലോഡുചെയ്യുന്നു..." + "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല" "കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക" "അറിയിപ്പുകൾ" "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." @@ -72,13 +72,16 @@ "വാൾപേപ്പർ" "ഹോം ക്രമീകരണം" "അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" - "കാഴ്ച" + "അവലോകനം" "ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക" "ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ" "നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല" "അറിയിപ്പ് ഡോട്ടുകൾ" "ഓൺ" "ഓഫ്" + "അറിയിപ്പിനായുള്ള ആക്‌സസ് ആവശ്യമാണ്" + "അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുന്നതിന്, %1$s എന്നയാളിനായുള്ള ആപ്പ് അറിയിപ്പുകൾ ഓണാക്കുക" + "ക്രമീകരണം മാറ്റുക" "ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക" "പുതിയ ആപ്പുകൾക്ക്" "ഐക്കണിന്റെ ആകാരം മാറ്റുക" diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 264e55a86e..22479cc3e4 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -35,9 +35,9 @@ "%1$d өргөн %2$d өндөр" "Гараар байршуулахын тулд дараад хүлээнэ үү" "Автоматаар нэмэх" - "Апп хайх" - "Аппликейшныг ачаалж байна..." - "\"%1$s\"-д нийцэх апп олдсонгүй" + "Апп хайх" + "Аппыг ачааллаж байна..." + "\"%1$s\"-д тохирох апп олдсонгүй" "Бусад апп-г хайх" "Мэдэгдэл" "Энэ Нүүр дэлгэц зайгүй." @@ -79,17 +79,18 @@ "Мэдэгдлийн цэг" "Асаалттай" "Унтраалттай" + "Мэдэгдлийн хандалт шаардлагатай" + "Мэдэгдлийн цэгийг харуулахын тулд %1$s-д аппын мэдэгдлийг асаана уу" + "Тохиргоог өөрчлөх" "Нүүр хуудаст дүрс тэмдэг нэмэх" "Шинэ аппад зориулсан" - - + "Дүрс тэмдгийн хэлбэрийг өөрчлөх" "Системийн өгөгдмөл тохиргоог ашиглах" "Дөрвөлжин" "Мохоо өнцөгтэй дөрвөлжин" "Дугуй" "Дусал" - - + "Дүрс тэмдгийн хэлбэрийг өөрчилж байна" "Тодорхойгүй" "Устгах" "Хайх" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index b23ea965d1..751ad227ab 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -27,31 +27,31 @@ "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" "विजेट सुरक्षित मोडमध्ये अक्षम झाले" "शॉर्टकट उपलब्ध नाही" - "मुख्यपृष्ठ" + "होम स्क्रीन" "सानुकूल क्रिया" "विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." "एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा सानुकूल क्रिया वापरा." "%1$d × %2$d" "%1$d रूंद बाय %2$d उंच" "स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा" - "स्वयंचलितपणे जोडा" - "अॅप्स शोधा" - "अॅप्स लोड करीत आहे..." - "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" + "अापोआप जोडा" + "अॅप्स शोधा" + "अॅप्स लोड करत आहे…" + "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" "अधिक अॅप्स शोधा" "सूचना" "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" "अॅप्स सूची" - "मुख्‍यपृष्‍ठ" + "होम" "काढा" "विस्थापित करा" "अॅप माहिती" "शॉर्टकट स्‍थापित करा" "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते." - "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा" + "होम सेटिग्ज आणि शॉर्टकट वाचा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते." - "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा" + "होम स्क्रीन सेटिंग्ज आणि शॉर्टकट लिहा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते." "%1$s ला फोन कॉल करण्याची अनुमती नाही" "विजेट लोड करण्यात समस्या" @@ -72,14 +72,17 @@ "वॉलपेपर" "होम सेटिंग्‍ज" "आपल्या प्रशासकाने अक्षम केले" - "विहंगावलोकन" + "अवलोकन" "मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या" "फोन फिरविला जातो तेव्हा" "वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही" "सूचना बिंदू" "चालू" "बंद" - "मुख्य स्क्रीनवर चिन्ह जोडा" + "सूचनांच्या अ‍ॅक्सेसची आवश्यकता आहे" + "सूचना बिंदू दाखवण्यासाठी, %1$s साठी अ‍ॅप सूचना चालू करा" + "सेटिंग्ज बदला" + "होम स्क्रीनवर आयकन जोडा" "नवीन अॅप्ससाठी" "चिन्हाचा आकार बदला" "सिस्‍टमचे डीफॉल्‍ट वापरा" @@ -96,7 +99,7 @@ "%1$s डाउनलोड होत आहे , %2$s पूर्ण झाले" "%1$s स्थापित करण्याची प्रतिक्षा करीत आहे" "%1$s विजेट" - "मुख्य स्क्रीनवर जोडा" + "होम स्क्रीनवर जोडा" "आयटम येथे हलवा" "आयटम मुख्य स्क्रीनवर जोडला" "आयटम काढला" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index f7237a01d2..e142b0b6e8 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -31,9 +31,11 @@ "Ketik dua kali & tahan untuk mengambil widget atau menggunakan tindakan tersuai" "%1$d × %2$d" "Lebar %1$d kali tinggi %2$d" - "Cari Apl" - "Memuatkan Apl…" - "Tiada Apl yang ditemui sepadan dengan \"%1$s\"" + "Sentuh & tahan untuk meletakkan widget/ikon secara manual" + "Tambahkan secara automatik" + "Cari apl" + "Memuatkan apl…" + "Tiada apl yang ditemui sepadan dengan \"%1$s\"" "Cari lagi apl" "Tiada lagi ruang pada skrin Laman Utama ini." "Tiada ruang dalam dulang Kegemaran lagi" @@ -74,17 +76,18 @@ "Titik pemberitahuan" "Hidup" "Mati" + "Akses pemberitahuan diperlukan" + "Untuk menunjukkan Titik Pemberitahuan, hidupkan pemberitahuan apl untuk %1$s" + "Tukar tetapan" "Tambahkan ikon pada Skrin Utama" "Untuk apl baharu" - - + "Tukar bentuk ikon" "Gunakan lalai sistem" "Segi empat sama" "Segi empat berbucu bulat" "Bulatan" "Titisan air mata" - - + "Menggunakan perubahan bentuk ikon" "Tidak diketahui" "Alih keluar" "Carian" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index a4648b15a2..74574b9b23 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -35,9 +35,9 @@ "အလျား %1$d နှင့် အမြင့် %2$d" "ကိုယ်တိုင်ထည့်ရန် ထိထားပါ" "အလိုအလျောက် ထည့်ရန်" - "ရှာဖွေမှု အက်ပ်များ" - "အက်ပ်များ ရယူနေစဉ်..." - "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ" + "ရှာဖွေမှု အက်ပ်များ" + "အက်ပ်များကို ဖွင့်နေသည်…" + "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ" "နောက်ထပ် အက်ပ်များကို ရှာပါ" "အကြောင်းကြားချက်များ" "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" @@ -79,17 +79,18 @@ "အကြောင်းကြားချက်အမှတ်အသားများ" "ဖွင့်ထားသည်" "ပိတ်ထားသည်" + "အကြောင်းကြားချက် အသုံးပြုခွင့် လိုအပ်သည်" + "အကြောင်းကြားချက် အစက်များကို ပြသရန် %1$s အတွက် အက်ပ်အကြောင်းကြားချက်များကို ဖွင့်ပါ" + "ဆက်တင်များ ပြောင်းရန်" "ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်" "အက်ပ်အသစ်များအတွက်" - - + "သင်္ကေတပုံစံကို ပြောင်းရန်" "စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်" "လေးထောင့်" "စတုရန်းမကျ စက်ဝိုင်းမကျပုံ" "စက်ဝိုင်း" "မျက်ရည်စက်ပုံ" - - + "သင်္ကေတပုံစံကို ပြောင်းလဲနေသည်" "မသိရ" "ဖယ်ရှားရန်" "ရှာဖွေရန်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index bd348b0aab..65247c168d 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -84,13 +84,13 @@ "Endre innstillingene" "Legg til ikon på startsiden" "For nye apper" - "Endre ikonets form" + "Endre formen på ikonet" "Bruk systemstandard" "Kvadrat" "Superellipse" "Sirkel" "Dråpe" - "Aktiverer endringer av ikonets form" + "Aktiverer endringer av formen på ikonet" "Ukjent" "Fjern" "Søk" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 558b6cf8e5..904dad45e2 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -35,9 +35,9 @@ "%1$d चौडाइ गुणा %2$d उचाइ" "म्यानुअल तरिकाले थप्न छुनुहोस् र थिची राख्नुहोस्‌" "स्वतः थप्नुहोस्" - "अनुप्रयोगहरू खोज्नुहोस्" - "अनुप्रयोगहरू लोड गरिँदै..." - "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्" + "खोजसम्बन्धी अनुप्रयोगहरू" + "अनुप्रयोगहरू लोड गर्दै…" + "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोग भेटिएन" "थप अनुप्रयोगहरू खोज्नुहोस्" "सूचनाहरू" "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" @@ -79,6 +79,9 @@ "सूचनाको प्रतीक जनाउने थोप्लोहरू" "सक्रिय छ" "निष्क्रिय छ" + "सूचनासम्बन्धी पहुँच आवश्यक हुन्छ" + "सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउन %1$s को अनुप्रयोगसम्बन्धी सूचनाहरूलाई सक्रिय गर्नुहोस्" + "सेटिङहरू बदल्नुहोस्" "गृह स्क्रिनमा आइकन थप्नुहोस्" "नयाँ अनुप्रयोगका लागि" "आइकनको आकार परिवर्तन गर्नुहोस्" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index 05fe509a27..cf5351c7a7 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -25,7 +25,7 @@ "ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।" "ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" - "ਵਿਜਿਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" + "ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਮੁੱਖ ਸਕ੍ਰੀਨ" "ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ" @@ -35,9 +35,9 @@ "%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ" "ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ" "ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਸ਼ਾਮਲ ਕਰੋ" - "ਐਪਸ ਖੋਜੋ" - "ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." - "\"%1$s\" ਨਾਲ ਮਿਲਦੀਆਂ ਕੋਈ ਵੀ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ" + "ਐਪਾਂ ਖੋਜੋ" + "ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." + "\"%1$s\" ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਕੋਈ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ" "ਹੋਰ ਐਪਾਂ ਖੋਜੋ" "ਸੂਚਨਾਵਾਂ" "ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।" @@ -68,7 +68,7 @@ "ਫੋਲਡਰ ਬੰਦ ਕੀਤਾ" "ਫੋਲਡਰ ਨੂੰ %1$s ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ" "ਫੋਲਡਰ: %1$s" - "ਵਿਜਿਟ" + "ਵਿਜੇਟ" "ਵਾਲਪੇਪਰ" "ਹੋਮ ਸੈਟਿੰਗਾਂ" "ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ" @@ -79,6 +79,9 @@ "ਸੂਚਨਾ ਬਿੰਦੂ" "ਚਾਲੂ" "ਬੰਦ" + "ਸੂਚਨਾ ਪਹੁੰਚ ਲੋੜੀਂਦੀ ਹੈ" + "ਸੂਚਨਾ ਬਿੰਦੀਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" + "ਸੈਟਿੰਗਾਂ ਬਦਲੋ" "ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" @@ -95,7 +98,7 @@ "ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਇੰਸਟੌਲ ਕਰ ਸਕਦੇ ਹੋ।" "%1$s ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, %2$s ਸੰਪੂਰਣ" "%1$s ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" - "%1$s ਵਿਜਿਟ" + "%1$s ਵਿਜੇਟ" "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ" "ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index aed8f18e00..923f3573b3 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -28,9 +28,9 @@ "Виджеты отключены в безопасном режиме" "Ярлык недоступен" "Главный экран" - "Пользовательские действия" + "Специальные действия" "Чтобы выбрать виджет, нажмите на значок и удерживайте его." - "Чтобы выбрать виджет, нажмите на него дважды и не отпускайте или выполните предложенные действия." + "Чтобы выбрать виджет или использовать специальные действия, нажмите на него дважды и не отпускайте." "%1$d x %2$d" "Ширина %1$d, высота %2$d" "Нажмите и удерживайте, чтобы добавить вручную" @@ -90,7 +90,7 @@ "Квадрат с закругленными краями" "Круг" "Капля" - "Применение изменений..." + "Применение изменений…" "Неизвестно" "Удалить" "Найти" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 3b0425e33b..f6c42b2d9a 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -35,9 +35,9 @@ "පළල %1$d උස %2$d" "අතින් ස්ථානගත කිරීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න" "ස්වයංක්‍රියව එක් කරන්න" - "යෙදුම් සෙවීම" - "යෙදුම් පූරණය වෙමින්…" - "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" + "යෙදුම් සොයන්න" + "යෙදුම් පූරණය වෙමින්…" + "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" "තව යෙදුම් සඳහා සොයන්න" "දැනුම්දීම්" "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." @@ -79,17 +79,18 @@ "දැනුම්දීම් තිත්" "ක්‍රියාත්මකයි" "ක්‍රියාවිරහිතයි" + "දැනුම්දීම් ප්‍රවේශය අවශ්‍යයි" + "දැනුම්දීම් තිත් පෙන්වීමට, %1$s සඳහා යෙදුම් දැනුම්දීම් සබල කරන්න" + "සැකසීම් වෙනස් කරන්න" "මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න" "නව යෙදුම් සඳහා" - - + "නිරූපක හැඩය වෙනස් කරන්න" "පද්ධති පෙරනිමි භාවිත කරන්න" "සමචතුරස්‍රය" "හතරැස් කවය" "කවය" "කඳුළු බිංදුව" - - + "නිරූපක හැඩය වෙනස් කිරීම් යොදමින්" "නොදනී" "ඉවත් කරන්න" "සොයන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 490f823fe2..4dbc407b9c 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -58,7 +58,7 @@ "Nastavenie" "Toto je systémová aplikácia a nedá sa odinštalovať." "Nepomenovaný priečinok" - "Aplikácia %1$s je zakázaná" + "Aplikácia %1$s je deaktivovaná" "Stránka %1$d z %2$d" "Plocha %1$d z %2$d" "Nová stránka plochy" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 1400557c0c..3e6afee609 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -35,9 +35,9 @@ "%1$d i gjerë me %2$d i lartë" "Prek dhe mbaj të shtypur për të vendosur në mënyrë manuale" "Shto automatikisht" - "Kërko për aplikacione" - "Po ngarkon aplikacionet..." - "Nuk u gjet asnjë aplikacion që përputhet me \"%1$s\"" + "Kërko për aplikacione" + "Po ngarkon aplikacionet..." + "Nuk u gjet asnjë aplikacion që përputhet me \"%1$s\"" "Kërko për më shumë aplikacione" "Njoftimet" "Nuk ka më hapësirë në këtë ekran bazë." @@ -79,17 +79,18 @@ "Pikat e njoftimeve" "Aktiv" "Joaktiv" + "Nevojitet qasja në njoftime" + "Për të shfaqur \"Pikat e njoftimeve\", aktivizo njoftimet e aplikacionit për %1$s" + "Ndrysho cilësimet" "Shto ikonë në ekranin bazë" "Për aplikacionet e reja" - - + "Ndrysho formën e ikonës" "Përdor parazgjedhjen e sisteit" "Katror" "Katror me kënde të rrumbullakëta" "Rreth" "Pikë loti" - - + "Po zbatohen ndryshimet e formës së ikonës" "I panjohur" "Hiq" "Kërko" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index be8e82df6c..1d92581a05 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -35,9 +35,9 @@ "%1$d அகலத்திற்கு %2$d உயரம்" "நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்" "தானாகவே சேர்" - "பயன்பாடுகளில் தேடுக" - "பயன்பாடுகளை ஏற்றுகிறது..." - "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" + "பயன்பாடுகளில் தேடுக" + "பயன்பாடுகளை ஏற்றுகிறது…" + "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" "கூடுதல் பயன்பாடுகளைத் தேடு" "அறிவிப்புகள்" "முகப்புத் திரையில் இடமில்லை." @@ -79,17 +79,18 @@ "அறிவிப்புப் புள்ளிகள்" "இயக்கப்பட்டுள்ளது" "முடக்கப்பட்டுள்ளது" + "அறிவிப்பிற்கான அணுகல் தேவை" + "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்" + "அமைப்புகளை மாற்று" "முகப்புத் திரையில் ஐகானைச் சேர்" "புதிய பயன்பாடுகளுக்கு" - - + "ஐகான் வடிவத்தை மாற்று" "அமைப்பின் இயல்புநிலையைப் பயன்படுத்து" "சதுரம்" "சதுரவட்டம்" "வட்டம்" "கண்ணீர்துளி" - - + "ஐகான் வடிவத்தை மாற்றுகிறது" "தெரியாதது" "அகற்று" "தேடு" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 15f3de7dab..6c6669938b 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -22,9 +22,9 @@ "Launcher3" "కార్యాలయం" - "అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు." - "అనువర్తనం అందుబాటులో లేదు" - "డౌన్‌లోడ్ చేసిన అనువర్తనం సురక్షిత మోడ్‌లో నిలిపివేయబడింది" + "యాప్ ఇన్‌స్టాల్ చేయబడలేదు." + "యాప్ అందుబాటులో లేదు" + "డౌన్‌లోడ్ చేసిన యాప్ సురక్షిత మోడ్‌లో నిలిపివేయబడింది" "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" "సత్వరమార్గం అందుబాటులో లేదు" "హోమ్ స్క్రీన్" @@ -35,9 +35,9 @@ "%1$d వెడల్పు X %2$d ఎత్తు" "మాన్యువల్‌గా ఉంచడానికి నొక్కి &amp పట్టుకోండి" "స్వయంచాలకంగా జోడించు" - "అనువర్తనాలను శోధించండి" - "అనువర్తనాలను లోడ్ చేస్తోంది…" - "\"%1$s\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు" + "అప్లికేషన్‌లను శోధించండి" + "అప్లికేషన్‌లను లోడ్ చేస్తోంది…" + "\"%1$s\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు" "మరిన్ని అనువర్తనాల కోసం శోధించు" "నోటిఫికేషన్‌లు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." @@ -56,7 +56,7 @@ "ఫోన్ కాల్‌లను చేసేందుకు %1$sకి అనుమతి లేదు" "విడ్జెట్‌ను లోడ్ చేయడంలో సమస్య" "సెటప్ చేయి" - "ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." + "ఇది సిస్టమ్ యాప్ మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." "పేరు లేని ఫోల్డర్" "%1$s నిలిపివేయబడింది" "%2$dలో %1$dవ పేజీ" @@ -79,6 +79,9 @@ "నోటిఫికేషన్ డాట్‌లు" "ఆన్" "ఆఫ్" + "నోటిఫికేషన్ యాక్సెస్ అవసరం" + "నోటిఫికేషన్ డాట్‌లను చూపించడానికి %1$sకు యాప్ నోటిఫికేషన్‌లను ఆన్ చేయండి" + "సెట్టింగ్‌లను మార్చు" "హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు" "కొత్త అనువర్తనాల కోసం" "చిహ్న ఆకారాన్ని మార్చు" @@ -91,8 +94,8 @@ "తెలియదు" "తీసివేయి" "శోధించు" - "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" - "ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." + "ఈ యాప్ ఇన్‌స్టాల్ చేయబడలేదు" + "ఈ చిహ్నం యొక్క యాప్ ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ యాప్ కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." "%1$s డౌన్‌లోడ్ అవుతోంది, %2$s పూర్తయింది" "%1$s ఇన్‌స్టాల్ కావడానికి వేచి ఉంది" "%1$s విడ్జెట్‌లు" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 84966b2888..a704fab4ca 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -35,9 +35,9 @@ "‏%1$d چوڑا اور ‎%2$d اونچا" "‏دستی طور پر رکھنے کیلئے ‎& ٹچ کرکے ہولڈ کریں" "خود کار طور پر شامل کریں" - "ایپس تلاش کریں" - "ایپس لوڈ ہو رہی ہیں…" - "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" + "ایپس تلاش کریں" + "ایپس لوڈ کی جا رہی ہیں…" + "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" "مزید ایپس تلاش کریں" "اطلاعات" "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" @@ -79,6 +79,9 @@ "اطلاعاتی ڈاٹس" "آن" "آف" + "اطلاعاتی رسائی درکار ہے" + "اطلاعاتی ڈاٹس دکھانے کی خاطر %1$s کیلئے ایپ کی اطلاعات آن کریں" + "ترتیبات تبدیل کریں" "آئیکن کو ہوم اسکرین میں شامل کریں" "نئی ایپس کیلئے" "آئیکن کی شکل تبدیل کریں" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index a4725db53c..7e31889138 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -35,9 +35,9 @@ "Eni %1$d, bo‘yi %2$d" "Qo‘lda joylashtirish uchun bosib turing" "Avtomatik chiqarish" - "Ilovalar ichidan qidirish" - "Ilovalar yuklanmoqda…" - "“%1$s” so‘rovi bo‘yicha hech narsa topilmadi" + "Ilovalarni qidirish" + "Ilovalar yuklanmoqda…" + "“%1$s” bilan mos hech qanday ilova topilmadi" "Boshqa ilovalarni qidirish" "Bildirishnomalar" "Uy ekranida bitta ham xona yo‘q." @@ -79,17 +79,18 @@ "Bildirishnoma nuqtalari" "Yoniq" "O‘chiq" + "Bildirishnomalarga ruxsat berilmagan" + "Bildirishnoma nuqtalarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" + "Sozlamalarni o‘zgartirish" "Bosh ekranga ikonka qo‘shish" "Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish" - - + "Ikonka shaklini o‘zgartirish" "Standart tizim parametrlaridan foydalanish" "Kvadrat" "Qirralari aylana kvadrat" "Aylana" "Tomchi" - - + "Ikonka shakli o‘zgartirilmoqda" "Noma’lum" "O‘chirish" "Qidirish" diff --git a/res/values/config.xml b/res/values/config.xml index e9c62a783d..e7ec0a0b1c 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -9,6 +9,8 @@ false false + 191 + @string/app_name diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 28af705967..3f6efd794d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -19,7 +19,8 @@ 8dp 32dp 1dp - 50dp + 0dp + 0dp 8dp 80dp 120dp @@ -31,16 +32,21 @@ 8dp 5.5dp + 0dp 8dp 8dp 2dp - 80dp + 80dp + 0dp 0dp 0dp 0dp + 0dp + 0dp + 48dp diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java index c55a5860ef..82175b721d 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -62,7 +62,7 @@ public abstract class BaseContainerView extends FrameLayout public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) { + if (this instanceof AllAppsContainerView) { mBaseDrawable = new ColorDrawable(); } else { TypedArray a = context.obtainStyledAttributes(attrs, diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index aeb82b376a..a63767c5d3 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -592,6 +592,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver { mIconLoadRequest = null; mDisableRelayout = true; + // Optimization: Starting in N, pre-uploads the bitmap to RenderThread. + info.iconBitmap.prepareToDraw(); + if (info instanceof AppInfo) { applyFromApplicationInfo((AppInfo) info); } else if (info instanceof ShortcutInfo) { diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index aac80052e4..3ebccda9e7 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -780,7 +780,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return mCellWidth; } - int getCellHeight() { + public int getCellHeight() { return mCellHeight; } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 031bfe115b..150bc5309d 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -63,7 +63,7 @@ public class DeviceProfile { */ private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f; - private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 1.82f; + private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f; // Overview mode private final int overviewModeMinIconZoneHeightPx; @@ -75,6 +75,7 @@ public class DeviceProfile { // Workspace private final int desiredWorkspaceLeftRightMarginPx; public final int cellLayoutPaddingLeftRightPx; + public final int cellLayoutBottomPaddingPx; public final int edgeMarginPx; public final Rect defaultWidgetPadding; private final int defaultPageSpacingPx; @@ -84,7 +85,8 @@ public class DeviceProfile { // Page indicator private int pageIndicatorSizePx; - private final int pageIndicatorLandGutterPx; + private final int pageIndicatorLandLeftNavBarGutterPx; + private final int pageIndicatorLandRightNavBarGutterPx; private final int pageIndicatorLandWorkspaceOffsetPx; // Workspace icons @@ -113,13 +115,16 @@ public class DeviceProfile { // Hotseat public int hotseatCellHeightPx; - public int hotseatBarHeightPx; + // In portrait: size = height, in landscape: size = width + public int hotseatBarSizePx; public int hotseatBarTopPaddingPx; - public int hotseatBarLeftNavBarRightPaddingPx; - public int hotseatBarRightNavBarRightPaddingPx; public int hotseatBarBottomPaddingPx; - public int hotseatLandLeftNavBarGutterPx; - public int hotseatLandRightNavBarGutterPx; + + public int hotseatBarLeftNavBarLeftPaddingPx; + public int hotseatBarLeftNavBarRightPaddingPx; + + public int hotseatBarRightNavBarLeftPaddingPx; + public int hotseatBarRightNavBarRightPaddingPx; // All apps public int allAppsCellHeightPx; @@ -177,10 +182,14 @@ public class DeviceProfile { desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx; cellLayoutPaddingLeftRightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding); + cellLayoutBottomPaddingPx = + res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding); pageIndicatorSizePx = res.getDimensionPixelSize( R.dimen.dynamic_grid_min_page_indicator_size); - pageIndicatorLandGutterPx = res.getDimensionPixelSize( - R.dimen.dynamic_grid_page_indicator_gutter_width); + pageIndicatorLandLeftNavBarGutterPx = res.getDimensionPixelSize( + R.dimen.dynamic_grid_page_indicator_land_left_nav_bar_gutter_width); + pageIndicatorLandRightNavBarGutterPx = res.getDimensionPixelSize( + R.dimen.dynamic_grid_page_indicator_land_right_nav_bar_gutter_width); pageIndicatorLandWorkspaceOffsetPx = res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset); defaultPageSpacingPx = @@ -213,16 +222,15 @@ public class DeviceProfile { R.dimen.dynamic_grid_hotseat_land_left_nav_bar_right_padding); hotseatBarRightNavBarRightPaddingPx = res.getDimensionPixelSize( R.dimen.dynamic_grid_hotseat_land_right_nav_bar_right_padding); - hotseatBarHeightPx = isVerticalBarLayout() - ? res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height) - : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height) + hotseatBarLeftNavBarLeftPaddingPx = res.getDimensionPixelSize( + R.dimen.dynamic_grid_hotseat_land_left_nav_bar_left_padding); + hotseatBarRightNavBarLeftPaddingPx = res.getDimensionPixelSize( + R.dimen.dynamic_grid_hotseat_land_right_nav_bar_left_padding); + hotseatBarSizePx = isVerticalBarLayout() + ? Utilities.pxFromDp(inv.iconSize, dm) + : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size) + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx; - hotseatLandLeftNavBarGutterPx = res.getDimensionPixelSize( - R.dimen.dynamic_grid_hotseat_land_left_nav_bar_gutter_width); - hotseatLandRightNavBarGutterPx = res.getDimensionPixelSize( - R.dimen.dynamic_grid_hotseat_land_right_nav_bar_gutter_width); - // Determine sizes. widthPx = width; heightPx = height; @@ -238,17 +246,17 @@ public class DeviceProfile { updateAvailableDimensions(dm, res); // Now that we have all of the variables calculated, we can tune certain sizes. - float aspectRatio = ((float) Math.max(availableWidthPx, availableHeightPx)) - / Math.min(availableWidthPx, availableHeightPx); + float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; if (!isVerticalBarLayout() && isPhone && isTallDevice) { - // We increase the page indicator size when there is extra space. + // We increase the hotseat size when there is extra space. // ie. For a display with a large aspect ratio, we can keep the icons on the workspace - // in portrait mode closer together by increasing the page indicator size. + // in portrait mode closer together by adding more height to the hotseat. // Note: This calculation was created after noticing a pattern in the design spec. - pageIndicatorSizePx = getCellSize().y - iconSizePx - iconDrawablePaddingPx; + int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx; + hotseatBarSizePx += extraSpace - pageIndicatorSizePx; - // Recalculate the available dimensions using the new page indicator size. + // Recalculate the available dimensions using the new hotseat size. updateAvailableDimensions(dm, res); } @@ -301,10 +309,10 @@ public class DeviceProfile { // In normal cases, All Apps cell height should equal the Workspace cell height. // Since we are removing labels from the Workspace, we need to manually compute the // All Apps cell height. + int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1); allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx + Utilities.calculateTextHeight(allAppsIconTextSizePx) - // Top and bottom padding is equal to the drawable padding - + allAppsIconDrawablePaddingPx * 2; + + topBottomPadding * 2; } /** @@ -332,11 +340,16 @@ public class DeviceProfile { } private void updateIconSize(float scale, Resources res, DisplayMetrics dm) { + // Workspace float invIconSizePx = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize; iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale); iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); + cellWidthPx = iconSizePx + iconDrawablePaddingPx; + cellHeightPx = iconSizePx + iconDrawablePaddingPx + + Utilities.calculateTextHeight(iconTextSizePx); + // All apps allAppsIconTextSizePx = iconTextSizePx; allAppsIconSizePx = iconSizePx; @@ -348,15 +361,14 @@ public class DeviceProfile { adjustToHideWorkspaceLabels(); } - cellWidthPx = iconSizePx + iconDrawablePaddingPx; - cellHeightPx = iconSizePx + iconDrawablePaddingPx - + Utilities.calculateTextHeight(iconTextSizePx); - // Hotseat - hotseatCellHeightPx = iconSizePx + iconDrawablePaddingPx; + if (isVerticalBarLayout()) { + hotseatBarSizePx = iconSizePx; + } + hotseatCellHeightPx = iconSizePx; if (!isVerticalBarLayout()) { - int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx + int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx - pageIndicatorSizePx - topWorkspacePadding; float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace; workspaceSpringLoadShrinkFactor = Math.min( @@ -449,9 +461,10 @@ public class DeviceProfile { // Since we are only concerned with the overall padding, layout direction does // not matter. Point padding = getTotalWorkspacePadding(); - int cellPadding = cellLayoutPaddingLeftRightPx * 2; - result.x = calculateCellWidth(availableWidthPx - padding.x - cellPadding, inv.numColumns); - result.y = calculateCellHeight(availableHeightPx - padding.y, inv.numRows); + result.x = calculateCellWidth(availableWidthPx - padding.x + - cellLayoutPaddingLeftRightPx * 2, inv.numColumns); + result.y = calculateCellHeight(availableHeightPx - padding.y + - cellLayoutBottomPaddingPx, inv.numRows); return result; } @@ -467,20 +480,21 @@ public class DeviceProfile { Rect padding = recycle == null ? new Rect() : recycle; if (isVerticalBarLayout()) { if (mInsets.left > 0) { - padding.set(mInsets.left + pageIndicatorLandGutterPx, + padding.set(mInsets.left + pageIndicatorLandLeftNavBarGutterPx, 0, - hotseatBarHeightPx + hotseatLandLeftNavBarGutterPx - + hotseatBarLeftNavBarRightPaddingPx - mInsets.left, + hotseatBarSizePx + hotseatBarLeftNavBarRightPaddingPx + + hotseatBarLeftNavBarLeftPaddingPx + - mInsets.left, edgeMarginPx); } else { - padding.set(pageIndicatorLandGutterPx, + padding.set(pageIndicatorLandRightNavBarGutterPx, 0, - hotseatBarHeightPx + hotseatLandRightNavBarGutterPx - + hotseatBarRightNavBarRightPaddingPx, + hotseatBarSizePx + hotseatBarRightNavBarRightPaddingPx + + hotseatBarRightNavBarLeftPaddingPx, edgeMarginPx); } } else { - int paddingBottom = hotseatBarHeightPx + pageIndicatorSizePx; + int paddingBottom = hotseatBarSizePx + pageIndicatorSizePx; if (isTablet) { // Pad the left and right of the workspace to ensure consistent spacing // between all icons @@ -515,14 +529,14 @@ public class DeviceProfile { // Folders should only appear right of the drop target bar and left of the hotseat return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx, mInsets.top, - mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx, + mInsets.left + availableWidthPx - hotseatBarSizePx - edgeMarginPx, mInsets.top + availableHeightPx); } else { // Folders should only appear below the drop target bar and above the hotseat return new Rect(mInsets.left, mInsets.top + dropTargetBarSizePx + edgeMarginPx, mInsets.left + availableWidthPx, - mInsets.top + availableHeightPx - hotseatBarHeightPx + mInsets.top + availableHeightPx - hotseatBarSizePx - pageIndicatorSizePx - edgeMarginPx); } } @@ -609,38 +623,44 @@ public class DeviceProfile { if (hasVerticalBarLayout) { // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the // screen regardless of RTL - lp.gravity = Gravity.RIGHT; - lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right; - lp.height = LayoutParams.MATCH_PARENT; - int paddingRight = mInsets.left > 0 ? hotseatBarLeftNavBarRightPaddingPx : hotseatBarRightNavBarRightPaddingPx; + int paddingLeft = mInsets.left > 0 + ? hotseatBarLeftNavBarLeftPaddingPx + : hotseatBarRightNavBarLeftPaddingPx; - hotseat.getLayout().setPadding(mInsets.left + cellLayoutPaddingLeftRightPx, - mInsets.top, mInsets.right + paddingRight + cellLayoutPaddingLeftRightPx, - workspacePadding.bottom); + lp.gravity = Gravity.RIGHT; + lp.width = hotseatBarSizePx + mInsets.left + mInsets.right + + paddingLeft + paddingRight; + lp.height = LayoutParams.MATCH_PARENT; + + hotseat.getLayout().setPadding(mInsets.left + cellLayoutPaddingLeftRightPx + + paddingLeft, + mInsets.top, + mInsets.right + cellLayoutPaddingLeftRightPx + paddingRight, + workspacePadding.bottom + cellLayoutBottomPaddingPx); } else if (isTablet) { // Pad the hotseat with the workspace padding calculated above lp.gravity = Gravity.BOTTOM; lp.width = LayoutParams.MATCH_PARENT; - lp.height = hotseatBarHeightPx + mInsets.bottom; + lp.height = hotseatBarSizePx + mInsets.bottom; hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx, hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx, - hotseatBarBottomPaddingPx + mInsets.bottom); + hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx); } else { // For phones, layout the hotseat without any bottom margin // to ensure that we have space for the folders lp.gravity = Gravity.BOTTOM; lp.width = LayoutParams.MATCH_PARENT; - lp.height = hotseatBarHeightPx + mInsets.bottom; + lp.height = hotseatBarSizePx + mInsets.bottom; hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx, hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx, - hotseatBarBottomPaddingPx + mInsets.bottom); + hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx); } hotseat.setLayoutParams(lp); @@ -659,7 +679,7 @@ public class DeviceProfile { // Put the page indicators above the hotseat lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; lp.height = pageIndicatorSizePx; - lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom; + lp.bottomMargin = hotseatBarSizePx + mInsets.bottom; } pageIndicator.setLayoutParams(lp); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4b3148626a..8492a7985c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -119,6 +119,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.ActivityResultInfo; +import com.android.launcher3.util.RunnableWithId; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; @@ -144,6 +145,10 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; + +import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; +import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; /** * Default launcher application. @@ -245,7 +250,6 @@ public class Launcher extends BaseActivity // Main container view and the model for the widget tray screen. @Thunk WidgetsContainerView mWidgetsView; - @Thunk MultiHashMap mAllWidgets; // We set the state in both onCreate and then onNewIntent in some cases, which causes both // scroll issues (because the workspace may not have been measured yet) and extra work. @@ -465,9 +469,6 @@ public class Launcher extends BaseActivity setOrientation(); setContentView(mLauncherView); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onCreate(savedInstanceState); - } // Listen for broadcasts IntentFilter filter = new IntentFilter(); @@ -478,6 +479,10 @@ public class Launcher extends BaseActivity getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onCreate(savedInstanceState); + } } @Override @@ -959,6 +964,9 @@ public class Launcher extends BaseActivity } else if (mOnResumeState == State.WIDGETS) { showWidgetsView(false, false); } + if (mOnResumeState != State.APPS) { + tryAndUpdatePredictedApps(); + } mOnResumeState = State.NONE; mPaused = false; @@ -1302,9 +1310,7 @@ public class Launcher extends BaseActivity mDragController.addDropTarget(mWorkspace); mDropTargetBar.setup(mDragController); - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { - mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace); - } + mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace); if (TestingUtils.MEMORY_DUMP_ENABLED) { TestingUtils.addWeightWatcher(this); @@ -1851,6 +1857,8 @@ public class Launcher extends BaseActivity LauncherAnimUtils.onDestroyActivity(); + clearPendingBinds(); + if (mLauncherCallbacks != null) { mLauncherCallbacks.onDestroy(); } @@ -2270,7 +2278,7 @@ public class Launcher extends BaseActivity if (v instanceof FolderIcon) { onClickFolderIcon(v); } - } else if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) || + } else if ((v instanceof PageIndicator) || (v == mAllAppsButton && mAllAppsButton != null)) { onClickAllAppsButton(v); } else if (tag instanceof AppInfo) { @@ -3106,12 +3114,12 @@ public class Launcher extends BaseActivity * * @return {@code true} if we are currently paused. The caller might be able to skip some work */ - @Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { + @Thunk boolean waitUntilResume(Runnable run) { if (mPaused) { if (LOGD) Log.d(TAG, "Deferring update until onResume"); - if (deletePreviousRunnables) { - while (mBindOnResumeCallbacks.remove(run)) { - } + if (run instanceof RunnableWithId) { + // Remove any runnables which have the same id + while (mBindOnResumeCallbacks.remove(run)) { } } mBindOnResumeCallbacks.add(run); return true; @@ -3120,10 +3128,6 @@ public class Launcher extends BaseActivity } } - private boolean waitUntilResume(Runnable run) { - return waitUntilResume(run, false); - } - public void addOnResumeCallback(Runnable run) { mOnResumeCallbacks.add(run); } @@ -3242,13 +3246,13 @@ public class Launcher extends BaseActivity } } + @Override public void bindAppsAdded(final ArrayList newScreens, final ArrayList addNotAnimated, - final ArrayList addAnimated, - final ArrayList addedApps) { + final ArrayList addAnimated) { Runnable r = new Runnable() { public void run() { - bindAppsAdded(newScreens, addNotAnimated, addAnimated, addedApps); + bindAppsAdded(newScreens, addNotAnimated, addAnimated); } }; if (waitUntilResume(r)) { @@ -3263,20 +3267,14 @@ public class Launcher extends BaseActivity // We add the items without animation on non-visible pages, and with // animations on the new page (which we will try and snap to). if (addNotAnimated != null && !addNotAnimated.isEmpty()) { - bindItems(addNotAnimated, 0, - addNotAnimated.size(), false); + bindItems(addNotAnimated, false); } if (addAnimated != null && !addAnimated.isEmpty()) { - bindItems(addAnimated, 0, - addAnimated.size(), true); + bindItems(addAnimated, true); } // Remove the extra empty screen mWorkspace.removeExtraEmptyScreen(false, false); - - if (addedApps != null && mAppsView != null) { - mAppsView.addApps(addedApps); - } } /** @@ -3285,11 +3283,10 @@ public class Launcher extends BaseActivity * Implementation of the method from LauncherModel.Callbacks. */ @Override - public void bindItems(final ArrayList items, final int start, final int end, - final boolean forceAnimateIcons) { + public void bindItems(final List items, final boolean forceAnimateIcons) { Runnable r = new Runnable() { public void run() { - bindItems(items, start, end, forceAnimateIcons); + bindItems(items, forceAnimateIcons); } }; if (waitUntilResume(r)) { @@ -3302,7 +3299,8 @@ public class Launcher extends BaseActivity final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation(); Workspace workspace = mWorkspace; long newItemsScreenId = -1; - for (int i = start; i < end; i++) { + int end = items.size(); + for (int i = 0; i < end; i++) { final ItemInfo item = items.get(i); // Short circuit if we are loading dock items for a configuration which has no dock @@ -3327,19 +3325,10 @@ public class Launcher extends BaseActivity break; } case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: { - LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item; - if (mIsSafeModeEnabled) { - view = new PendingAppWidgetHostView(this, info, mIconCache, true); - } else { - LauncherAppWidgetProviderInfo providerInfo = - mAppWidgetManager.getLauncherAppWidgetInfo(info.appWidgetId); - if (providerInfo == null) { - deleteWidgetInfo(info); - continue; - } - view = mAppWidgetHost.createView(this, info.appWidgetId, providerInfo); + view = inflateAppWidget((LauncherAppWidgetInfo) item); + if (view == null) { + continue; } - prepareAppWidget((AppWidgetHostView) view, info); break; } default: @@ -3409,26 +3398,21 @@ public class Launcher extends BaseActivity /** * Add the views for a widget to the workspace. - * - * Implementation of the method from LauncherModel.Callbacks. */ - public void bindAppWidget(final LauncherAppWidgetInfo item) { - Runnable r = new Runnable() { - public void run() { - bindAppWidget(item); - } - }; - if (waitUntilResume(r)) { - return; + public void bindAppWidget(LauncherAppWidgetInfo item) { + View view = inflateAppWidget(item); + if (view != null) { + mWorkspace.addInScreen(view, item); + mWorkspace.requestLayout(); } + } + private View inflateAppWidget(LauncherAppWidgetInfo item) { if (mIsSafeModeEnabled) { PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, mIconCache, true); prepareAppWidget(view, item); - mWorkspace.addInScreen(view, item); - mWorkspace.requestLayout(); - return; + return view; } final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0; @@ -3458,7 +3442,7 @@ public class Launcher extends BaseActivity + ", as the provider is null"); } getModelWriter().deleteItemFromDatabase(item); - return; + return null; } // If we do not have a valid id, try to bind an id. @@ -3526,7 +3510,7 @@ public class Launcher extends BaseActivity if (appWidgetInfo == null) { FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); deleteWidgetInfo(item); - return; + return null; } item.minSpanX = appWidgetInfo.minSpanX; @@ -3536,13 +3520,12 @@ public class Launcher extends BaseActivity view = new PendingAppWidgetHostView(this, item, mIconCache, false); } prepareAppWidget(view, item); - mWorkspace.addInScreen(view, item); - mWorkspace.requestLayout(); if (DEBUG_WIDGETS) { Log.d(TAG, "bound widget id="+item.appWidgetId+" in " + (SystemClock.uptimeMillis()-start) + "ms"); } + return view; } /** @@ -3677,30 +3660,29 @@ public class Launcher extends BaseActivity return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL; } - /** - * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent - * multiple calls to bind the same list.) - */ - @Thunk ArrayList mTmpAppsList; - private final Runnable mBindAllApplicationsRunnable = new Runnable() { - public void run() { - bindAllApplications(mTmpAppsList); - mTmpAppsList = null; - } - }; - /** * Add the icons for all apps. * * Implementation of the method from LauncherModel.Callbacks. */ public void bindAllApplications(final ArrayList apps) { - if (waitUntilResume(mBindAllApplicationsRunnable, true)) { - mTmpAppsList = apps; + Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) { + public void run() { + bindAllApplications(apps); + } + }; + if (waitUntilResume(r)) { return; } if (mAppsView != null) { + Executor pendingExecutor = getPendingExecutor(); + if (pendingExecutor != null && mState != State.APPS) { + // Wait until the fade in animation has finished before setting all apps list. + pendingExecutor.execute(r); + return; + } + mAppsView.setApps(apps); } if (mLauncherCallbacks != null) { @@ -3708,6 +3690,14 @@ public class Launcher extends BaseActivity } } + /** + * Returns an Executor that will run after the launcher is first drawn (including after the + * initial fade in animation). Returns null if the first draw has already occurred. + */ + public @Nullable Executor getPendingExecutor() { + return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null; + } + /** * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary * because LauncherModel's map is updated in the background, while Launcher runs on the UI. @@ -3722,10 +3712,10 @@ public class Launcher extends BaseActivity * * Implementation of the method from LauncherModel.Callbacks. */ - public void bindAppsUpdated(final ArrayList apps) { + public void bindAppsAddedOrUpdated(final ArrayList apps) { Runnable r = new Runnable() { public void run() { - bindAppsUpdated(apps); + bindAppsAddedOrUpdated(apps); } }; if (waitUntilResume(r)) { @@ -3733,7 +3723,7 @@ public class Launcher extends BaseActivity } if (mAppsView != null) { - mAppsView.updateApps(apps); + mAppsView.addOrUpdateApps(apps); } } @@ -3771,16 +3761,12 @@ public class Launcher extends BaseActivity * Implementation of the method from LauncherModel.Callbacks. * * @param updated list of shortcuts which have changed. - * @param removed list of shortcuts which were deleted in the background. This can happen when - * an app gets removed from the system or some of its components are no longer - * available. */ @Override - public void bindShortcutsChanged(final ArrayList updated, - final ArrayList removed, final UserHandle user) { + public void bindShortcutsChanged(final ArrayList updated, final UserHandle user) { Runnable r = new Runnable() { public void run() { - bindShortcutsChanged(updated, removed, user); + bindShortcutsChanged(updated, user); } }; if (waitUntilResume(r)) { @@ -3790,31 +3776,6 @@ public class Launcher extends BaseActivity if (!updated.isEmpty()) { mWorkspace.updateShortcuts(updated); } - - if (!removed.isEmpty()) { - HashSet removedComponents = new HashSet<>(); - HashSet removedDeepShortcuts = new HashSet<>(); - - for (ShortcutInfo si : removed) { - if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si)); - } else { - removedComponents.add(si.getTargetComponent()); - } - } - - if (!removedComponents.isEmpty()) { - ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(removedComponents, user); - mWorkspace.removeItemsByMatcher(matcher); - mDragController.onAppsRemoved(matcher); - } - - if (!removedDeepShortcuts.isEmpty()) { - ItemInfoMatcher matcher = ItemInfoMatcher.ofShortcutKeys(removedDeepShortcuts); - mWorkspace.removeItemsByMatcher(matcher); - mDragController.onAppsRemoved(matcher); - } - } } /** @@ -3844,28 +3805,17 @@ public class Launcher extends BaseActivity * package-removal should clear all items by package name. */ @Override - public void bindWorkspaceComponentsRemoved( - final HashSet packageNames, final HashSet components, - final UserHandle user) { + public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) { Runnable r = new Runnable() { public void run() { - bindWorkspaceComponentsRemoved(packageNames, components, user); + bindWorkspaceComponentsRemoved(matcher); } }; if (waitUntilResume(r)) { return; } - if (!packageNames.isEmpty()) { - ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageNames, user); - mWorkspace.removeItemsByMatcher(matcher); - mDragController.onAppsRemoved(matcher); - - } - if (!components.isEmpty()) { - ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(components, user); - mWorkspace.removeItemsByMatcher(matcher); - mDragController.onAppsRemoved(matcher); - } + mWorkspace.removeItemsByMatcher(matcher); + mDragController.onAppsRemoved(matcher); } @Override @@ -3886,22 +3836,25 @@ public class Launcher extends BaseActivity } } - private final Runnable mBindAllWidgetsRunnable = new Runnable() { + @Override + public void bindAllWidgets(final MultiHashMap allWidgets) { + Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) { + @Override public void run() { - bindAllWidgets(mAllWidgets); + bindAllWidgets(allWidgets); } }; - - @Override - public void bindAllWidgets(MultiHashMap allWidgets) { - if (waitUntilResume(mBindAllWidgetsRunnable, true)) { - mAllWidgets = allWidgets; + if (waitUntilResume(r)) { return; } if (mWidgetsView != null && allWidgets != null) { + Executor pendingExecutor = getPendingExecutor(); + if (pendingExecutor != null && mState != State.WIDGETS) { + pendingExecutor.execute(r); + return; + } mWidgetsView.setWidgets(allWidgets); - mAllWidgets = null; } AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index cf20febd5b..1ffe41bc6c 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.Context; import android.content.Intent; @@ -28,13 +29,17 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dynamicui.ExtractionUtils; +import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.ConfigMonitor; import com.android.launcher3.util.Preconditions; +import com.android.launcher3.util.SettingsObserver; import com.android.launcher3.util.TestingUtils; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING; + public class LauncherAppState { public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD; @@ -47,7 +52,7 @@ public class LauncherAppState { private final IconCache mIconCache; private final WidgetPreviewLoader mWidgetCache; private final InvariantDeviceProfile mInvariantDeviceProfile; - + private final SettingsObserver mNotificationBadgingObserver; public static LauncherAppState getInstance(final Context context) { if (INSTANCE == null) { @@ -117,6 +122,23 @@ public class LauncherAppState { new ConfigMonitor(mContext).register(); ExtractionUtils.startColorExtractionServiceIfNecessary(mContext); + + if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) { + mNotificationBadgingObserver = null; + } else { + // Register an observer to rebind the notification listener when badging is re-enabled. + mNotificationBadgingObserver = new SettingsObserver.Secure( + mContext.getContentResolver()) { + @Override + public void onSettingChanged(boolean isNotificationBadgingEnabled) { + if (isNotificationBadgingEnabled) { + NotificationListener.requestRebind(new ComponentName( + mContext, NotificationListener.class)); + } + } + }; + mNotificationBadgingObserver.register(NOTIFICATION_BADGING); + } } /** @@ -127,6 +149,9 @@ public class LauncherAppState { final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext); launcherApps.removeOnAppsChangedCallback(mModel); PackageInstallerCompat.getInstance(mContext).onStop(); + if (mNotificationBadgingObserver != null) { + mNotificationBadgingObserver.unregister(); + } } LauncherModel setLauncher(Launcher launcher) { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f1638fda25..a906b00f1f 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -56,6 +56,7 @@ import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutInfoCompat; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; @@ -138,26 +139,20 @@ public class LauncherModel extends BroadcastReceiver public int getCurrentWorkspaceScreen(); public void clearPendingBinds(); public void startBinding(); - public void bindItems(ArrayList shortcuts, int start, int end, - boolean forceAnimateIcons); + public void bindItems(List shortcuts, boolean forceAnimateIcons); public void bindScreens(ArrayList orderedScreenIds); public void finishFirstPageBind(ViewOnDrawExecutor executor); public void finishBindingItems(); - public void bindAppWidget(LauncherAppWidgetInfo info); public void bindAllApplications(ArrayList apps); + public void bindAppsAddedOrUpdated(ArrayList apps); public void bindAppsAdded(ArrayList newScreens, ArrayList addNotAnimated, - ArrayList addAnimated, - ArrayList addedApps); - public void bindAppsUpdated(ArrayList apps); + ArrayList addAnimated); public void bindPromiseAppProgressUpdated(PromiseAppInfo app); - public void bindShortcutsChanged(ArrayList updated, - ArrayList removed, UserHandle user); + public void bindShortcutsChanged(ArrayList updated, UserHandle user); public void bindWidgetsRestored(ArrayList widgets); public void bindRestoreItemsChange(HashSet updates); - public void bindWorkspaceComponentsRemoved( - HashSet packageNames, HashSet components, - UserHandle user); + public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher); public void bindAppInfosRemoved(ArrayList appInfos); public void bindAllWidgets(MultiHashMap widgets); public void onPageBoundSynchronously(int page); @@ -537,7 +532,7 @@ public class LauncherModel extends BroadcastReceiver scheduleCallbackTask(new CallbackTask() { @Override public void execute(Callbacks callbacks) { - callbacks.bindAppsAdded(null, null, null, arrayList); + callbacks.bindAppsAddedOrUpdated(arrayList); } }); } @@ -689,4 +684,8 @@ public class LauncherModel extends BroadcastReceiver public static Looper getWorkerLooper() { return sWorkerThread.getLooper(); } + + public static void setWorkerPriority(final int priority) { + Process.setThreadPriority(sWorkerThread.getThreadId(), priority); + } } diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index 643d48adfc..a814323633 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -11,6 +11,9 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewDebug; +import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV; +import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW; + public class LauncherRootView extends InsettableFrameLayout { private final Paint mOpaquePaint; @@ -44,20 +47,28 @@ public class LauncherRootView extends InsettableFrameLayout { @TargetApi(23) @Override protected boolean fitSystemWindows(Rect insets) { - boolean rawInsetsChanged = !mInsets.equals(insets); mDrawSideInsetBar = (insets.right > 0 || insets.left > 0) && (!Utilities.ATLEAST_MARSHMALLOW || - getContext().getSystemService(ActivityManager.class).isLowRamDevice()); - mRightInsetBarWidth = insets.right; - mLeftInsetBarWidth = insets.left; - setInsets(mDrawSideInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets); + getContext().getSystemService(ActivityManager.class).isLowRamDevice()); + if (mDrawSideInsetBar) { + mLeftInsetBarWidth = insets.left; + mRightInsetBarWidth = insets.right; + insets = new Rect(0, insets.top, 0, insets.bottom); + } else { + mLeftInsetBarWidth = mRightInsetBarWidth = 0; + } + Launcher.getLauncher(getContext()).getSystemUiController().updateUiState( + UI_STATE_ROOT_VIEW, mDrawSideInsetBar ? FLAG_DARK_NAV : 0); - if (mAlignedView != null && mDrawSideInsetBar) { + boolean rawInsetsChanged = !mInsets.equals(insets); + setInsets(insets); + + if (mAlignedView != null) { // Apply margins on aligned view to handle left/right insets. MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams(); - if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) { - lp.leftMargin = insets.left; - lp.rightMargin = insets.right; + if (lp.leftMargin != mLeftInsetBarWidth || lp.rightMargin != mRightInsetBarWidth) { + lp.leftMargin = mLeftInsetBarWidth; + lp.rightMargin = mRightInsetBarWidth; mAlignedView.setLayoutParams(lp); } } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 44b9704f28..e2474900da 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -80,13 +80,11 @@ import com.android.launcher3.widget.WidgetsContainerView; public class LauncherStateTransitionAnimation { /** - * animation used for all apps and widget tray when - *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code false} + * animation used for the widget tray */ public static final int CIRCULAR_REVEAL = 0; /** - * animation used for all apps and not widget tray when - *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code true} + * animation used for all apps tray */ public static final int PULLUP = 1; @@ -154,13 +152,9 @@ public class LauncherStateTransitionAnimation { mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } }; - int animType = CIRCULAR_REVEAL; - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { - animType = PULLUP; - } // Only animate the search bar if animating from spring loaded mode back to all apps startAnimationToOverlay( - Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb); + Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, PULLUP, cb); } /** @@ -193,12 +187,8 @@ public class LauncherStateTransitionAnimation { if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED || mAllAppsController.isTransitioning()) { - int animType = CIRCULAR_REVEAL; - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { - animType = PULLUP; - } startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, - animated, animType, onCompleteRunnable); + animated, PULLUP, onCompleteRunnable); } else if (fromState == Launcher.State.WIDGETS || fromState == Launcher.State.WIDGETS_SPRING_LOADED) { startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, @@ -235,8 +225,7 @@ public class LauncherStateTransitionAnimation { playCommonTransitionAnimations(toWorkspaceState, animated, initialized, animation, layerViews); if (!animated || !initialized) { - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && - toWorkspaceState == Workspace.State.NORMAL_HIDDEN) { + if (toWorkspaceState == Workspace.State.NORMAL_HIDDEN) { mAllAppsController.finishPullUp(); } toView.setTranslationX(0.0f); @@ -338,8 +327,10 @@ public class LauncherStateTransitionAnimation { toView.post(new StartAnimRunnable(animation, toView)); mCurrentAnimation = animation; } else if (animType == PULLUP) { - // We are animating the content view alpha, so ensure we have a layer for it - layerViews.addView(contentView); + if (!FeatureFlags.LAUNCHER3_PHYSICS) { + // We are animating the content view alpha, so ensure we have a layer for it. + layerViews.addView(contentView); + } animation.addListener(new AnimatorListenerAdapter() { @Override @@ -525,8 +516,7 @@ public class LauncherStateTransitionAnimation { playCommonTransitionAnimations(toWorkspaceState, animated, initialized, animation, layerViews); if (!animated || !initialized) { - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && - fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) { + if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) { mAllAppsController.finishPullDown(); } fromView.setVisibility(View.GONE); diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index d5d5eab761..90463725ff 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -26,18 +26,15 @@ import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.database.ContentObserver; import android.os.Bundle; -import android.os.Handler; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.provider.Settings; -import android.provider.Settings.System; -import android.view.View; import com.android.launcher3.graphics.IconShapeOverride; import com.android.launcher3.notification.NotificationListener; +import com.android.launcher3.util.SettingsObserver; import com.android.launcher3.views.ButtonPreference; /** @@ -46,8 +43,8 @@ import com.android.launcher3.views.ButtonPreference; public class SettingsActivity extends Activity { private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging"; - // TODO: use Settings.Secure.NOTIFICATION_BADGING - private static final String NOTIFICATION_BADGING = "notification_badging"; + /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ + public static final String NOTIFICATION_BADGING = "notification_badging"; /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners"; @@ -89,12 +86,9 @@ public class SettingsActivity extends Activity { // Register a content observer to listen for system setting changes while // this UI is active. - resolver.registerContentObserver( - Settings.System.getUriFor(System.ACCELEROMETER_ROTATION), - false, mRotationLockObserver); + mRotationLockObserver.register(Settings.System.ACCELEROMETER_ROTATION); // Initialize the UI once - mRotationLockObserver.onChange(true); rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity())); } @@ -108,13 +102,7 @@ public class SettingsActivity extends Activity { // Listen to system notification badge settings while this UI is active. mIconBadgingObserver = new IconBadgingObserver( iconBadgingPref, resolver, getFragmentManager()); - resolver.registerContentObserver( - Settings.Secure.getUriFor(NOTIFICATION_BADGING), - false, mIconBadgingObserver); - resolver.registerContentObserver( - Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), - false, mIconBadgingObserver); - mIconBadgingObserver.onChange(true); + mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS); } Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE); @@ -130,11 +118,11 @@ public class SettingsActivity extends Activity { @Override public void onDestroy() { if (mRotationLockObserver != null) { - getActivity().getContentResolver().unregisterContentObserver(mRotationLockObserver); + mRotationLockObserver.unregister(); mRotationLockObserver = null; } if (mIconBadgingObserver != null) { - getActivity().getContentResolver().unregisterContentObserver(mIconBadgingObserver); + mIconBadgingObserver.unregister(); mIconBadgingObserver = null; } super.onDestroy(); @@ -145,22 +133,18 @@ public class SettingsActivity extends Activity { * Content observer which listens for system auto-rotate setting changes, and enables/disables * the launcher rotation setting accordingly. */ - private static class SystemDisplayRotationLockObserver extends ContentObserver { + private static class SystemDisplayRotationLockObserver extends SettingsObserver.System { private final Preference mRotationPref; - private final ContentResolver mResolver; public SystemDisplayRotationLockObserver( Preference rotationPref, ContentResolver resolver) { - super(new Handler()); + super(resolver); mRotationPref = rotationPref; - mResolver = resolver; } @Override - public void onChange(boolean selfChange) { - boolean enabled = Settings.System.getInt(mResolver, - Settings.System.ACCELEROMETER_ROTATION, 1) == 1; + public void onSettingChanged(boolean enabled) { mRotationPref.setEnabled(enabled); mRotationPref.setSummary(enabled ? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc); @@ -171,8 +155,8 @@ public class SettingsActivity extends Activity { * Content observer which listens for system badging setting changes, * and updates the launcher badging setting subtext accordingly. */ - private static class IconBadgingObserver extends ContentObserver - implements View.OnClickListener { + private static class IconBadgingObserver extends SettingsObserver.Secure + implements Preference.OnPreferenceClickListener { private final ButtonPreference mBadgingPref; private final ContentResolver mResolver; @@ -180,15 +164,14 @@ public class SettingsActivity extends Activity { public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver, FragmentManager fragmentManager) { - super(new Handler()); + super(resolver); mBadgingPref = badgingPref; mResolver = resolver; mFragmentManager = fragmentManager; } @Override - public void onChange(boolean selfChange) { - boolean enabled = Settings.Secure.getInt(mResolver, NOTIFICATION_BADGING, 1) == 1; + public void onSettingChanged(boolean enabled) { int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off; boolean serviceEnabled = true; @@ -205,14 +188,16 @@ public class SettingsActivity extends Activity { summary = R.string.title_missing_notification_access; } } - mBadgingPref.setButtonOnClickListener(serviceEnabled ? null : this); + mBadgingPref.setWidgetFrameVisible(!serviceEnabled); + mBadgingPref.setOnPreferenceClickListener(serviceEnabled ? null : this); mBadgingPref.setSummary(summary); } @Override - public void onClick(View view) { + public boolean onPreferenceClick(Preference preference) { new NotificationAccessConfirmation().show(mFragmentManager, "notification_access"); + return true; } } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index fd708c0fd5..a7e68ff148 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -101,7 +101,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup { mInvertIfRtl = invert; } - int getCellContentHeight() { + public int getCellContentHeight() { return Math.min(getMeasuredHeight(), mLauncher.getDeviceProfile().getCellHeight(mContainerType)); } diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 45c14d6bba..e15cf9f50a 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -42,7 +42,7 @@ public class UninstallDropTarget extends ButtonDropTarget { return supportsDrop(getContext(), info); } - public static boolean supportsDrop(Context context, Object info) { + public static boolean supportsDrop(Context context, ItemInfo info) { UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); Bundle restrictions = userManager.getUserRestrictions(); if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) @@ -56,21 +56,13 @@ public class UninstallDropTarget extends ButtonDropTarget { /** * @return the component name that should be uninstalled or null. */ - private static ComponentName getUninstallTarget(Context context, Object item) { + private static ComponentName getUninstallTarget(Context context, ItemInfo item) { Intent intent = null; UserHandle user = null; - if (item instanceof AppInfo) { - AppInfo info = (AppInfo) item; - intent = info.intent; - user = info.user; - } else if (item instanceof ShortcutInfo) { - ShortcutInfo info = (ShortcutInfo) item; - if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) { - // Do not use restore/target intent here as we cannot uninstall an app which is - // being installed/restored. - intent = info.intent; - user = info.user; - } + if (item != null && + item.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) { + intent = item.getIntent(); + user = item.user; } if (intent != null) { LauncherActivityInfo info = LauncherAppsCompat.getInstance(context) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a2270d6c57..f8d64984e9 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -671,7 +671,8 @@ public class Workspace extends PagedView newScreen.setSoundEffectsEnabled(false); int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx; - newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, 0); + int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx; + newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); mWorkspaceScreens.put(screenId, newScreen); mScreenOrder.add(insertIndex, screenId); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 32deaf2862..a105a73034 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -273,10 +273,8 @@ public class WorkspaceStateTransitionAnimation { float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ? 1.0f : 0f; float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded || - (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f; - float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f; - float finalQsbAlpha = (states.stateIsNormal || - (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f; + states.stateIsNormalHidden) ? 1f : 0f; + float finalQsbAlpha = (states.stateIsNormal || states.stateIsNormalHidden) ? 1f : 0f; float finalWorkspaceTranslationY = 0; if (states.stateIsOverview || states.stateIsOverviewHidden) { @@ -313,8 +311,7 @@ public class WorkspaceStateTransitionAnimation { if (states.stateIsOverviewHidden) { finalAlpha = 0f; } else if(states.stateIsNormalHidden) { - finalAlpha = (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && - i == mWorkspace.getNextPage()) ? 1 : 0; + finalAlpha = (i == mWorkspace.getNextPage()) ? 1 : 0; } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) { finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; } else { @@ -323,7 +320,7 @@ public class WorkspaceStateTransitionAnimation { // If we are animating to/from the small state, then hide the side pages and fade the // current page in - if (!FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !mWorkspace.isSwitchingState()) { + if (!FeatureFlags.NO_ALL_APPS_ICON && !mWorkspace.isSwitchingState()) { if (states.workspaceToAllApps || states.allAppsToWorkspace) { boolean isCurrentPage = (i == toPage); if (states.allAppsToWorkspace && isCurrentPage) { @@ -359,38 +356,47 @@ public class WorkspaceStateTransitionAnimation { final ViewGroup overviewPanel = mLauncher.getOverviewPanel(); + float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f; if (animated) { + // This is true when transitioning between: + // - Overview <-> Workspace + // - Overview <-> Widget Tray + if (finalOverviewPanelAlpha != overviewPanel.getAlpha()) { + Animator overviewPanelAlpha = ObjectAnimator.ofFloat( + overviewPanel, View.ALPHA, finalOverviewPanelAlpha); + overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel, + accessibilityEnabled)); + layerViews.addView(overviewPanel); + + if (states.overviewToWorkspace) { + overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); + } else if (states.workspaceToOverview) { + overviewPanelAlpha.setInterpolator(null); + } + + overviewPanelAlpha.setDuration(duration); + mStateAnimator.play(overviewPanelAlpha); + } + Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace, new PropertyListBuilder().scale(mNewScale) .translationY(finalWorkspaceTranslationY).build()) .setDuration(duration); scale.setInterpolator(mZoomInInterpolator); mStateAnimator.play(scale); - Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha); - - Animator overviewPanelAlpha = ObjectAnimator.ofFloat( - overviewPanel, View.ALPHA, finalOverviewPanelAlpha); - overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel, - accessibilityEnabled)); // For animation optimization, we may need to provide the Launcher transition // with a set of views on which to force build and manage layers in certain scenarios. - layerViews.addView(overviewPanel); layerViews.addView(mLauncher.getHotseat()); layerViews.addView(mWorkspace.getPageIndicator()); + Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha); if (states.workspaceToOverview) { hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); - overviewPanelAlpha.setInterpolator(null); } else if (states.overviewToWorkspace) { hotseatAlpha.setInterpolator(null); - overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); } - - overviewPanelAlpha.setDuration(duration); hotseatAlpha.setDuration(duration); - - mStateAnimator.play(overviewPanelAlpha); mStateAnimator.play(hotseatAlpha); mStateAnimator.addListener(new AnimatorListenerAdapter() { boolean canceled = false; diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 34335330be..a0ad07aeb1 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -173,7 +173,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme ArrayList itemList = new ArrayList<>(); itemList.add(info); - mLauncher.bindItems(itemList, 0, itemList.size(), true); + mLauncher.bindItems(itemList, true); } else if (item instanceof PendingAddItemInfo) { PendingAddItemInfo info = (PendingAddItemInfo) item; Workspace workspace = mLauncher.getWorkspace(); @@ -205,7 +205,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public void run() { ArrayList itemList = new ArrayList<>(); itemList.add(item); - mLauncher.bindItems(itemList, 0, itemList.size(), true); + mLauncher.bindItems(itemList, true); announceConfirmation(R.string.item_moved); } }); diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java index b7c500fa6e..5b7353aa18 100644 --- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java @@ -73,7 +73,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele screenId, coordinates[0], coordinates[1]); ArrayList itemList = new ArrayList<>(); itemList.add(info); - mLauncher.bindItems(itemList, 0, itemList.size(), true); + mLauncher.bindItems(itemList, true); AbstractFloatingView.closeAllOpenViews(mLauncher); announceConfirmation(R.string.item_added_to_workspace); } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 0083d47f2a..97a87c16cb 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -102,18 +102,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Override protected void updateBackground( int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - getRevealView().setBackground(new InsetDrawable(mBaseDrawable, - paddingLeft, paddingTop, paddingRight, paddingBottom)); - getContentView().setBackground( - new InsetDrawable(new ColorDrawable(Color.TRANSPARENT), - paddingLeft, paddingTop, paddingRight, paddingBottom)); - } else { - getRevealView().setBackground(mBaseDrawable); - } + if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { + getRevealView().setBackground(new InsetDrawable(mBaseDrawable, + paddingLeft, paddingTop, paddingRight, paddingBottom)); + getContentView().setBackground( + new InsetDrawable(new ColorDrawable(Color.TRANSPARENT), + paddingLeft, paddingTop, paddingRight, paddingBottom)); } else { - super.updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom); + getRevealView().setBackground(mBaseDrawable); } } @@ -132,18 +128,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } /** - * Adds new apps to the list. + * Adds or updates existing apps in the list */ - public void addApps(List apps) { - mApps.addApps(apps); - mSearchUiManager.refreshSearchResult(); - } - - /** - * Updates existing apps in the list - */ - public void updateApps(List apps) { - mApps.updateApps(apps); + public void addOrUpdateApps(List apps) { + mApps.addOrUpdateApps(apps); mSearchUiManager.refreshSearchResult(); } @@ -224,6 +212,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); mAppsRecyclerView.setHasFixedSize(true); + // No animations will occur when changes occur to the items in this RecyclerView. + mAppsRecyclerView.setItemAnimator(null); if (FeatureFlags.LAUNCHER3_PHYSICS) { mAppsRecyclerView.setSpringAnimationHandler(mSpringAnimationHandler); } @@ -238,11 +228,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mAppsRecyclerView.preMeasureViews(mAdapter); mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener()); - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { - getRevealView().setVisibility(View.VISIBLE); - getContentView().setVisibility(View.VISIBLE); - getContentView().setBackground(null); - } + getRevealView().setVisibility(View.VISIBLE); + getContentView().setVisibility(View.VISIBLE); + getContentView().setBackground(null); } public SearchUiManager getSearchUiManager() { @@ -260,32 +248,15 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // Update the number of items in the grid before we measure the view grid.updateAppsViewNumCols(); - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { - if (mNumAppsPerRow != grid.inv.numColumns || - mNumPredictedAppsPerRow != grid.inv.numColumns) { - mNumAppsPerRow = grid.inv.numColumns; - mNumPredictedAppsPerRow = grid.inv.numColumns; - - mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow); - mAdapter.setNumAppsPerRow(mNumAppsPerRow); - mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - return; - } - - // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. --- - if (mNumAppsPerRow != grid.allAppsNumCols || - mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) { - mNumAppsPerRow = grid.allAppsNumCols; - mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols; + if (mNumAppsPerRow != grid.inv.numColumns || + mNumPredictedAppsPerRow != grid.inv.numColumns) { + mNumAppsPerRow = grid.inv.numColumns; + mNumPredictedAppsPerRow = grid.inv.numColumns; mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow); mAdapter.setNumAppsPerRow(mNumAppsPerRow); mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); } - - // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. --- super.onMeasure(widthMeasureSpec, heightMeasureSpec); } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 331900cf15..494cd5ac5e 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -110,12 +110,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine } } - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - mPullDetector.onTouchEvent(ev); - return super.onInterceptTouchEvent(rv, ev) || mOverScrollHelper.isInOverScroll(); - } - @Override public boolean onTouchEvent(MotionEvent e) { mPullDetector.onTouchEvent(e); @@ -205,8 +199,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @Override public void onDraw(Canvas c) { - c.translate(0, mContentTranslationY); - // Draw the background if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { mEmptySearchBackground.draw(c); @@ -215,6 +207,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine super.onDraw(c); } + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.translate(0, mContentTranslationY); + super.dispatchDraw(canvas); + canvas.translate(0, -mContentTranslationY); + } + public float getContentTranslationY() { return mContentTranslationY; } @@ -282,7 +281,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @Override public boolean onInterceptTouchEvent(MotionEvent e) { - boolean result = super.onInterceptTouchEvent(e); + mPullDetector.onTouchEvent(e); + boolean result = super.onInterceptTouchEvent(e) || mOverScrollHelper.isInOverScroll(); if (!result && e.getAction() == MotionEvent.ACTION_DOWN && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { mEmptySearchBackground.setHotspot(e.getX(), e.getY()); @@ -339,6 +339,22 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter); } + @Override + protected float getBottomFadingEdgeStrength() { + // No bottom fading edge. + return 0; + } + + @Override + protected boolean isPaddingOffsetRequired() { + return true; + } + + @Override + protected int getTopPaddingOffset() { + return -getPaddingTop(); + } + /** * Updates the bounds for the scrollbar. */ @@ -541,11 +557,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine // and if one of the following criteria are met: // - User scrolls down when they're already at the bottom. // - User starts scrolling up, hits the top, and continues scrolling up. + boolean wasInOverScroll = mIsInOverScroll; mIsInOverScroll = !mScrollbar.isDraggingThumb() && ((!canScrollVertically(1) && displacement < 0) || (!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0)); - if (mIsInOverScroll) { + if (wasInOverScroll && !mIsInOverScroll) { + // Exit overscroll. This can happen when the user is in overscroll and then + // scrolls the opposite way. + reset(false /* shouldSpring */); + } else if (mIsInOverScroll) { if (Float.compare(mFirstDisplacement, 0) == 0) { // Because users can scroll before entering overscroll, we need to // subtract the amount where the user was not in overscroll. @@ -560,11 +581,15 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @Override public void onDragEnd(float velocity, boolean fling) { + reset(mIsInOverScroll /* shouldSpring */); + } + + private void reset(boolean shouldSpring) { float y = getContentTranslationY(); if (Float.compare(y, 0) != 0) { - if (FeatureFlags.LAUNCHER3_PHYSICS) { + if (FeatureFlags.LAUNCHER3_PHYSICS && shouldSpring) { // We calculate our own velocity to give the springs the desired effect. - velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY; + float velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY; // We want to negate the velocity because we are moving to 0 from -1 due to the // downward motion. (y-axis -1 is above 0). mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity); diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 6896b37d99..edfe0c15e9 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -283,7 +283,9 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } // Use a light system UI (dark icons) if all apps is behind at least half of the status bar. - boolean forceChange = shift <= mStatusBarHeight / 2; + boolean forceChange = FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS ? + shift <= mShiftRange / 4 : + shift <= mStatusBarHeight / 2; if (forceChange) { mLauncher.getSystemUiController().updateUiState( SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme); diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 608e898ae8..5e7a5cac56 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -298,14 +298,75 @@ public class AlphabeticalAppsList { updateAdapterItems(); } + private List processPredictedAppComponents(List components) { + if (mComponentToAppMap.isEmpty()) { + // Apps have not been bound yet. + return Collections.emptyList(); + } + + List predictedApps = new ArrayList<>(); + for (ComponentKey ck : components) { + AppInfo info = mComponentToAppMap.get(ck); + if (info != null) { + predictedApps.add(info); + } else { + if (FeatureFlags.IS_DOGFOOD_BUILD) { + Log.e(TAG, "Predicted app not found: " + ck); + } + } + // Stop at the number of predicted apps + if (predictedApps.size() == mNumPredictedAppsPerRow) { + break; + } + } + return predictedApps; + } + /** - * Sets the current set of predicted apps. Since this can be called before we get the full set - * of applications, we should merge the results only in onAppsUpdated() which is idempotent. + * Sets the current set of predicted apps. + * + * This can be called before we get the full set of applications, we should merge the results + * only in onAppsUpdated() which is idempotent. + * + * If the number of predicted apps is the same as the previous list of predicted apps, + * we can optimize by swapping them in place. */ public void setPredictedApps(List apps) { mPredictedAppComponents.clear(); mPredictedAppComponents.addAll(apps); - onAppsUpdated(); + + List newPredictedApps = processPredictedAppComponents(apps); + // We only need to do work if any of the visible predicted apps have changed. + if (!newPredictedApps.equals(mPredictedApps)) { + if (newPredictedApps.size() == mPredictedApps.size()) { + swapInNewPredictedApps(newPredictedApps); + } else { + // We need to update the appIndex of all the items. + onAppsUpdated(); + } + } + } + + /** + * Swaps out the old predicted apps with the new predicted apps, in place. This optimization + * allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged. + * + * Note: This should only be called if the # of predicted apps is the same. + * This method assumes that predicted apps are the first items in the adapter. + */ + private void swapInNewPredictedApps(List apps) { + mPredictedApps.clear(); + mPredictedApps.addAll(apps); + + int size = apps.size(); + for (int i = 0; i < size; ++i) { + AppInfo info = apps.get(i); + AdapterItem appItem = AdapterItem.asPredictedApp(i, "", info, i); + appItem.rowAppIndex = i; + mAdapterItems.set(i, appItem); + mFilteredApps.set(i, info); + mAdapter.notifyItemChanged(i); + } } /** @@ -313,20 +374,13 @@ public class AlphabeticalAppsList { */ public void setApps(List apps) { mComponentToAppMap.clear(); - addApps(apps); + addOrUpdateApps(apps); } /** - * Adds new apps to the list. + * Adds or updates existing apps in the list */ - public void addApps(List apps) { - updateApps(apps); - } - - /** - * Updates existing apps in the list - */ - public void updateApps(List apps) { + public void addOrUpdateApps(List apps) { for (AppInfo app : apps) { mComponentToAppMap.put(app.toComponentKey(), app); } @@ -432,20 +486,7 @@ public class AlphabeticalAppsList { // Process the predicted app components mPredictedApps.clear(); if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { - for (ComponentKey ck : mPredictedAppComponents) { - AppInfo info = mComponentToAppMap.get(ck); - if (info != null) { - mPredictedApps.add(info); - } else { - if (FeatureFlags.IS_DOGFOOD_BUILD) { - Log.e(TAG, "Predicted app not found: " + ck); - } - } - // Stop at the number of predicted apps - if (mPredictedApps.size() == mNumPredictedAppsPerRow) { - break; - } - } + mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents)); if (!mPredictedApps.isEmpty()) { // Add a section for the predictions diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index 39e2088746..d504551712 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -108,8 +108,7 @@ public class AppsSearchContainerLayout extends FrameLayout @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && - !mLauncher.getDeviceProfile().isVerticalBarLayout()) { + if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) { getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index cdbaf708dc..e9dead3997 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -71,10 +71,6 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { return result; } - public static boolean matches(AppInfo info, String query) { - return matches(info, query, StringMatcher.getInstance()); - } - public static boolean matches(AppInfo info, String query, StringMatcher matcher) { int queryLength = query.length(); diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java index 6233fabb2b..fe5ff2a8c6 100644 --- a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java +++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java @@ -20,6 +20,7 @@ import android.app.WallpaperManager; import android.content.Context; import android.graphics.Color; import android.os.Build; +import android.os.Handler; import android.support.annotation.Nullable; import android.util.Log; @@ -48,8 +49,7 @@ public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat { mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener"); mAddOCLMethod = WallpaperManager.class.getDeclaredMethod( - "addOnColorsChangedListener", mOCLClass); - + "addOnColorsChangedListener", mOCLClass, Handler.class); mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class); Class wallpaperColorsClass = mWCGetMethod.getReturnType(); mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor"); @@ -89,7 +89,7 @@ public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat { } }); try { - mAddOCLMethod.invoke(mWm, onChangeListener); + mAddOCLMethod.invoke(mWm, onChangeListener, null); } catch (Exception e) { Log.e(TAG, "Error calling wallpaper API", e); } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 9e0a3b3b5c..456562e2b5 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -34,12 +34,11 @@ abstract class BaseFlags { public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false; public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false; public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false; - public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true; public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true; // When enabled allows to use any point on the fast scrollbar to start dragging. public static final boolean LAUNCHER3_DIRECT_SCROLL = true; // When enabled while all-apps open, the soft input will be set to adjust resize . - public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = true; + public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false; // When enabled the promise icon is visible in all apps while installation an app. public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false; // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index be5f01adbe..ee6a0e0b8c 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -243,7 +243,7 @@ public class DragLayer extends InsettableFrameLayout { return true; } - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && mAllAppsController.onControllerInterceptTouchEvent(ev)) { + if (mAllAppsController.onControllerInterceptTouchEvent(ev)) { mActiveController = mAllAppsController; return true; } diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index b6e38bb15e..e81e2a3863 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -218,6 +218,12 @@ public class DragView extends View { Rect bounds = new Rect(0, 0, w, h); bounds.inset(blurMargin, blurMargin); + // Badge is applied after icon normalization so the bounds for badge should not + // be scaled down due to icon normalization. + Rect badgeBounds = new Rect(bounds); + mBadge = getBadge(info, appState, outObj[0]); + mBadge.setBounds(badgeBounds); + Utilities.scaleRectAboutCenter(bounds, IconNormalizer.getInstance(mLauncher).getScale(dr, null, null, null)); AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr; @@ -234,9 +240,6 @@ public class DragView extends View { mTranslateY = new SpringFloatValue(DragView.this, h * AdaptiveIconDrawable.getExtraInsetFraction()); - mBadge = getBadge(info, appState, outObj[0]); - mBadge.setBounds(bounds); - bounds.inset( (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()), (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction()) diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java index 678396df1f..5455b43ec6 100644 --- a/src/com/android/launcher3/graphics/GradientView.java +++ b/src/com/android/launcher3/graphics/GradientView.java @@ -27,6 +27,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; @@ -46,7 +47,6 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private static final int DEFAULT_COLOR = Color.WHITE; private static final int ALPHA_MASK_HEIGHT_DP = 500; private static final int ALPHA_MASK_WIDTH_DP = 2; - private static final int ALPHA_COLORS = 0xBF; private static final boolean DEBUG = false; private final Bitmap mAlphaGradientMask; @@ -62,7 +62,7 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private final Paint mPaintNoScrim = new Paint(); private float mProgress; private final int mMaskHeight, mMaskWidth; - private final Context mAppContext; + private final int mAlphaColors; private final Paint mDebugPaint = DEBUG ? new Paint() : null; private final Interpolator mAccelerator = new AccelerateInterpolator(); private final float mAlphaStart; @@ -71,15 +71,14 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis public GradientView(Context context, AttributeSet attrs) { super(context, attrs); - this.mAppContext = context.getApplicationContext(); - this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, - mAppContext.getResources().getDisplayMetrics()); - this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, - mAppContext.getResources().getDisplayMetrics()); + DisplayMetrics dm = getResources().getDisplayMetrics(); + this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); + this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); Launcher launcher = Launcher.getLauncher(context); this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100; this.mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor); this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher); + mAlphaColors = getResources().getInteger(R.integer.extracted_color_gradient_alpha); updateColors(); mAlphaGradientMask = createDitheredAlphaMask(); } @@ -104,9 +103,9 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private void updateColors() { this.mColor1 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getMainColor(), - ALPHA_COLORS); + mAlphaColors); this.mColor2 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getSecondaryColor(), - ALPHA_COLORS); + mAlphaColors); if (mWidth + mHeight > 0) { createRadialShader(); } diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java index 34d0b727c1..8ed62bcdcc 100644 --- a/src/com/android/launcher3/graphics/IconNormalizer.java +++ b/src/com/android/launcher3/graphics/IconNormalizer.java @@ -32,10 +32,8 @@ import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; - import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; - import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; @@ -84,12 +82,12 @@ public class IconNormalizer { private final Rect mBounds; private final Matrix mMatrix; - private Paint mPaintIcon; - private Canvas mCanvasARGB; + private final Paint mPaintIcon; + private final Canvas mCanvasARGB; - private File mDir; + private final File mDir; private int mFileId; - private Random mRandom; + private final Random mRandom; private IconNormalizer(Context context) { // Use twice the icon size as maximum size to avoid scaling down twice. @@ -122,7 +120,6 @@ public class IconNormalizer { mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); mMatrix = new Matrix(); - int[] mPixels = new int[mMaxSize * mMaxSize]; mAdaptiveIconScale = SCALE_NOT_INITIALIZED; mDir = context.getExternalFilesDir(null); @@ -174,7 +171,8 @@ public class IconNormalizer { boolean isTrans = isTransparentBitmap(mBitmapARGB); if (DEBUG) { - final File afterFile = new File(mDir, "isShape" + mFileId + "_after_" + isTrans + ".png"); + final File afterFile = new File(mDir, + "isShape" + mFileId + "_after_" + isTrans + ".png"); try { mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(afterFile)); @@ -210,7 +208,9 @@ public class IconNormalizer { float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; if (DEBUG) { - Log.d(TAG, "Total # pixel that is different (id="+ mFileId + "):" + percentageDiffPixels + "="+ sum + "/" + mBounds.width() * mBounds.height()); + Log.d(TAG, + "Total # pixel that is different (id=" + mFileId + "):" + percentageDiffPixels + + "=" + sum + "/" + mBounds.width() * mBounds.height()); } return transparentImage; } diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java index 4c83e9ac22..f7f8ef18f6 100644 --- a/src/com/android/launcher3/logging/FileLog.java +++ b/src/com/android/launcher3/logging/FileLog.java @@ -29,6 +29,8 @@ import java.util.concurrent.TimeUnit; */ public final class FileLog { + protected static final boolean ENABLED = + FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE; private static final String FILE_NAME_PREFIX = "log-"; private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); @@ -39,7 +41,7 @@ public final class FileLog { private static File sLogsDirectory = null; public static void setDir(File logsDir) { - if (FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) { + if (ENABLED) { synchronized (DATE_FORMAT) { // If the target directory changes, stop any active thread. if (sHandler != null && !logsDir.equals(sLogsDirectory)) { @@ -76,7 +78,7 @@ public final class FileLog { } public static void print(String tag, String msg, Exception e) { - if (!FeatureFlags.IS_DOGFOOD_BUILD) { + if (!ENABLED) { return; } String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg); @@ -102,7 +104,7 @@ public final class FileLog { * @param out if not null, all the persisted logs are copied to the writer. */ public static void flushAll(PrintWriter out) throws InterruptedException { - if (!FeatureFlags.IS_DOGFOOD_BUILD) { + if (!ENABLED) { return; } CountDownLatch latch = new CountDownLatch(1); @@ -135,7 +137,7 @@ public final class FileLog { @Override public boolean handleMessage(Message msg) { - if (sLogsDirectory == null || !FeatureFlags.IS_DOGFOOD_BUILD) { + if (sLogsDirectory == null || !ENABLED) { return true; } switch (msg.what) { diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 68012c4ba1..42926fa3ea 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -158,7 +158,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { } } callbacks.bindAppsAdded(addedWorkspaceScreensFinal, - addNotAnimated, addAnimated, null); + addNotAnimated, addAnimated); } }); } diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index 9b4510fcae..d5b5aa7cf3 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -26,6 +26,7 @@ import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; import java.util.ArrayList; @@ -94,19 +95,12 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { public void bindUpdatedShortcuts( - ArrayList updatedShortcuts, UserHandle user) { - bindUpdatedShortcuts(updatedShortcuts, new ArrayList(), user); - } - - public void bindUpdatedShortcuts( - final ArrayList updatedShortcuts, - final ArrayList removedShortcuts, - final UserHandle user) { - if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) { + final ArrayList updatedShortcuts, final UserHandle user) { + if (!updatedShortcuts.isEmpty()) { scheduleCallbackTask(new CallbackTask() { @Override public void execute(Callbacks callbacks) { - callbacks.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user); + callbacks.bindShortcutsChanged(updatedShortcuts, user); } }); } @@ -132,4 +126,16 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { } }); } + + public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) { + getModelWriter().deleteItemsFromDatabase(matcher); + + // Call the components-removed callback + scheduleCallbackTask(new CallbackTask() { + @Override + public void execute(Callbacks callbacks) { + callbacks.bindWorkspaceComponentsRemoved(matcher); + } + }); + } } diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java index 8597e10486..7a27741c33 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -77,7 +77,7 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask { scheduleCallbackTask(new CallbackTask() { @Override public void execute(Callbacks callbacks) { - callbacks.bindAppsUpdated(updatedApps); + callbacks.bindAppsAddedOrUpdated(updatedApps); } }); } diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java index 0df8b6fee2..b7a6b68e83 100644 --- a/src/com/android/launcher3/model/LoaderResults.java +++ b/src/com/android/launcher3/model/LoaderResults.java @@ -122,7 +122,7 @@ public class LoaderResults { filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); - filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets, + filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets, otherAppWidgets); sortWorkspaceItemsSpatially(currentWorkspaceItems); sortWorkspaceItemsSpatially(otherWorkspaceItems); @@ -208,12 +208,12 @@ public class LoaderResults { /** Filters the set of items who are directly or indirectly (via another container) on the * specified screen. */ - private void filterCurrentWorkspaceItems(long currentScreenId, - ArrayList allWorkspaceItems, - ArrayList currentScreenItems, - ArrayList otherScreenItems) { + private void filterCurrentWorkspaceItems(long currentScreenId, + ArrayList allWorkspaceItems, + ArrayList currentScreenItems, + ArrayList otherScreenItems) { // Purge any null ItemInfos - Iterator iter = allWorkspaceItems.iterator(); + Iterator iter = allWorkspaceItems.iterator(); while (iter.hasNext()) { ItemInfo i = iter.next(); if (i == null) { @@ -224,14 +224,14 @@ public class LoaderResults { // Order the set of items by their containers first, this allows use to walk through the // list sequentially, build up a list of containers that are in the specified screen, // as well as all items in those containers. - Set itemsOnScreen = new HashSet(); + Set itemsOnScreen = new HashSet<>(); Collections.sort(allWorkspaceItems, new Comparator() { @Override public int compare(ItemInfo lhs, ItemInfo rhs) { return Utilities.longCompare(lhs.container, rhs.container); } }); - for (ItemInfo info : allWorkspaceItems) { + for (T info : allWorkspaceItems) { if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { if (info.screenId == currentScreenId) { currentScreenItems.add(info); @@ -253,23 +253,6 @@ public class LoaderResults { } } - /** Filters the set of widgets which are on the specified screen. */ - private void filterCurrentAppWidgets(long currentScreenId, - ArrayList appWidgets, - ArrayList currentScreenWidgets, - ArrayList otherScreenWidgets) { - - for (LauncherAppWidgetInfo widget : appWidgets) { - if (widget == null) continue; - if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - widget.screenId == currentScreenId) { - currentScreenWidgets.add(widget); - } else { - otherScreenWidgets.add(widget); - } - } - } - /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to * right) */ private void sortWorkspaceItemsSpatially(ArrayList workspaceItems) { @@ -322,7 +305,7 @@ public class LoaderResults { public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { - callbacks.bindItems(workspaceItems, start, start+chunkSize, false); + callbacks.bindItems(workspaceItems.subList(start, start+chunkSize), false); } } }; @@ -332,12 +315,12 @@ public class LoaderResults { // Bind the widgets, one at a time N = appWidgets.size(); for (int i = 0; i < N; i++) { - final LauncherAppWidgetInfo widget = appWidgets.get(i); + final ItemInfo widget = appWidgets.get(i); final Runnable r = new Runnable() { public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { - callbacks.bindAppWidget(widget); + callbacks.bindItems(Collections.singletonList(widget), false); } } }; diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 1b2f8d63fa..6c78d5bfca 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -43,6 +43,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.ItemInfoMatcher; +import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; @@ -147,55 +148,32 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { break; } - ArrayList added = null; - ArrayList modified = null; - final ArrayList removedApps = new ArrayList<>(); + final ArrayList addedOrModified = new ArrayList<>(); + addedOrModified.addAll(appsList.added); + appsList.added.clear(); + addedOrModified.addAll(appsList.modified); + appsList.modified.clear(); - if (appsList.added.size() > 0) { - added = new ArrayList<>(appsList.added); - appsList.added.clear(); - } - if (appsList.modified.size() > 0) { - modified = new ArrayList<>(appsList.modified); - appsList.modified.clear(); - } - if (appsList.removed.size() > 0) { - removedApps.addAll(appsList.removed); - appsList.removed.clear(); - } + final ArrayList removedApps = new ArrayList<>(appsList.removed); + appsList.removed.clear(); final ArrayMap addedOrUpdatedApps = new ArrayMap<>(); - - if (added != null) { - final ArrayList addedApps = added; + if (!addedOrModified.isEmpty()) { scheduleCallbackTask(new CallbackTask() { @Override public void execute(Callbacks callbacks) { - callbacks.bindAppsAdded(null, null, null, addedApps); + callbacks.bindAppsAddedOrUpdated(addedOrModified); } }); - for (AppInfo ai : added) { + for (AppInfo ai : addedOrModified) { addedOrUpdatedApps.put(ai.componentName, ai); } } - if (modified != null) { - final ArrayList modifiedFinal = modified; - for (AppInfo ai : modified) { - addedOrUpdatedApps.put(ai.componentName, ai); - } - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindAppsUpdated(modifiedFinal); - } - }); - } - // Update shortcut infos if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) { final ArrayList updatedShortcuts = new ArrayList<>(); - final ArrayList removedShortcuts = new ArrayList<>(); + final LongArrayMap removedShortcuts = new LongArrayMap<>(); final ArrayList widgets = new ArrayList<>(); synchronized (dataModel) { @@ -236,7 +214,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { } if ((intent == null) || (appInfo == null)) { - removedShortcuts.add(si); + removedShortcuts.put(si.id, true); continue; } si.intent = intent; @@ -290,9 +268,9 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { } } - bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser); + bindUpdatedShortcuts(updatedShortcuts, mUser); if (!removedShortcuts.isEmpty()) { - getModelWriter().deleteItemsFromDatabase(removedShortcuts); + deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false)); } if (!widgets.isEmpty()) { @@ -329,22 +307,12 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { } if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) { - getModelWriter().deleteItemsFromDatabase( - ItemInfoMatcher.ofPackages(removedPackages, mUser)); - getModelWriter().deleteItemsFromDatabase( - ItemInfoMatcher.ofComponents(removedComponents, mUser)); + ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser) + .or(ItemInfoMatcher.ofComponents(removedComponents, mUser)); + deleteAndBindComponentsRemoved(removeMatch); // Remove any queued items from the install queue InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser); - - // Call the components-removed callback - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindWorkspaceComponentsRemoved( - removedPackages, removedComponents, mUser); - } - }); } if (!removedApps.isEmpty()) { diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java index 17cc238d42..c1f33a6b2c 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -26,9 +26,12 @@ import com.android.launcher3.ShortcutInfo; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutInfoCompat; +import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; /** @@ -56,33 +59,35 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask { deepShortcutManager.onShortcutsChanged(mShortcuts); // Find ShortcutInfo's that have changed on the workspace. - final ArrayList removedShortcutInfos = new ArrayList<>(); - MultiHashMap idsToWorkspaceShortcutInfos = new MultiHashMap<>(); + HashSet removedKeys = new HashSet<>(); + MultiHashMap keyToShortcutInfo = new MultiHashMap<>(); + HashSet allIds = new HashSet<>(); + for (ItemInfo itemInfo : dataModel.itemsIdMap) { if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { ShortcutInfo si = (ShortcutInfo) itemInfo; - if (si.getIntent().getPackage().equals(mPackageName) - && si.user.equals(mUser)) { - idsToWorkspaceShortcutInfos.addToList(si.getDeepShortcutId(), si); + if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) { + keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si); + allIds.add(si.getDeepShortcutId()); } } } final ArrayList updatedShortcutInfos = new ArrayList<>(); - if (!idsToWorkspaceShortcutInfos.isEmpty()) { + if (!keyToShortcutInfo.isEmpty()) { // Update the workspace to reflect the changes to updated shortcuts residing on it. List shortcuts = deepShortcutManager.queryForFullDetails( - mPackageName, new ArrayList<>(idsToWorkspaceShortcutInfos.keySet()), mUser); + mPackageName, new ArrayList<>(allIds), mUser); for (ShortcutInfoCompat fullDetails : shortcuts) { - List shortcutInfos = idsToWorkspaceShortcutInfos - .remove(fullDetails.getId()); + ShortcutKey key = ShortcutKey.fromInfo(fullDetails); + List shortcutInfos = keyToShortcutInfo.remove(key); if (!fullDetails.isPinned()) { // The shortcut was previously pinned but is no longer, so remove it from // the workspace and our pinned shortcut counts. // Note that we put this check here, after querying for full details, // because there's a possible race condition between pinning and // receiving this callback. - removedShortcutInfos.addAll(shortcutInfos); + removedKeys.add(key); continue; } for (final ShortcutInfo shortcutInfo : shortcutInfos) { @@ -94,16 +99,14 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask { } } - // If there are still entries in idsToWorkspaceShortcutInfos, that means that + // If there are still entries in keyToShortcutInfo, that means that // the corresponding shortcuts weren't passed in onShortcutsChanged(). This // means they were cleared, so we remove and unpin them now. - for (String id : idsToWorkspaceShortcutInfos.keySet()) { - removedShortcutInfos.addAll(idsToWorkspaceShortcutInfos.get(id)); - } + removedKeys.addAll(keyToShortcutInfo.keySet()); - bindUpdatedShortcuts(updatedShortcutInfos, removedShortcutInfos, mUser); - if (!removedShortcutInfos.isEmpty()) { - getModelWriter().deleteItemsFromDatabase(removedShortcutInfos); + bindUpdatedShortcuts(updatedShortcutInfos, mUser); + if (!keyToShortcutInfo.isEmpty()) { + deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys)); } if (mUpdateIdMap) { diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index 802771f048..8170f9a67f 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -29,9 +29,11 @@ import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutInfoCompat; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ItemInfoMatcher; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -70,17 +72,19 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { // Update the workspace to reflect the changes to updated shortcuts residing on it. ArrayList updatedShortcutInfos = new ArrayList<>(); - ArrayList deletedShortcutInfos = new ArrayList<>(); + HashSet removedKeys = new HashSet<>(); + for (ItemInfo itemInfo : dataModel.itemsIdMap) { if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT && mUser.equals(itemInfo.user)) { ShortcutInfo si = (ShortcutInfo) itemInfo; if (isUserUnlocked) { - ShortcutInfoCompat shortcut = pinnedShortcuts.get(ShortcutKey.fromItemInfo(si)); + ShortcutKey key = ShortcutKey.fromItemInfo(si); + ShortcutInfoCompat shortcut = pinnedShortcuts.get(key); // We couldn't verify the shortcut during loader. If its no longer available // (probably due to clear data), delete the workspace item as well if (shortcut == null) { - deletedShortcutInfos.add(si); + removedKeys.add(key); continue; } si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER; @@ -93,9 +97,9 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { updatedShortcutInfos.add(si); } } - bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser); - if (!deletedShortcutInfos.isEmpty()) { - getModelWriter().deleteItemsFromDatabase(deletedShortcutInfos); + bindUpdatedShortcuts(updatedShortcutInfos, mUser); + if (!removedKeys.isEmpty()) { + deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys)); } // Remove shortcut id map for that user diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java index 73d89aa189..6a7098915a 100644 --- a/src/com/android/launcher3/notification/NotificationListener.java +++ b/src/com/android/launcher3/notification/NotificationListener.java @@ -30,15 +30,20 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.Pair; + import com.android.launcher3.LauncherModel; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.SettingsObserver; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING; + /** * A {@link NotificationListenerService} that sends updates to its * {@link NotificationsChangedListener} when notifications are posted or canceled, @@ -57,12 +62,14 @@ public class NotificationListener extends NotificationListenerService { private static NotificationListener sNotificationListenerInstance = null; private static NotificationsChangedListener sNotificationsChangedListener; private static boolean sIsConnected; + private static boolean sIsCreated; private final Handler mWorkerHandler; private final Handler mUiHandler; - private final Ranking mTempRanking = new Ranking(); + private SettingsObserver mNotificationBadgingObserver; + private final Handler.Callback mWorkerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message message) { @@ -77,7 +84,7 @@ public class NotificationListener extends NotificationListenerService { List activeNotifications; if (sIsConnected) { try { - activeNotifications = filterNotifications(getActiveNotifications()); + activeNotifications = filterNotifications(getActiveNotifications()); } catch (SecurityException ex) { Log.e(TAG, "SecurityException: failed to fetch notifications"); activeNotifications = new ArrayList(); @@ -130,6 +137,28 @@ public class NotificationListener extends NotificationListenerService { sNotificationListenerInstance = this; } + @Override + public void onCreate() { + super.onCreate(); + sIsCreated = true; + mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) { + @Override + public void onSettingChanged(boolean isNotificationBadgingEnabled) { + if (!isNotificationBadgingEnabled) { + requestUnbind(); + } + } + }; + mNotificationBadgingObserver.register(NOTIFICATION_BADGING); + } + + @Override + public void onDestroy() { + super.onDestroy(); + sIsCreated = false; + mNotificationBadgingObserver.unregister(); + } + public static @Nullable NotificationListener getInstanceIfConnected() { return sIsConnected ? sNotificationListenerInstance : null; } @@ -143,6 +172,11 @@ public class NotificationListener extends NotificationListenerService { NotificationListener notificationListener = getInstanceIfConnected(); if (notificationListener != null) { notificationListener.onNotificationFullRefresh(); + } else if (!sIsCreated && sNotificationsChangedListener != null) { + // User turned off badging globally, so we unbound this service; + // tell the listener that there are no notifications to remove dots. + sNotificationsChangedListener.onNotificationFullRefresh( + Collections.emptyList()); } } @@ -205,7 +239,7 @@ public class NotificationListener extends NotificationListenerService { .getActiveNotifications(NotificationKeyData.extractKeysOnly(keys) .toArray(new String[keys.size()])); return notifications == null - ? Collections.emptyList() : Arrays.asList(notifications); + ? Collections.emptyList() : Arrays.asList(notifications); } /** diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java index 42de284665..18787b6a2a 100644 --- a/src/com/android/launcher3/util/ItemInfoMatcher.java +++ b/src/com/android/launcher3/util/ItemInfoMatcher.java @@ -18,6 +18,7 @@ package com.android.launcher3.util; import android.content.ComponentName; import android.os.UserHandle; +import android.util.SparseLongArray; import com.android.launcher3.FolderInfo; import com.android.launcher3.ItemInfo; @@ -66,6 +67,32 @@ public abstract class ItemInfoMatcher { return filtered; } + /** + * Returns a new matcher with returns true if either this or {@param matcher} returns true. + */ + public ItemInfoMatcher or(final ItemInfoMatcher matcher) { + final ItemInfoMatcher that = this; + return new ItemInfoMatcher() { + @Override + public boolean matches(ItemInfo info, ComponentName cn) { + return that.matches(info, cn) || matcher.matches(info, cn); + } + }; + } + + /** + * Returns a new matcher with returns true if both this and {@param matcher} returns true. + */ + public ItemInfoMatcher and(final ItemInfoMatcher matcher) { + final ItemInfoMatcher that = this; + return new ItemInfoMatcher() { + @Override + public boolean matches(ItemInfo info, ComponentName cn) { + return that.matches(info, cn) && matcher.matches(info, cn); + } + }; + } + public static ItemInfoMatcher ofUser(final UserHandle user) { return new ItemInfoMatcher() { @Override @@ -104,4 +131,14 @@ public abstract class ItemInfoMatcher { } }; } + + public static ItemInfoMatcher ofItemIds( + final LongArrayMap ids, final Boolean matchDefault) { + return new ItemInfoMatcher() { + @Override + public boolean matches(ItemInfo info, ComponentName cn) { + return ids.get(info.id, matchDefault); + } + }; + } } diff --git a/src/com/android/launcher3/util/RunnableWithId.java b/src/com/android/launcher3/util/RunnableWithId.java new file mode 100644 index 0000000000..030eb09ff3 --- /dev/null +++ b/src/com/android/launcher3/util/RunnableWithId.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +/** + * A runnable with an id associated which is used for equality check. + */ +public abstract class RunnableWithId implements Runnable { + + public static final int RUNNABLE_ID_BIND_APPS = 1; + public static final int RUNNABLE_ID_BIND_WIDGETS = 2; + + public final int id; + + public RunnableWithId(int id) { + this.id = id; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof RunnableWithId && ((RunnableWithId) obj).id == id; + } +} diff --git a/src/com/android/launcher3/util/SettingsObserver.java b/src/com/android/launcher3/util/SettingsObserver.java new file mode 100644 index 0000000000..6baa24293e --- /dev/null +++ b/src/com/android/launcher3/util/SettingsObserver.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.util; + +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; + +public interface SettingsObserver { + + /** + * Registers the content observer to call {@link #onSettingChanged(boolean)} when any of the + * passed settings change. The value passed to onSettingChanged() is based on the key setting. + */ + void register(String keySetting, String ... dependentSettings); + void unregister(); + void onSettingChanged(boolean keySettingEnabled); + + + abstract class Secure extends ContentObserver implements SettingsObserver { + private ContentResolver mResolver; + private String mKeySetting; + + public Secure(ContentResolver resolver) { + super(new Handler()); + mResolver = resolver; + } + + @Override + public void register(String keySetting, String ... dependentSettings) { + mKeySetting = keySetting; + mResolver.registerContentObserver( + Settings.Secure.getUriFor(mKeySetting), false, this); + for (String setting : dependentSettings) { + mResolver.registerContentObserver( + Settings.Secure.getUriFor(setting), false, this); + } + onChange(true); + } + + @Override + public void unregister() { + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + onSettingChanged(Settings.Secure.getInt(mResolver, mKeySetting, 1) == 1); + } + } + + abstract class System extends ContentObserver implements SettingsObserver { + private ContentResolver mResolver; + private String mKeySetting; + + public System(ContentResolver resolver) { + super(new Handler()); + mResolver = resolver; + } + + @Override + public void register(String keySetting, String ... dependentSettings) { + mKeySetting = keySetting; + mResolver.registerContentObserver( + Settings.System.getUriFor(mKeySetting), false, this); + for (String setting : dependentSettings) { + mResolver.registerContentObserver( + Settings.System.getUriFor(setting), false, this); + } + onChange(true); + } + + @Override + public void unregister() { + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + onSettingChanged(Settings.System.getInt(mResolver, mKeySetting, 1) == 1); + } + } +} diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java index 6b5b095c78..d7a2625e94 100644 --- a/src/com/android/launcher3/util/SystemUiController.java +++ b/src/com/android/launcher3/util/SystemUiController.java @@ -30,6 +30,7 @@ public class SystemUiController { public static final int UI_STATE_BASE_WINDOW = 0; public static final int UI_STATE_ALL_APPS = 1; public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2; + public static final int UI_STATE_ROOT_VIEW = 3; public static final int FLAG_LIGHT_NAV = 1 << 0; public static final int FLAG_DARK_NAV = 1 << 1; @@ -37,7 +38,7 @@ public class SystemUiController { public static final int FLAG_DARK_STATUS = 1 << 3; private final Window mWindow; - private final int[] mStates = new int[3]; + private final int[] mStates = new int[4]; public SystemUiController(Window window) { mWindow = window; diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java index 4cb6ca8314..e5c1dd1b43 100644 --- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java +++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java @@ -16,11 +16,13 @@ package com.android.launcher3.util; +import android.os.Process; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewTreeObserver.OnDrawListener; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherModel; import java.util.ArrayList; import java.util.concurrent.Executor; @@ -37,6 +39,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, private Launcher mLauncher; private View mAttachedView; private boolean mCompleted; + private boolean mIsExecuting; private boolean mLoadAnimationCompleted; private boolean mFirstDrawCompleted; @@ -62,6 +65,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, @Override public void execute(Runnable command) { mTasks.add(command); + LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND); } @Override @@ -78,6 +82,13 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, mAttachedView.post(this); } + /** + * Returns whether the executor is still queuing tasks and hasn't yet executed them. + */ + public boolean canQueue() { + return !mIsExecuting && !mCompleted; + } + public void onLoadAnimationCompleted() { mLoadAnimationCompleted = true; if (mAttachedView != null) { @@ -89,6 +100,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, public void run() { // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called. if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) { + mIsExecuting = true; for (final Runnable r : mTasks) { mExecutor.execute(r); } @@ -99,6 +111,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, public void markCompleted() { mTasks.clear(); mCompleted = true; + mIsExecuting = false; if (mAttachedView != null) { mAttachedView.getViewTreeObserver().removeOnDrawListener(this); mAttachedView.removeOnAttachStateChangeListener(this); @@ -106,5 +119,6 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, if (mLauncher != null) { mLauncher.clearPendingExecutor(this); } + LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT); } } diff --git a/src/com/android/launcher3/views/ButtonPreference.java b/src/com/android/launcher3/views/ButtonPreference.java index 4697e25e44..fdcf2ca5ba 100644 --- a/src/com/android/launcher3/views/ButtonPreference.java +++ b/src/com/android/launcher3/views/ButtonPreference.java @@ -28,7 +28,7 @@ import android.view.ViewGroup; */ public class ButtonPreference extends Preference { - private View.OnClickListener mClickListener; + private boolean mWidgetFrameVisible = false; public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); @@ -46,9 +46,9 @@ public class ButtonPreference extends Preference { super(context); } - public void setButtonOnClickListener(View.OnClickListener clickListener) { - if (mClickListener != clickListener) { - mClickListener = clickListener; + public void setWidgetFrameVisible(boolean isVisible) { + if (mWidgetFrameVisible != isVisible) { + mWidgetFrameVisible = isVisible; notifyChanged(); } } @@ -59,12 +59,7 @@ public class ButtonPreference extends Preference { ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame); if (widgetFrame != null) { - View button = widgetFrame.getChildAt(0); - if (button != null) { - button.setOnClickListener(mClickListener); - } - widgetFrame.setVisibility( - (mClickListener == null || button == null) ? View.GONE : View.VISIBLE); + widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE); } } } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 0b4bf628e9..01101ac74c 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -88,7 +88,8 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); mInsets = new Rect(); mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); - mGradientBackground = (GradientView) mLauncher.findViewById(R.id.gradient_bg); + mGradientBackground = (GradientView) mLauncher.getLayoutInflater().inflate( + R.layout.gradient_bg, mLauncher.getDragLayer(), false); } @Override @@ -106,6 +107,8 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab onWidgetsBound(); + mLauncher.getDragLayer().addView(mGradientBackground); + mGradientBackground.setVisibility(VISIBLE); mLauncher.getDragLayer().addView(this); measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); setTranslationY(mTranslationYClosed); @@ -212,11 +215,8 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mIsOpen = false; mSwipeDetector.finishedScrolling(); - ((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this); - mLauncher.getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); + onCloseComplete(); } }); mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState() @@ -224,12 +224,18 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab mOpenCloseAnimator.start(); } else { setTranslationY(mTranslationYClosed); - mLauncher.getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); - mIsOpen = false; + onCloseComplete(); } } + private void onCloseComplete() { + mIsOpen = false; + mLauncher.getDragLayer().removeView(mGradientBackground); + mLauncher.getDragLayer().removeView(WidgetsBottomSheet.this); + mLauncher.getSystemUiController().updateUiState( + SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); + } + @Override protected boolean isOfType(@FloatingViewType int type) { return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0; diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java deleted file mode 100644 index 230d623e90..0000000000 --- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.android.launcher3; - -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import java.util.ArrayList; - -/** - * Tests the {@link DeviceProfile} and {@link InvariantDeviceProfile}. - */ -@SmallTest -public class InvariantDeviceProfileTest extends AndroidTestCase { - - private static final String TAG = "DeviceProfileTest"; - private static final boolean DEBUG = false; - - private InvariantDeviceProfile mInvariantProfile; - private ArrayList mPredefinedDeviceProfiles; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mInvariantProfile = new InvariantDeviceProfile(getContext()); - mPredefinedDeviceProfiles = mInvariantProfile.getPredefinedDeviceProfiles(getContext()); - } - - @Override - protected void tearDown() throws Exception { - // Nothing to tear down as this class only tests static methods. - } - - public void testFindClosestDeviceProfile2() { - for (InvariantDeviceProfile idf: mPredefinedDeviceProfiles) { - ArrayList predefinedProfilesCopy = - new ArrayList<>(mPredefinedDeviceProfiles); - ArrayList closestProfiles = - mInvariantProfile.findClosestDeviceProfiles( - idf.minWidthDps, idf.minHeightDps, predefinedProfilesCopy - ); - assertTrue(closestProfiles.get(0).equals(idf)); - } - } - - /** - * Used to print out how the invDistWeightedInterpolate works between device profiles to - * tweak the two constants that control how the interpolation curve is shaped. - */ - public void testInvInterpolation() { - - InvariantDeviceProfile p1 = mPredefinedDeviceProfiles.get(7); // e.g., Large Phone - InvariantDeviceProfile p2 = mPredefinedDeviceProfiles.get(8); // e.g., Nexus 7 - - ArrayList pts = createInterpolatedPoints( - new PointF(p1.minWidthDps, p1.minHeightDps), - new PointF(p2.minWidthDps, p2.minHeightDps), - 20f); - - for (int i = 0; i < pts.size(); i++) { - ArrayList closestProfiles = - mInvariantProfile.findClosestDeviceProfiles( - pts.get(i).x, pts.get(i).y, mPredefinedDeviceProfiles); - InvariantDeviceProfile result = - mInvariantProfile.invDistWeightedInterpolate( - pts.get(i).x, pts.get(i).y, closestProfiles); - if (DEBUG) { - Log.d(TAG, String.format("width x height = (%f, %f)] iconSize = %f", - pts.get(i).x, pts.get(i).y, result.iconSize)); - } - } - } - - private ArrayList createInterpolatedPoints(PointF a, PointF b, float numPts) { - ArrayList result = new ArrayList(); - result.add(a); - for (float i = 1; i < numPts; i = i + 1.0f) { - result.add(new PointF((b.x * i + a.x * (numPts - i)) / numPts, - (b.y * i + a.y * (numPts - i)) / numPts)); - } - result.add(b); - return result; - } - - /** - * Ensures that system calls (e.g., WindowManager, DisplayMetrics) that require contexts are - * properly working to generate minimum width and height of the display. - */ - public void test_hammerhead() { - if (!android.os.Build.DEVICE.equals("hammerhead")) { - return; - } - assertEquals(4, mInvariantProfile.numRows); - assertEquals(4, mInvariantProfile.numColumns); - assertEquals(5, mInvariantProfile.numHotseatIcons); - } - - // Add more tests for other devices, however, running them once on a single device is enough - // for verifying that for a platform version, the WindowManager and DisplayMetrics is - // working as intended. -} diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java index 26ec69b1c7..846a163700 100644 --- a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java +++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java @@ -16,85 +16,78 @@ package com.android.launcher3.allapps.search; import android.content.ComponentName; -import android.test.InstrumentationTestCase; +import android.support.test.runner.AndroidJUnit4; import com.android.launcher3.AppInfo; import com.android.launcher3.Utilities; -import java.util.ArrayList; -import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * Unit tests for {@link DefaultAppSearchAlgorithm} */ -public class DefaultAppSearchAlgorithmTest extends InstrumentationTestCase { - - private List mAppsList; - private DefaultAppSearchAlgorithm mAlgorithm; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mAppsList = new ArrayList<>(); - getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - mAlgorithm = new DefaultAppSearchAlgorithm(mAppsList); - } - }); - } +@RunWith(AndroidJUnit4.class) +public class DefaultAppSearchAlgorithmTest { + private static final DefaultAppSearchAlgorithm.StringMatcher MATCHER = + DefaultAppSearchAlgorithm.StringMatcher.getInstance(); + @Test public void testMatches() { - assertTrue(mAlgorithm.matches(getInfo("white cow"), "cow")); - assertTrue(mAlgorithm.matches(getInfo("whiteCow"), "cow")); - assertTrue(mAlgorithm.matches(getInfo("whiteCOW"), "cow")); - assertTrue(mAlgorithm.matches(getInfo("whitecowCOW"), "cow")); - assertTrue(mAlgorithm.matches(getInfo("white2cow"), "cow")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("white cow"), "cow", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whiteCow"), "cow", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whiteCOW"), "cow", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecowCOW"), "cow", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("white2cow"), "cow", MATCHER)); - assertFalse(mAlgorithm.matches(getInfo("whitecow"), "cow")); - assertFalse(mAlgorithm.matches(getInfo("whitEcow"), "cow")); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitecow"), "cow", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitEcow"), "cow", MATCHER)); - assertTrue(mAlgorithm.matches(getInfo("whitecowCow"), "cow")); - assertTrue(mAlgorithm.matches(getInfo("whitecow cow"), "cow")); - assertFalse(mAlgorithm.matches(getInfo("whitecowcow"), "cow")); - assertFalse(mAlgorithm.matches(getInfo("whit ecowcow"), "cow")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecowCow"), "cow", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecow cow"), "cow", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitecowcow"), "cow", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whit ecowcow"), "cow", MATCHER)); - assertTrue(mAlgorithm.matches(getInfo("cats&dogs"), "dog")); - assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "dog")); - assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "&")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&dogs"), "dog", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&Dogs"), "dog", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&Dogs"), "&", MATCHER)); - assertTrue(mAlgorithm.matches(getInfo("2+43"), "43")); - assertFalse(mAlgorithm.matches(getInfo("2+43"), "3")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("2+43"), "43", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("2+43"), "3", MATCHER)); - assertTrue(mAlgorithm.matches(getInfo("Q"), "q")); - assertTrue(mAlgorithm.matches(getInfo(" Q"), "q")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("Q"), "q", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo(" Q"), "q", MATCHER)); // match lower case words - assertTrue(mAlgorithm.matches(getInfo("elephant"), "e")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("elephant"), "e", MATCHER)); - assertTrue(mAlgorithm.matches(getInfo("电子邮件"), "电")); - assertTrue(mAlgorithm.matches(getInfo("电子邮件"), "电子")); - assertFalse(mAlgorithm.matches(getInfo("电子邮件"), "子")); - assertFalse(mAlgorithm.matches(getInfo("电子邮件"), "邮件")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "电", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "电子", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "子", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "邮件", MATCHER)); - assertFalse(mAlgorithm.matches(getInfo("Bot"), "ba")); - assertFalse(mAlgorithm.matches(getInfo("bot"), "ba")); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("Bot"), "ba", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("bot"), "ba", MATCHER)); } + @Test public void testMatchesVN() { if (!Utilities.ATLEAST_NOUGAT) { return; } - assertTrue(mAlgorithm.matches(getInfo("다운로드"), "다")); - assertTrue(mAlgorithm.matches(getInfo("드라이브"), "드")); - assertTrue(mAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ")); - assertTrue(mAlgorithm.matches(getInfo("운로 드라이브"), "ㄷ")); - assertTrue(mAlgorithm.matches(getInfo("abc"), "åbç")); - assertTrue(mAlgorithm.matches(getInfo("Alpha"), "ål")); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드"), "다", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("드라이브"), "드", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("운로 드라이브"), "ㄷ", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("abc"), "åbç", MATCHER)); + assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("Alpha"), "ål", MATCHER)); - assertFalse(mAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷㄷ")); - assertFalse(mAlgorithm.matches(getInfo("로드라이브"), "ㄷ")); - assertFalse(mAlgorithm.matches(getInfo("abc"), "åç")); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷㄷ", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("로드라이브"), "ㄷ", MATCHER)); + assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("abc"), "åç", MATCHER)); } private AppInfo getInfo(String title) { diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java index c24cc3fb77..7048c2868c 100644 --- a/tests/src/com/android/launcher3/logging/FileLogTest.java +++ b/tests/src/com/android/launcher3/logging/FileLogTest.java @@ -37,6 +37,9 @@ public class FileLogTest extends AndroidTestCase { } public void testPrintLog() throws Exception { + if (!FileLog.ENABLED) { + return; + } FileLog.print("Testing", "hoolalala"); StringWriter writer = new StringWriter(); FileLog.flushAll(new PrintWriter(writer)); @@ -54,6 +57,9 @@ public class FileLogTest extends AndroidTestCase { } public void testOldFileTruncated() throws Exception { + if (!FileLog.ENABLED) { + return; + } FileLog.print("Testing", "hoolalala"); StringWriter writer = new StringWriter(); FileLog.flushAll(new PrintWriter(writer)); diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java index 4c80902f00..82f34e43de 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java @@ -20,9 +20,9 @@ import com.android.launcher3.util.Provider; import org.mockito.ArgumentCaptor; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -131,7 +131,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { // only info2 should be added because info was already added to the workspace // in setupWorkspaceWithHoles() verify(callbacks).bindAppsAdded(any(ArrayList.class), notAnimated.capture(), - animated.capture(), any(ArrayList.class)); + animated.capture()); assertTrue(notAnimated.getValue().isEmpty()); assertEquals(1, animated.getValue().size()); diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java similarity index 80% rename from tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java rename to tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 47b43f530e..1be33d2f86 100644 --- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -15,29 +15,26 @@ */ package com.android.launcher3.ui; +import android.app.Instrumentation; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; +import android.content.pm.LauncherActivityInfo; import android.graphics.Point; -import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; import android.support.test.uiautomator.Direction; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.test.InstrumentationTestCase; import android.view.MotionEvent; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherSettings; @@ -45,25 +42,26 @@ import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.util.ManagedProfileHeuristic; -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; +import org.junit.Before; + import java.util.Locale; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + /** * Base class for all instrumentation tests providing various utility methods. */ -public class LauncherInstrumentationTestCase extends InstrumentationTestCase { +public abstract class AbstractLauncherUiTest { public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; @@ -71,16 +69,15 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { public static final long DEFAULT_UI_TIMEOUT = 3000; public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5; + protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); protected UiDevice mDevice; protected Context mTargetContext; protected String mTargetPackage; - @Override - protected void setUp() throws Exception { - super.setUp(); - + @Before + public void setUp() throws Exception { mDevice = UiDevice.getInstance(getInstrumentation()); - mTargetContext = getInstrumentation().getTargetContext(); + mTargetContext = InstrumentationRegistry.getTargetContext(); mTargetPackage = mTargetContext.getPackageName(); } @@ -97,57 +94,16 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { } } - /** - * Starts the launcher activity in the target package and returns the Launcher instance. - */ - protected Launcher startLauncher() { - return (Launcher) getInstrumentation().startActivitySync(getHomeIntent()); - } - - protected Intent getHomeIntent() { - return new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_HOME) - .setPackage(mTargetPackage) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - - /** - * Grants the launcher permission to bind widgets. - */ - protected void grantWidgetPermission() throws IOException { - // Check bind widget permission - if (mTargetContext.getPackageManager().checkPermission( - mTargetPackage, android.Manifest.permission.BIND_APPWIDGET) - != PackageManager.PERMISSION_GRANTED) { - runShellCommand("appwidget grantbind --package " + mTargetPackage); - } - } - - /** - * Sets the target launcher as default launcher. - */ - protected void setDefaultLauncher() throws IOException { - ActivityInfo launcher = mTargetContext.getPackageManager() - .queryIntentActivities(getHomeIntent(), 0).get(0).activityInfo; - runShellCommand("cmd package set-home-activity " + - new ComponentName(launcher.packageName, launcher.name).flattenToString()); - } - - protected void runShellCommand(String command) throws IOException { - ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() - .executeShellCommand(command); - - // Read the input stream fully. - FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); - while (fis.read() != -1); - fis.close(); + protected Instrumentation getInstrumentation() { + return InstrumentationRegistry.getInstrumentation(); } /** * Opens all apps and returns the recycler view */ protected UiObject2 openAllApps() { - if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + mDevice.waitForIdle(); + if (FeatureFlags.NO_ALL_APPS_ICON) { // clicking on the page indicator brings up all apps tray on non tablets. findViewById(R.id.page_indicator).click(); } else { @@ -262,7 +218,7 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { protected void resetLoaderState() { try { - runTestOnUiThread(new Runnable() { + mMainThreadExecutor.execute(new Runnable() { @Override public void run() { ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext); @@ -279,7 +235,7 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { */ protected T getOnUiThread(final Callable callback) { try { - return new MainThreadExecutor().submit(callback).get(); + return mMainThreadExecutor.submit(callback).get(); } catch (Exception e) { throw new RuntimeException(e); } @@ -315,6 +271,10 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { return By.res(mTargetPackage, name); } + protected LauncherActivityInfo getSettingsApp() { + return LauncherAppsCompat.getInstance(mTargetContext) + .getActivityList("com.android.settings", Process.myUserHandle()).get(0); + } /** * Broadcast receiver which blocks until the result is received. diff --git a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java index 0ced7cf33a..46343a3894 100644 --- a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java +++ b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java @@ -1,52 +1,55 @@ package com.android.launcher3.ui; import android.content.pm.LauncherActivityInfo; -import android.os.Process; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.test.suitebuilder.annotation.LargeTest; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertTrue; /** * Test for verifying apps is launched from all-apps */ @LargeTest -public class AllAppsAppLaunchTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class AllAppsAppLaunchTest extends AbstractLauncherUiTest { - private LauncherActivityInfo mSettingsApp; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) - .getActivityList("com.android.settings", Process.myUserHandle()).get(0); - } + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Test public void testAppLauncher_portrait() throws Exception { lockRotation(true); performTest(); } + @Test public void testAppLauncher_landscape() throws Exception { lockRotation(false); performTest(); } private void performTest() throws Exception { - startLauncher(); + mActivityMonitor.startLauncher(); + + LauncherActivityInfo settingsApp = getSettingsApp(); // Open all apps and wait for load complete final UiObject2 appsContainer = openAllApps(); assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT)); // Open settings app and verify app launched - scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())).click(); + scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString())).click(); assertTrue(mDevice.wait(Until.hasObject(By.pkg( - mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT)); + settingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT)); } } diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java index 9361750873..00f30ada9d 100644 --- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java +++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java @@ -1,58 +1,62 @@ package com.android.launcher3.ui; import android.content.pm.LauncherActivityInfo; -import android.os.Process; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.test.suitebuilder.annotation.LargeTest; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertTrue; /** * Test for dragging an icon from all-apps to homescreen. */ @LargeTest -public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class AllAppsIconToHomeTest extends AbstractLauncherUiTest { - private LauncherActivityInfo mSettingsApp; - - @Override - protected void setUp() throws Exception { - super.setUp(); - setDefaultLauncher(); - - mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) - .getActivityList("com.android.settings", Process.myUserHandle()).get(0); - } + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher(); + @Test public void testDragIcon_portrait() throws Throwable { lockRotation(true); performTest(); } + @Test public void testDragIcon_landscape() throws Throwable { lockRotation(false); performTest(); } private void performTest() throws Throwable { + LauncherActivityInfo settingsApp = getSettingsApp(); + clearHomescreen(); - startLauncher(); + mActivityMonitor.startLauncher(); // Open all apps and wait for load complete. final UiObject2 appsContainer = openAllApps(); assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT)); // Drag icon to homescreen. - UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())); + UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString())); dragToWorkspace(icon, true); // Verify that the icon works on homescreen. - mDevice.findObject(By.text(mSettingsApp.getLabel().toString())).click(); + mDevice.findObject(By.text(settingsApp.getLabel().toString())).click(); assertTrue(mDevice.wait(Until.hasObject(By.pkg( - mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT)); + settingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT)); } } diff --git a/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java b/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java deleted file mode 100644 index e84ad047f1..0000000000 --- a/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.android.launcher3.ui; - -import android.content.SharedPreferences; -import android.graphics.Rect; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject; -import android.support.test.uiautomator.UiSelector; -import android.test.suitebuilder.annotation.MediumTest; - -import com.android.launcher3.R; -import com.android.launcher3.Utilities; - -/** - * Test for auto rotate preference. - */ -@MediumTest -public class RotationPreferenceTest extends LauncherInstrumentationTestCase { - - private SharedPreferences mPrefs; - private boolean mOriginalRotationValue; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - mDevice = UiDevice.getInstance(getInstrumentation()); - mTargetContext = getInstrumentation().getTargetContext(); - mTargetPackage = mTargetContext.getPackageName(); - mPrefs = Utilities.getPrefs(mTargetContext); - mOriginalRotationValue = mPrefs.getBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false); - } - - @Override - protected void tearDown() throws Exception { - setRotationEnabled(mOriginalRotationValue); - super.tearDown(); - } - - public void testRotation_disabled() throws Exception { - if (mTargetContext.getResources().getBoolean(R.bool.allow_rotation)) { - // This is a tablet. The test is only valid to mobile devices. - return; - } - - setRotationEnabled(false); - mDevice.setOrientationRight(); - startLauncher(); - - Rect hotseat = getHotseatBounds(); - assertTrue(hotseat.width() > hotseat.height()); - } - - public void testRotation_enabled() throws Exception { - if (mTargetContext.getResources().getBoolean(R.bool.allow_rotation)) { - // This is a tablet. The test is only valid to mobile devices. - return; - } - - setRotationEnabled(true); - mDevice.setOrientationRight(); - startLauncher(); - - Rect hotseat = getHotseatBounds(); - assertTrue(hotseat.width() < hotseat.height()); - } - - private void setRotationEnabled(boolean enabled) { - mPrefs.edit().putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, enabled).commit(); - } - - private Rect getHotseatBounds() throws Exception { - UiObject hotseat = mDevice.findObject( - new UiSelector().resourceId(mTargetPackage + ":id/hotseat")); - hotseat.waitForExists(6000); - return hotseat.getVisibleBounds(); - } -} diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java index 3a0b6132c6..a40ad7fadc 100644 --- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java +++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java @@ -2,54 +2,58 @@ package com.android.launcher3.ui; import android.content.pm.LauncherActivityInfo; import android.graphics.Point; -import android.os.Process; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.test.suitebuilder.annotation.LargeTest; import android.view.MotionEvent; import com.android.launcher3.R; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * Test for verifying that shortcuts are shown and can be launched after long pressing an app */ @LargeTest -public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class ShortcutsLaunchTest extends AbstractLauncherUiTest { - private LauncherActivityInfo mSettingsApp; - - @Override - protected void setUp() throws Exception { - super.setUp(); - setDefaultLauncher(); - - mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) - .getActivityList("com.android.settings", Process.myUserHandle()).get(0); - } + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher(); + @Test public void testAppLauncher_portrait() throws Exception { lockRotation(true); performTest(); } + @Test public void testAppLauncher_landscape() throws Exception { lockRotation(false); performTest(); } private void performTest() throws Exception { - startLauncher(); + mActivityMonitor.startLauncher(); + LauncherActivityInfo settingsApp = getSettingsApp(); // Open all apps and wait for load complete final UiObject2 appsContainer = openAllApps(); assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT)); // Find settings app and verify shortcuts appear when long pressed - UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())); + UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString())); // Press icon center until shortcuts appear Point iconCenter = icon.getVisibleCenter(); sendPointer(MotionEvent.ACTION_DOWN, iconCenter); @@ -63,7 +67,7 @@ public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase { .findObject(getSelectorForId(R.id.bubble_text)); shortcut.click(); assertTrue(mDevice.wait(Until.hasObject(By.pkg( - mSettingsApp.getComponentName().getPackageName()) + settingsApp.getComponentName().getPackageName()) .text(shortcut.getText())), DEFAULT_UI_TIMEOUT)); } } diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java index 5d86d1ec68..434311dd6f 100644 --- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java +++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java @@ -2,40 +2,43 @@ package com.android.launcher3.ui; import android.content.pm.LauncherActivityInfo; import android.graphics.Point; -import android.os.Process; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.test.suitebuilder.annotation.LargeTest; import android.view.MotionEvent; import com.android.launcher3.R; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * Test for dragging a deep shortcut to the home screen. */ @LargeTest -public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class ShortcutsToHomeTest extends AbstractLauncherUiTest { - private LauncherActivityInfo mSettingsApp; - - @Override - protected void setUp() throws Exception { - super.setUp(); - setDefaultLauncher(); - - mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) - .getActivityList("com.android.settings", Process.myUserHandle()).get(0); - } + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher(); + @Test public void testDragIcon_portrait() throws Throwable { lockRotation(true); performTest(); } + @Test public void testDragIcon_landscape() throws Throwable { lockRotation(false); performTest(); @@ -43,14 +46,16 @@ public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase { private void performTest() throws Throwable { clearHomescreen(); - startLauncher(); + mActivityMonitor.startLauncher(); + + LauncherActivityInfo settingsApp = getSettingsApp(); // Open all apps and wait for load complete. final UiObject2 appsContainer = openAllApps(); assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT)); // Find the app and long press it to show shortcuts. - UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())); + UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString())); // Press icon center until shortcuts appear Point iconCenter = icon.getVisibleCenter(); sendPointer(MotionEvent.ACTION_DOWN, iconCenter); @@ -69,7 +74,7 @@ public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase { // (the app opens and has the same text as the shortcut). mDevice.findObject(By.text(shortcutName)).click(); assertTrue(mDevice.wait(Until.hasObject(By.pkg( - mSettingsApp.getComponentName().getPackageName()) + settingsApp.getComponentName().getPackageName()) .text(shortcutName)), DEFAULT_UI_TIMEOUT)); } } diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 0b4e34f94c..a5c2e69af7 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -15,75 +15,75 @@ */ package com.android.launcher3.ui.widget; -import android.app.Activity; -import android.app.Application; import android.appwidget.AppWidgetManager; import android.content.Intent; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; -import android.test.suitebuilder.annotation.LargeTest; import android.view.View; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.Workspace; import com.android.launcher3.testcomponent.WidgetConfigActivity; -import com.android.launcher3.ui.LauncherInstrumentationTestCase; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.Condition; -import com.android.launcher3.util.SimpleActivityMonitor; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.WidgetCell; -import java.util.concurrent.Callable; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; /** * Test to verify widget configuration is properly shown. */ @LargeTest -public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class AddConfigWidgetTest extends AbstractLauncherUiTest { + + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind(); private LauncherAppWidgetProviderInfo mWidgetInfo; - private SimpleActivityMonitor mActivityMonitor; - private MainThreadExecutor mMainThreadExecutor; private AppWidgetManager mAppWidgetManager; private int mWidgetId; @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { super.setUp(); mWidgetInfo = findWidgetProvider(true /* hasConfigureScreen */); - mActivityMonitor = new SimpleActivityMonitor(); - ((Application) getInstrumentation().getTargetContext().getApplicationContext()) - .registerActivityLifecycleCallbacks(mActivityMonitor); - mMainThreadExecutor = new MainThreadExecutor(); mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext); - - grantWidgetPermission(); - } - - @Override - protected void tearDown() throws Exception { - ((Application) getInstrumentation().getTargetContext().getApplicationContext()) - .unregisterActivityLifecycleCallbacks(mActivityMonitor); - super.tearDown(); } + @Test public void testWidgetConfig() throws Throwable { runTest(false, true); } + @Test public void testWidgetConfig_rotate() throws Throwable { runTest(true, true); } + @Test public void testConfigCancelled() throws Throwable { runTest(false, false); } + @Test public void testConfigCancelled_rotate() throws Throwable { runTest(true, false); } @@ -96,7 +96,7 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { lockRotation(true); clearHomescreen(); - startLauncher(); + mActivityMonitor.startLauncher(); // Open widget tray and wait for load complete. final UiObject2 widgetContainer = openWidgetsTray(); @@ -146,11 +146,11 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { * Condition for searching widget id */ private class WidgetSearchCondition extends Condition - implements Callable, Workspace.ItemOperator { + implements Workspace.ItemOperator { @Override public boolean isTrue() throws Throwable { - return mMainThreadExecutor.submit(this).get(); + return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get(); } @Override @@ -159,21 +159,6 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { ((LauncherAppWidgetInfo) info).providerName.equals(mWidgetInfo.provider) && ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId; } - - @Override - public Boolean call() throws Exception { - // Find the resumed launcher - Launcher launcher = null; - for (Activity a : mActivityMonitor.resumed) { - if (a instanceof Launcher) { - launcher = (Launcher) a; - } - } - if (launcher == null) { - return false; - } - return launcher.getWorkspace().getFirstMatch(this) != null; - } } /** diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 3c92c578d5..19f7db72bf 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -15,42 +15,46 @@ */ package com.android.launcher3.ui.widget; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; -import android.test.suitebuilder.annotation.LargeTest; import android.view.View; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Workspace.ItemOperator; -import com.android.launcher3.ui.LauncherInstrumentationTestCase; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.WidgetCell; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertTrue; + /** * Test to add widget from widget tray */ @LargeTest -public class AddWidgetTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class AddWidgetTest extends AbstractLauncherUiTest { - private LauncherAppWidgetProviderInfo widgetInfo; - - @Override - protected void setUp() throws Exception { - super.setUp(); - grantWidgetPermission(); - - widgetInfo = findWidgetProvider(false /* hasConfigureScreen */); - } + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind(); + @Test public void testDragIcon_portrait() throws Throwable { lockRotation(true); performTest(); } + @Test public void testDragIcon_landscape() throws Throwable { lockRotation(false); performTest(); @@ -58,7 +62,10 @@ public class AddWidgetTest extends LauncherInstrumentationTestCase { private void performTest() throws Throwable { clearHomescreen(); - Launcher launcher = startLauncher(); + mActivityMonitor.startLauncher(); + + final LauncherAppWidgetProviderInfo widgetInfo = + findWidgetProvider(false /* hasConfigureScreen */); // Open widget tray and wait for load complete. final UiObject2 widgetContainer = openWidgetsTray(); @@ -69,12 +76,12 @@ public class AddWidgetTest extends LauncherInstrumentationTestCase { .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager())))); dragToWorkspace(widget, false); - assertNotNull(launcher.getWorkspace().getFirstMatch(new ItemOperator() { + assertTrue(mActivityMonitor.itemExists(new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View view) { return info instanceof LauncherAppWidgetInfo && ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider); } - })); + }).call()); } } diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 221fed1fc5..d4d517a22c 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -24,10 +24,10 @@ import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiSelector; -import android.test.suitebuilder.annotation.LargeTest; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetHostView; import com.android.launcher3.LauncherAppWidgetInfo; @@ -38,23 +38,40 @@ import com.android.launcher3.PendingAppWidgetHostView; import com.android.launcher3.Workspace; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.PackageInstallerCompat; -import com.android.launcher3.ui.LauncherInstrumentationTestCase; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.LooperExecutor; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetHostViewLoader; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + /** * Tests for bind widget flow. * * Note running these tests will clear the workspace on the device. */ @LargeTest -public class BindWidgetTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class BindWidgetTest extends AbstractLauncherUiTest { + + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind(); private ContentResolver mResolver; private AppWidgetManagerCompat mWidgetManager; @@ -65,21 +82,20 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { private int mSessionId = -1; @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { super.setUp(); mResolver = mTargetContext.getContentResolver(); mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext); - grantWidgetPermission(); // Clear all existing data LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { if (mCursor != null) { mCursor.close(); } @@ -89,6 +105,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { } } + @Test public void testBindNormalWidget_withConfig() { LauncherAppWidgetProviderInfo info = findWidgetProvider(true); LauncherAppWidgetInfo item = createWidgetInfo(info, true); @@ -96,6 +113,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label); } + @Test public void testBindNormalWidget_withoutConfig() { LauncherAppWidgetProviderInfo info = findWidgetProvider(false); LauncherAppWidgetInfo item = createWidgetInfo(info, true); @@ -103,6 +121,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label); } + @Test public void testUnboundWidget_removed() throws Exception { LauncherAppWidgetProviderInfo info = findWidgetProvider(false); LauncherAppWidgetInfo item = createWidgetInfo(info, false); @@ -121,6 +140,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists()); } + @Test public void testPendingWidget_autoRestored() { // A non-restored widget with no config screen gets restored automatically. LauncherAppWidgetProviderInfo info = findWidgetProvider(false); @@ -132,6 +152,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label); } + @Test public void testPendingWidget_withConfigScreen() throws Exception { // A non-restored widget with config screen get bound and shows a 'Click to setup' UI. LauncherAppWidgetProviderInfo info = findWidgetProvider(true); @@ -154,6 +175,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { LauncherSettings.Favorites.APPWIDGET_ID)))); } + @Test public void testPendingWidget_notRestored_removed() throws Exception { LauncherAppWidgetInfo item = getInvalidWidgetInfo(); item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID @@ -170,6 +192,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { assertEquals(0, mCursor.getCount()); } + @Test public void testPendingWidget_notRestored_brokenInstall() throws Exception { // A widget which is was being installed once, even if its not being // installed at the moment is not removed. @@ -192,6 +215,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); } + @Test public void testPendingWidget_notRestored_activeInstall() throws Exception { // A widget which is being installed is not removed LauncherAppWidgetInfo item = getInvalidWidgetInfo(); @@ -250,7 +274,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase { resetLoaderState(); // Launch the home activity - startLauncher(); + mActivityMonitor.startLauncher(); // Verify UI UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) .className(widgetClass); diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index b798dfa88d..4b9d83f8ae 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -15,23 +15,20 @@ */ package com.android.launcher3.ui.widget; -import android.app.Activity; -import android.app.Application; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.content.Intent; import android.graphics.Color; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.test.suitebuilder.annotation.LargeTest; import android.view.View; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; @@ -40,50 +37,48 @@ import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.testcomponent.RequestPinItemActivity; -import com.android.launcher3.ui.LauncherInstrumentationTestCase; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.Condition; -import com.android.launcher3.util.SimpleActivityMonitor; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.LauncherActivityRule; +import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.WidgetCell; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.UUID; -import java.util.concurrent.Callable; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; /** * Test to verify pin item request flow. */ @LargeTest -public class RequestPinItemTest extends LauncherInstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class RequestPinItemTest extends AbstractLauncherUiTest { - private SimpleActivityMonitor mActivityMonitor; - private MainThreadExecutor mMainThreadExecutor; + @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); + @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind(); + @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher(); private String mCallbackAction; private String mShortcutId; private int mAppWidgetId; @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { super.setUp(); - grantWidgetPermission(); - setDefaultLauncher(); - - mActivityMonitor = new SimpleActivityMonitor(); - ((Application) getInstrumentation().getTargetContext().getApplicationContext()) - .registerActivityLifecycleCallbacks(mActivityMonitor); - mMainThreadExecutor = new MainThreadExecutor(); - mCallbackAction = UUID.randomUUID().toString(); mShortcutId = UUID.randomUUID().toString(); } - @Override - protected void tearDown() throws Exception { - ((Application) getInstrumentation().getTargetContext().getApplicationContext()) - .unregisterActivityLifecycleCallbacks(mActivityMonitor); - super.tearDown(); - } - + @Test public void testPinWidgetNoConfig() throws Throwable { runTest("pinWidgetNoConfig", true, new ItemOperator() { @Override @@ -96,6 +91,7 @@ public class RequestPinItemTest extends LauncherInstrumentationTestCase { }); } + @Test public void testPinWidgetNoConfig_customPreview() throws Throwable { // Command to set custom preview Intent command = RequestPinItemActivity.getCommandIntent( @@ -113,6 +109,7 @@ public class RequestPinItemTest extends LauncherInstrumentationTestCase { }, command); } + @Test public void testPinWidgetWithConfig() throws Throwable { runTest("pinWidgetWithConfig", true, new ItemOperator() { @Override @@ -125,6 +122,7 @@ public class RequestPinItemTest extends LauncherInstrumentationTestCase { }); } + @Test public void testPinShortcut() throws Throwable { // Command to set the shortcut id Intent command = RequestPinItemActivity.getCommandIntent( @@ -149,7 +147,7 @@ public class RequestPinItemTest extends LauncherInstrumentationTestCase { lockRotation(true); clearHomescreen(); - startLauncher(); + mActivityMonitor.startLauncher(); // Open all apps and wait for load complete final UiObject2 appsContainer = openAllApps(); @@ -191,14 +189,14 @@ public class RequestPinItemTest extends LauncherInstrumentationTestCase { } // Go back to home - mTargetContext.startActivity(getHomeIntent()); + mActivityMonitor.returnToHome(); assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT)); } /** * Condition for for an item */ - private class ItemSearchCondition extends Condition implements Callable { + private class ItemSearchCondition extends Condition { private final ItemOperator mOp; @@ -208,22 +206,7 @@ public class RequestPinItemTest extends LauncherInstrumentationTestCase { @Override public boolean isTrue() throws Throwable { - return mMainThreadExecutor.submit(this).get(); - } - - @Override - public Boolean call() throws Exception { - // Find the resumed launcher - Launcher launcher = null; - for (Activity a : mActivityMonitor.resumed) { - if (a instanceof Launcher) { - launcher = (Launcher) a; - } - } - if (launcher == null) { - return false; - } - return launcher.getWorkspace().getFirstMatch(mOp) != null; + return mMainThreadExecutor.submit(mActivityMonitor.itemExists(mOp)).get(); } } } diff --git a/tests/src/com/android/launcher3/util/SimpleActivityMonitor.java b/tests/src/com/android/launcher3/util/SimpleActivityMonitor.java deleted file mode 100644 index 6154ab6c58..0000000000 --- a/tests/src/com/android/launcher3/util/SimpleActivityMonitor.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.android.launcher3.util; - -import android.app.Activity; -import android.app.Application.*; -import android.os.Bundle; - -import java.util.ArrayList; - -/** - * Simple monitor to keep a list of active activities. - */ -public class SimpleActivityMonitor implements ActivityLifecycleCallbacks { - - public final ArrayList created = new ArrayList<>(); - public final ArrayList started = new ArrayList<>(); - public final ArrayList resumed = new ArrayList<>(); - - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - created.add(activity); - } - - @Override - public void onActivityStarted(Activity activity) { - started.add(activity); - } - - @Override - public void onActivityResumed(Activity activity) { - resumed.add(activity); - } - - @Override - public void onActivityPaused(Activity activity) { - resumed.remove(activity); - } - - @Override - public void onActivityStopped(Activity activity) { - started.remove(activity); - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { } - - @Override - public void onActivityDestroyed(Activity activity) { - created.remove(activity); - } -} diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java new file mode 100644 index 0000000000..edd152a2c7 --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.launcher3.util.rule; + +import android.app.Activity; +import android.app.Application; +import android.app.Application.ActivityLifecycleCallbacks; +import android.content.Intent; +import android.os.Bundle; +import android.support.test.InstrumentationRegistry; + +import com.android.launcher3.Launcher; +import com.android.launcher3.Workspace.ItemOperator; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.util.concurrent.Callable; + +/** + * Test rule to get the current Launcher activity. + */ +public class LauncherActivityRule implements TestRule { + + private Launcher mActivity; + + @Override + public Statement apply(Statement base, Description description) { + return new MyStatement(base); + } + + public Launcher getActivity() { + return mActivity; + } + + public Callable itemExists(final ItemOperator op) { + return new Callable() { + + @Override + public Boolean call() throws Exception { + Launcher launcher = getActivity(); + if (launcher == null) { + return false; + } + return launcher.getWorkspace().getFirstMatch(op) != null; + } + }; + } + + /** + * Starts the launcher activity in the target package. + */ + public void startLauncher() { + InstrumentationRegistry.getInstrumentation().startActivitySync(getHomeIntent()); + } + + public void returnToHome() { + InstrumentationRegistry.getTargetContext().startActivity(getHomeIntent()); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + public static Intent getHomeIntent() { + return new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setPackage(InstrumentationRegistry.getTargetContext().getPackageName()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + private class MyStatement extends Statement implements ActivityLifecycleCallbacks { + + private final Statement mBase; + + public MyStatement(Statement base) { + mBase = base; + } + + @Override + public void evaluate() throws Throwable { + Application app = (Application) + InstrumentationRegistry.getTargetContext().getApplicationContext(); + app.registerActivityLifecycleCallbacks(this); + try { + mBase.evaluate(); + } finally { + app.unregisterActivityLifecycleCallbacks(this); + } + } + + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + if (activity instanceof Launcher) { + mActivity = (Launcher) activity; + } + } + + @Override + public void onActivityStarted(Activity activity) { } + + @Override + public void onActivityResumed(Activity activity) { } + + @Override + public void onActivityPaused(Activity activity) { } + + @Override + public void onActivityStopped(Activity activity) { } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { } + + @Override + public void onActivityDestroyed(Activity activity) { + if (activity == mActivity) { + mActivity = null; + } + } + } +} diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java new file mode 100644 index 0000000000..dba2d71003 --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.launcher3.util.rule; + +import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.os.ParcelFileDescriptor; +import android.support.test.InstrumentationRegistry; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.io.FileInputStream; +import java.io.IOException; + +/** + * Test rule which executes a shell command at the start of the test. + */ +public class ShellCommandRule implements TestRule { + + private final String mCmd; + + public ShellCommandRule(String cmd) { + mCmd = cmd; + } + + @Override + public Statement apply(Statement base, Description description) { + return new MyStatement(base, mCmd); + } + + public static void runShellCommand(String command) throws IOException { + ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation() + .executeShellCommand(command); + + // Read the input stream fully. + FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + while (fis.read() != -1); + fis.close(); + } + + private static class MyStatement extends Statement { + private final Statement mBase; + private final String mCmd; + + public MyStatement(Statement base, String cmd) { + mBase = base; + mCmd = cmd; + } + + @Override + public void evaluate() throws Throwable { + runShellCommand(mCmd); + mBase.evaluate(); + } + } + + /** + * Grants the launcher permission to bind widgets. + */ + public static ShellCommandRule grandWidgetBind() { + return new ShellCommandRule("appwidget grantbind --package " + + InstrumentationRegistry.getTargetContext().getPackageName()); + } + + /** + * Sets the target launcher as default launcher. + */ + public static ShellCommandRule setDefaultLauncher() { + ActivityInfo launcher = InstrumentationRegistry.getTargetContext().getPackageManager() + .queryIntentActivities(LauncherActivityRule.getHomeIntent(), 0).get(0) + .activityInfo; + return new ShellCommandRule("cmd package set-home-activity " + + new ComponentName(launcher.packageName, launcher.name).flattenToString()); + } +}