From 7765ff637e3ef2085368756313eecc136bea5f49 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Mon, 18 Aug 2014 20:00:05 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I1655a89a033b0d5412f87b0c50515f5dd84f9987 Auto-generated-cl: translation import --- res/values-af/strings.xml | 2 ++ res/values-am/strings.xml | 2 ++ res/values-ar/strings.xml | 2 ++ res/values-bg/strings.xml | 2 ++ res/values-ca/strings.xml | 2 ++ res/values-cs/strings.xml | 2 ++ res/values-da/strings.xml | 2 ++ res/values-de/strings.xml | 2 ++ res/values-el/strings.xml | 1 + res/values-en-rGB/strings.xml | 2 ++ res/values-en-rIN/strings.xml | 2 ++ res/values-es-rUS/strings.xml | 2 ++ res/values-es/strings.xml | 2 ++ res/values-et-rEE/strings.xml | 2 ++ res/values-fa/strings.xml | 2 ++ res/values-fi/strings.xml | 2 ++ res/values-fr-rCA/strings.xml | 2 ++ res/values-fr/strings.xml | 2 ++ res/values-hi/strings.xml | 2 ++ res/values-hr/strings.xml | 2 ++ res/values-hu/strings.xml | 2 ++ res/values-hy-rAM/strings.xml | 2 ++ res/values-in/strings.xml | 2 ++ res/values-it/strings.xml | 2 ++ res/values-iw/strings.xml | 2 ++ res/values-ja/strings.xml | 2 ++ res/values-ka-rGE/strings.xml | 2 ++ res/values-km-rKH/strings.xml | 4 +++- res/values-ko/strings.xml | 2 ++ res/values-lo-rLA/strings.xml | 5 +++-- res/values-lt/strings.xml | 2 ++ res/values-lv/strings.xml | 2 ++ res/values-mn-rMN/strings.xml | 1 + res/values-ms-rMY/strings.xml | 2 ++ res/values-nb/strings.xml | 2 ++ res/values-nl/strings.xml | 2 ++ res/values-pl/strings.xml | 2 ++ res/values-pt-rPT/strings.xml | 2 ++ res/values-pt/strings.xml | 2 ++ res/values-ro/strings.xml | 2 ++ res/values-ru/strings.xml | 2 ++ res/values-sk/strings.xml | 2 ++ res/values-sl/strings.xml | 2 ++ res/values-sr/strings.xml | 2 ++ res/values-sv/strings.xml | 2 ++ res/values-sw/strings.xml | 2 ++ res/values-th/strings.xml | 2 ++ res/values-tl/strings.xml | 2 ++ res/values-tr/strings.xml | 2 ++ res/values-uk/strings.xml | 2 ++ res/values-vi/strings.xml | 2 ++ res/values-zh-rCN/strings.xml | 2 ++ res/values-zh-rHK/strings.xml | 2 ++ res/values-zh-rTW/strings.xml | 2 ++ res/values-zu/strings.xml | 2 ++ 55 files changed, 110 insertions(+), 3 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index bb933f702..5f10b21a1 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -77,6 +77,8 @@ "skryf Tuis-instellings en -kortpaaie" "Laat die program toe om die instellings en kortpaaie in Tuis te verander." "Kon nie legstuk laai nie" + + "Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie." "Vuurpyllanseerder" "Naamlose vouer" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 51d5c706f..9d0b18503 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -77,6 +77,8 @@ "የመነሻ ቅንብሮችን እና አቋራጮችን ይጽፋል" "መተግብሪያው ቅንብሮችን እና አቋራጮችን በመነሻ ውስጥ እንዲቀይራቸው ያስችለዋል።" "ፍርግም የመጫን ችግር" + + "ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።" "የሮኬት ማስጀመሪያ" "ስም-አልባ አቃፊ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index cb6218439..b0d6589a3 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -77,6 +77,8 @@ "كتابة إعدادات واختصارات الشاشة الرئيسية" "للسماح للتطبيق بتغيير الإعدادات والاختصارات في الشاشة الرئيسية." "حدثت مشكلة أثناء تحميل الأداة" + + "هذا تطبيق نظام وتتعذر إزالته." "قاذفة صواريخ" "مجلد بدون اسم" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 5f93a9a3e..0b59e1ae2 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -77,6 +77,8 @@ "запис на настройките и преките пътища в Начало" "Разрешава на приложението да променя настройките и преките пътища в Начало." "Проблем при зареждане на приспособлението" + + "Това е системно приложение и не може да се деинсталира." "Ракетна площадка" "Папка без име" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 7fc9051ca..82db60284 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -77,6 +77,8 @@ "escriu la configuració i les dreceres de la pantalla d\'inici" "Permet que l\'aplicació canviï la configuració i les dreceres de la pantalla d\'inici." "S\'ha produït un problema en carregar el widget" + + "Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar." "Rocket Launcher" "Carpeta sense nom" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index e90afb001..0d2a60768 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -77,6 +77,8 @@ "zápis nastavení a odkazů plochy" "Umožňuje aplikaci změnit nastavení a odkazy na ploše." "Problém s načtením widgetu" + + "Toto je systémová aplikace a nelze ji odinstalovat." "Rocket Launcher" "Složka bez názvu" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 60ea2c6bd..1a71c17b6 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -77,6 +77,8 @@ "skrive indstillinger og genveje for startskærmen" "Tillader, at appen ændrer indstillingerne og genvejene på startskærmen." "Der er problemer med indlæsning af widgetten" + + "Dette er en systemapp, som ikke kan afinstalleres." "Rocket Launcher" "Unavngiven mappe" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 1e225eb75..f04fb1602 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -77,6 +77,8 @@ "Einstellungen und Verknüpfungen für den Startbildschirm schreiben" "Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu ändern" "Problem beim Laden des Widgets" + + "Dies ist eine Systemanwendung, die nicht deinstalliert werden kann." "Rocket Launcher" "Unbenannter Ordner" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index ccc5a29a7..2ee3c2166 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -77,6 +77,7 @@ "εγγραφή ρυθμίσεων και συντομεύσεων αρχικής οθόνης" "Επιτρέπει στην εφαρμογή την αλλαγή των ρυθμίσεων και των συντομεύσεων στην Αρχική οθόνη." "Παρουσιάστηκε πρόβλημα στη φόρτωση του γραφικού στοιχείου" + "Ρύθμιση" "Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της." "Rocket Launcher" "Φάκελος χωρίς όνομα" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 7be42dbba..d127c27fc 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -77,6 +77,8 @@ "write Home settings and shortcuts" "Allows the app to change the settings and shortcuts in Home." "Problem loading widget" + + "This is a system app and can\'t be uninstalled." "Rocket Launcher" "Unnamed Folder" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 7be42dbba..d127c27fc 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -77,6 +77,8 @@ "write Home settings and shortcuts" "Allows the app to change the settings and shortcuts in Home." "Problem loading widget" + + "This is a system app and can\'t be uninstalled." "Rocket Launcher" "Unnamed Folder" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 304f027f6..1d38832de 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -77,6 +77,8 @@ "escribir configuración y accesos directos de la pantalla principal" "Permite que la aplicación cambie la configuración y los accesos directos de la pantalla principal." "Problema al cargar el widget" + + "Esta es una aplicación del sistema y no se puede desinstalar." "Lanzacohetes" "Carpeta sin nombre" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index dbb3c436f..87982de52 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -77,6 +77,8 @@ "escribir información de accesos directos y de ajustes de la pantalla de inicio" "Permite que las aplicaciones cambien los ajustes y los accesos directos de la pantalla de inicio." "Problema al cargar el widget" + + "Esta aplicación es del sistema y no se puede desinstalar." "Rocket Launcher" "Carpeta sin nombre" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 05f38b020..b33987ef9 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -77,6 +77,8 @@ "kirjuta avaekraani seaded ja otseteed" "Võimaldab rakendusel muuta avaekraanil seadeid ja otseteid." "Probleem vidina laadimisel" + + "See on süsteemirakendus ja seda ei saa desinstallida." "Rocket Launcher" "Nimetu kaust" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 3a63ad9fe..f67facc55 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -77,6 +77,8 @@ "نوشتن تنظیمات و میان‌برهای صفحه اصلی" "به برنامه اجازه می‌دهد تنظیمات و میان‌برها را در صفحه اصلی تغییر دهد." "مشکل در بارگیری ابزارک" + + "این برنامه سیستمی است و حذف نصب نمی‌شود." "Rocket Launcher" "پوشه بی‌نام" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 4c09e34de..70c517652 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -77,6 +77,8 @@ "kirjoita aloitusruudun asetuksia ja pikakuvakkeita" "Antaa sovelluksen muuttaa aloitusruudun asetuksia ja pikakuvakkeita." "Ongelma ladattaessa widgetiä" + + "Tämä on järjestelmäsovellus, eikä sitä voi poistaa." "Sinko" "Nimetön kansio" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 8674cd3b6..5e2bed95b 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -77,6 +77,8 @@ "enregistrer les paramètres de la page d\'accueil et des raccourcis" "Permet à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil." "Problème lors du chargement du widget" + + "Impossible de désinstaller cette application, car il s\'agit d\'une application système." "Lance-missile" "Dossier sans nom" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 17cdab7a6..0f59ad7c9 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -77,6 +77,8 @@ "modifier les paramètres et les raccourcis de l\'écran d\'accueil" "Permettre à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil" "Problème lors du chargement du widget." + + "Impossible de désinstaller cette application, car il s\'agit d\'une application système." "Rocket Launcher" "Dossier sans nom" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 49943aafd..0e2aa4fb2 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -77,6 +77,8 @@ "होम सेटिंग और शॉर्टकट लिखें" "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट बदलने देती है." "विजेट लोड करने में समस्‍या" + + "यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता." "रॉकेट लॉन्‍चर" "अनामित फ़ोल्डर" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 696d2a73b..b1a3b2685 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -77,6 +77,8 @@ "pisanje postavki početnog zaslona i prečaca" "Aplikaciji omogućuje promjenu postavki i prečaca na početnom zaslonu." "Problem pri učitavanju widgeta" + + "Ovo je aplikacija sustava i ne može se ukloniti." "Lansirna rampa" "Neimenovana mapa" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 33ee61772..148140624 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -77,6 +77,8 @@ "Főoldal beállításainak és parancsikonjainak írása" "Lehetővé teszi az alkalmazás számára, hogy módosítsa a kezdőképernyő beállításait és parancsikonjait." "Probléma történt a modul betöltésekor" + + "Ez egy rendszeralkalmazás, és nem lehet eltávolítani." "Aknavető" "Névtelen mappa" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 2aa5cc00f..0a3ff0ed5 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -77,6 +77,8 @@ "ստեղծել հիմնաէջի կարգավորումներ ու դյուրանցումներ" "Ծրագրին թույլ է տալիս փոփոխել հիմնաէջի կարգավորումներն ու դյուրանցումները:" "Վիջեթի բեռնման խնդիր կա" + + "Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:" "Հրթիռային թողարկիչ" "Անանուն թղթապանակ" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index db6f9a152..e0aa1fb38 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -77,6 +77,8 @@ "menulis setelan dan pintasan layar Utama" "Mengizinkan aplikasi mengubah setelan dan pintasan di layar Utama." "Masalah memuat widget" + + "Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya." "Rocket Launcher" "Folder Tanpa Nama" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 388fcca27..6f712f761 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -77,6 +77,8 @@ "creazione di impostazioni e scorciatoie in Home" "Consente all\'app di modificare le impostazioni e le scorciatoie in Home." "Errore durante il caricamento del widget" + + "Questa è un\'app di sistema e non può essere disinstallata." "Lanciamissili" "Cartella senza nome" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index c87d948e8..cb4bcc5d6 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -77,6 +77,8 @@ "כתוב הגדרות וקיצורי דרך של דף הבית" "מאפשר לאפליקציה לשנות את ההגדרות וקיצורי הדרך בדף הבית." "בעיה בטעינת ווידג\'ט" + + "זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה." "Rocket Launcher" "תיקיה ללא שם" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index aeddd0755..36eaf5ab0 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -77,6 +77,8 @@ "ホームの設定とショートカットの書き込み" "ホームの設定とショートカットの変更をアプリに許可します。" "ウィジェットを表示できません" + + "このシステムアプリはアンインストールできません。" "Rocket Launcher" "名前のないフォルダ" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index a1ab30888..e354ab00b 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -77,6 +77,8 @@ "მთავარი ეკრანის პარამეტრებისა და მალსახმობების ჩაწერა" "აპისთვის მთავარი ეკრანის პარამეტრებისა და მალსახმობების შეცვლის უფლების მიცემა." "პრობლემა ვიჯეტის ჩატვირთვისას" + + "ეს სისტემური აპია და მისი წაშლა შეუძლებელია." "ფეიერვერკი" "უსახელო საქაღალდე" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 2a7a78217..56c8bc716 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -77,6 +77,8 @@ "សរសេរ​ការ​កំណត់ ​និង​ផ្លូវកាត់​​លើ​អេក្រង់​ដើម" "អនុញ្ញាត​ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​កំណត់ និង​ផ្លូវ​កាត់​ក្នុង​អេក្រង់​ដើម។" "បញ្ហា​ក្នុង​ការ​ផ្ទុក​ធាតុ​​ក្រាហ្វិក" + + "នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។" "កម្មវិធី​ចាប់ផ្ដើម​រ៉ូកែត" "ថត​គ្មាន​ឈ្មោះ" @@ -85,7 +87,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍​" + "សូម​ស្វាគមន៍" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 392cfec8f..0746de94c 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -77,6 +77,8 @@ "홈 설정 및 바로가기 쓰기" "앱이 홈에 있는 설정 및 바로가기를 변경할 수 있도록 합니다." "위젯을 로드하는 중 문제가 발생했습니다." + + "시스템 앱은 제거할 수 없습니다." "로켓 실행기" "이름이 없는 폴더" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 185f4f89c..e628100d0 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -36,7 +36,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ​" + "ຍົກ​ເລີກ" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -77,6 +77,7 @@ "ຂຽນການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ" "ອະນຸຍາດໃຫ້ແອັບຯດັ່ງກ່າວ ປ່ຽນການຕັ້ງຄ່າ ແລະທາງລັດໃນໜ້າຫຼັກ." "ມີບັນຫາໃນການໂຫລດວິດເຈັດ" + "ຕິດຕັ້ງ" "ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້." "Rocket Launcher" "ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່" @@ -114,7 +115,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ​" + "ລຶບ" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 9acb910fc..29cd23b6b 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -77,6 +77,8 @@ "rašyti pagrindinio puslapio nustatymus ir sparčiuosius klavišus" "Programai leidžiama keisti pagrindinio puslapio nustatymus ir sparčiuosius klavišus." "Problema įkeliant valdiklį" + + "Tai sistemos programa ir jos negalima pašalinti." "Rocket Launcher" "Aplankas be pavadinimo" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index b71a7dfbe..b694dd093 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -77,6 +77,8 @@ "rakstīt sākuma ekrāna iestatījumus un saīsnes" "Ļauj lietotnei mainīt iestatījumus un saīsnes sākuma ekrānā." "Ielādējot logrīku, radās problēma." + + "Šī ir sistēmas lietotne, un to nevar atinstalēt." "Rocket Launcher" "Mape bez nosaukuma" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 358cea3ae..0d0b519cb 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -77,6 +77,7 @@ "Нүүрний тохиргоо болон товчлолыг бичих" "Апп нь Нүүрэндэх товчлол болон тохиргоог өөрчилж чадна." "Виджет ачаалахад асуудал гарав" + "Тохируулга" "Энэ апп нь системийн апп ба устгах боломжгүй." "Пуужин хөөргөгч" "Нэргүй фолдер" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 0c83d8367..fc2ec337a 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -77,6 +77,8 @@ "tulis tetapan dan pintasan Laman Utama" "Membenarkan apl menukar tetapan dan pintasan di Laman Utama." "Masalah memuatkan widget" + + "Ini ialah apl sistem dan tidak boleh dinyahpasang." "Pelancar Roket" "Folder Tanpa Nama" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index f43eea793..71c2fed2a 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -77,6 +77,8 @@ "angi startsideinnstillinger og -snarveier" "Lar appen endre innstillingene og snarveiene på startsiden." "Problem ved innlasting av modul" + + "Dette er en systemapp som ikke kan avinstalleres." "Rocket Launcher" "Mappe uten navn" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index a0add2f24..95b872566 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -77,6 +77,8 @@ "instellingen en snelkoppelingen op de startpagina schrijven" "De app toestaan de instellingen en snelkoppelingen op de startpagina te wijzigen." "Probleem bij het laden van widget" + + "Dit is een systeemapp die niet kan worden verwijderd." "Rocket Launcher" "Naamloze map" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 9d32b34c9..95c8fe349 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -77,6 +77,8 @@ "zapisywanie ustawień i skrótów na ekranie głównym" "Umożliwia aplikacji zmianę ustawień i skrótów na ekranie głównym." "Problem podczas ładowania widżetu" + + "To aplikacja systemowa i nie można jej odinstalować." "Wyrzutnia rakiet" "Folder bez nazwy" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 4734def55..6cecbf925 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -77,6 +77,8 @@ "escrever definições e atalhos do Ecrã principal" "Permite à aplicação alterar as definições e os atalhos no Ecrã Principal." "Problema ao carregar o widget" + + "É uma aplicação de sistema e não pode ser desinstalada." "Lança-mísseis" "Pasta sem nome" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index c1d87644e..f24687fd2 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -77,6 +77,8 @@ "gravar configurações e atalhos da tela inicial" "Permite que o aplicativo altere as configurações e os atalhos na tela inicial." "Problema ao carregar o widget" + + "Este é um aplicativo do sistema e não pode ser desinstalado." "Rocket Launcher" "Pasta sem nome" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 72180bfb8..07cf8fbf9 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -77,6 +77,8 @@ "scrie setări și comenzi rapide pentru ecranul de pornire" "Permite aplicației să modifice setările și comenzile rapide din ecranul de pornire." "Problemă la încărcarea widgetului" + + "Aceasta este o aplicație de sistem și nu poate fi dezinstalată." "Rocket Launcher" "Dosar fără nume" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 1ff78ff68..e065213a4 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -77,6 +77,8 @@ "Изменение настроек и ярлыков главного экрана" "Приложение сможет изменять настройки и ярлыки на главном экране." "Не удалось загрузить виджет" + + "Это системное приложение, его нельзя удалить." "Rocket Launcher" "Папка без названия" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 80a61b79d..4e4d88efb 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -77,6 +77,8 @@ "zápis nastavení a odkazov plochy" "Povoľuje aplikácii zmeniť nastavenia a odkazy na ploche." "Problém s načítaním miniaplikácií" + + "Toto je systémová aplikácia a nedá sa odinštalovať." "Raketomet" "Nepomenovaný priečinok" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 228876f26..f3fa342f2 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -77,6 +77,8 @@ "zapis nastavitev in bližnjic na začetnem zaslonu" "Aplikaciji dovoli spreminjanje nastavitev in bližnjic na začetnem zaslonu." "Težava pri nalaganju pripomočka" + + "To je sistemska aplikacija in je ni mogoče odstraniti." "Raketno izstrelišče" "Neimenovana mapa" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 4b8abfd62..1caf5495e 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -77,6 +77,8 @@ "уписивање подешавања и пречица на почетном екрану" "Дозвољава апликацији да мења подешавања и пречице на почетном екрану." "Проблем при учитавању виџета" + + "Ово је системска апликација и не може да се деинсталира." "Лансер ракета" "Неименовани директоријум" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index bdf2ca612..d33f533bb 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -77,6 +77,8 @@ "skriva inställningar och genvägar för startsidan" "Tillåter att appen ändrar inställningar och genvägar på startsidan." "Det gick inte att läsa in widgeten" + + "Det här är en systemapp som inte kan avinstalleras." "Rocket Launcher" "Namnlös mapp" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 72764ebff..f20819a79 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -77,6 +77,8 @@ "andika mipangilio ya skrini ya Mwanzo na njia za mkato" "Huruhusu programu kubadilisha mipangilio na njia za mkato katika skrini ya Mwanzo." "Tatizo la kupakia wijeti" + + "Hii ni programu ya mfumo na haiwezi kuondolewa." "Kizinduzi cha Roketi" "Folda isiyo na jina" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index e00c55147..ff0887a5e 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -77,6 +77,8 @@ "เขียนการตั้งค่าและทางลัดหน้าแรกแล้ว" "อนุญาตให้แอปเปลี่ยนการตั้งค่าและทางลัดในหน้าแรก" "มีปัญหาขณะโหลดวิดเจ็ต" + + "นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้" "Rocket Launcher" "โฟลเดอร์ที่ไม่มีชื่อ" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 75b5ce13b..be5f8ceb4 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -77,6 +77,8 @@ "magsulat ng mga setting at shortcut ng Home" "Pinapayagan ang app na baguhin ang mga setting at shortcut sa Home." "Problema sa pag-load ng widget" + + "Isa itong app ng system at hindi maaaring i-uninstall." "Rocket Launcher" "Walang Pangalang Folder" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index d8ceb99b0..f90a96d62 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -77,6 +77,8 @@ "Ana ekran ayarlarını ve kısayollarını yaz" "Uygulamaya, Ana ekrandaki ayarları ve kısayolları değiştirme izni verir." "Widget yüklenirken sorun oluştu" + + "Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz." "Roket Fırlatıcı" "Adsız Klasör" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 197b20126..cb89a2aea 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -77,6 +77,8 @@ "записувати налаштування та ярлики головного екрана" "Дозволяє програмі змінювати налаштування та ярлики на головному екрані." "Проблема із завантаженням віджета" + + "Це системна програма, її неможливо видалити." "Rocket Launcher" "Папка без назви" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 164a2c7f1..6102ceed2 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -77,6 +77,8 @@ "ghi cài đặt và lối tắt trên Màn hình chính" "Cho phép ứng dụng thay đổi cài đặt và lối tắt trên Màn hình chính." "Sự cố khi tải tiện ích con" + + "Đây là ứng dụng hệ thống và không thể gỡ cài đặt." "Rocket Launcher" "Thư mục chưa đặt tên" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index c29cfb0d9..3561aec7f 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -77,6 +77,8 @@ "写入主屏幕设置和快捷方式" "允许应用更改主屏幕中的设置和快捷方式。" "加载小部件时出现问题" + + "这是系统应用,无法卸载。" "火箭发射器" "未命名文件夹" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 254a07058..d60af8a2e 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -77,6 +77,8 @@ "寫入主畫面的設定和捷徑" "允許應用程式更改主畫面中的設定和捷徑。" "載入小工具時發生問題" + + "這是系統應用程式,無法將其解除安裝。" "Rocket Launcher" "未命名的資料夾" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index e017ae2c0..18d0890d4 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -77,6 +77,8 @@ "寫入主螢幕設定和捷徑" "允許應用程式變更主螢幕中的設定和捷徑。" "載入小工具時發生問題" + + "這是系統應用程式,不可解除安裝。" "Rocket Launcher" "未命名的資料夾" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index d3d2fbba6..094c227b8 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -77,6 +77,8 @@ "bhala izilungiselelo zokuthi Ikhaya nezinqamuleli" "Ivumela uhlelo lokusebenza ukuthi lushintshe izilungiselelo nezinqamuleli Ekhaya." "Inkinga yokulayisha iwijethi" + + "Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa." "Isiqalisi se-Rocket" "Ifolda engenagama" -- cgit v1.2.3 From 651c1b0a2baf5b3dfeacbdf47cd6c7301e67228f Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Tue, 26 Aug 2014 22:03:04 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I06640f863a8f0d39531dab46ecc5924850b16cb6 Auto-generated-cl: translation import --- res/values-af/strings.xml | 6 ++++-- res/values-am/strings.xml | 6 ++++-- res/values-ar/strings.xml | 6 ++++-- res/values-bg/strings.xml | 6 ++++-- res/values-ca/strings.xml | 6 ++++-- res/values-cs/strings.xml | 6 ++++-- res/values-da/strings.xml | 6 ++++-- res/values-de/strings.xml | 6 ++++-- res/values-el/strings.xml | 3 +++ res/values-en-rGB/strings.xml | 6 ++++-- res/values-en-rIN/strings.xml | 6 ++++-- res/values-es-rUS/strings.xml | 6 ++++-- res/values-es/strings.xml | 6 ++++-- res/values-et-rEE/strings.xml | 6 ++++-- res/values-fa/strings.xml | 6 ++++-- res/values-fi/strings.xml | 6 ++++-- res/values-fr-rCA/strings.xml | 6 ++++-- res/values-fr/strings.xml | 6 ++++-- res/values-hi/strings.xml | 6 ++++-- res/values-hr/strings.xml | 6 ++++-- res/values-hu/strings.xml | 6 ++++-- res/values-hy-rAM/strings.xml | 6 ++++-- res/values-in/strings.xml | 6 ++++-- res/values-it/strings.xml | 6 ++++-- res/values-iw/strings.xml | 6 ++++-- res/values-ja/strings.xml | 6 ++++-- res/values-ka-rGE/strings.xml | 6 ++++-- res/values-km-rKH/strings.xml | 8 +++++--- res/values-ko/strings.xml | 6 ++++-- res/values-lo-rLA/strings.xml | 7 +++++-- res/values-lt/strings.xml | 6 ++++-- res/values-lv/strings.xml | 6 ++++-- res/values-mn-rMN/strings.xml | 3 +++ res/values-ms-rMY/strings.xml | 6 ++++-- res/values-nb/strings.xml | 6 ++++-- res/values-nl/strings.xml | 6 ++++-- res/values-pl/strings.xml | 6 ++++-- res/values-pt-rPT/strings.xml | 6 ++++-- res/values-pt/strings.xml | 6 ++++-- res/values-ro/strings.xml | 6 ++++-- res/values-ru/strings.xml | 6 ++++-- res/values-sk/strings.xml | 6 ++++-- res/values-sl/strings.xml | 6 ++++-- res/values-sr/strings.xml | 6 ++++-- res/values-sv/strings.xml | 6 ++++-- res/values-sw/strings.xml | 6 ++++-- res/values-th/strings.xml | 6 ++++-- res/values-tl/strings.xml | 6 ++++-- res/values-tr/strings.xml | 6 ++++-- res/values-uk/strings.xml | 6 ++++-- res/values-vi/strings.xml | 6 ++++-- res/values-zh-rCN/strings.xml | 6 ++++-- res/values-zh-rHK/strings.xml | 6 ++++-- res/values-zh-rTW/strings.xml | 6 ++++-- res/values-zu/strings.xml | 6 ++++-- 55 files changed, 220 insertions(+), 107 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 5f10b21a1..4a4bb78a2 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -77,8 +77,7 @@ "skryf Tuis-instellings en -kortpaaie" "Laat die program toe om die instellings en kortpaaie in Tuis te verander." "Kon nie legstuk laai nie" - - + "Stel op" "Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie." "Vuurpyllanseerder" "Naamlose vouer" @@ -98,6 +97,9 @@ "BEGIN VAN NUUTS AF" "Organiseer jou spasie" "Raak en hou agtergrond om muurpapier, legstukke en instellings te bestuur." + "Muurpapiere, legstukke en instellings" + "Raak en hou agtergrond om te pasmaak" + "HET DIT" "Hier\'s \'n vouer" "Om een soos dié te skep, raak en hou \'n program en skuif dit dan oor \'n ander een." "OK" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 9d0b18503..d77744c94 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -77,8 +77,7 @@ "የመነሻ ቅንብሮችን እና አቋራጮችን ይጽፋል" "መተግብሪያው ቅንብሮችን እና አቋራጮችን በመነሻ ውስጥ እንዲቀይራቸው ያስችለዋል።" "ፍርግም የመጫን ችግር" - - + "ማዋቀሪያ" "ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።" "የሮኬት ማስጀመሪያ" "ስም-አልባ አቃፊ" @@ -98,6 +97,9 @@ "እንደ አዲስ ይጀምሩ" "ቦታዎን ያደራጁ" "የግድግዳ ወረቀት፣ ምግብሮችን እና ቅንብሮችን ለማቀናበር ጀርባውን ይንኩ እና ይያዙት።" + "የግድግዳ ወረቀቶች፣ ንዑስ ፕሮግራሞች እና ቅንብሮች" + "ለማበጀት ጀርባውን ነክተው ይያዙት" + "ገባኝ" "አንድ አቃፊ እነሆ" "አንድ እንደዚህ አይነት ለመፍጠር መተግበሪያውን ነክተው ይያዙት እና ወደ ሌላ ያንቀሳቅሱት።" "እሺ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index b0d6589a3..8b6aa025c 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -77,8 +77,7 @@ "كتابة إعدادات واختصارات الشاشة الرئيسية" "للسماح للتطبيق بتغيير الإعدادات والاختصارات في الشاشة الرئيسية." "حدثت مشكلة أثناء تحميل الأداة" - - + "الإعداد" "هذا تطبيق نظام وتتعذر إزالته." "قاذفة صواريخ" "مجلد بدون اسم" @@ -98,6 +97,9 @@ "بداية جديدة" "تنظيم مساحتك" "المس مع الاستمرار الجزء الخلفي من صورة الشاشة لإدارة الخلفية والأدوات والإعدادات." + "الخلفيات والأدوات والإعدادات" + "المس مع الاستمرار الخلفية لتخصيصها" + "حسنًا" "إليك المجلد" "لإنشاء مجلد مثل هذا، المس أحد التطبيقات مع استمرار اللمس، ثم حركه فوق آخر." "موافق" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 0b59e1ae2..dcd193050 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -77,8 +77,7 @@ "запис на настройките и преките пътища в Начало" "Разрешава на приложението да променя настройките и преките пътища в Начало." "Проблем при зареждане на приспособлението" - - + "Настройване" "Това е системно приложение и не може да се деинсталира." "Ракетна площадка" "Папка без име" @@ -98,6 +97,9 @@ "СТАРТИРАНЕ ОТНАЧАЛО" "Организиране на мястото ви" "Докоснете и задръжте фона, за да управлявате тапета, приспособленията и настройките." + "Тапети, приспособления и настройки" + "Докоснете и задръжте фона за персонализиране" + "РАЗБРАХ" "Ето една папка" "За да създадете подобна, докоснете и задръжте приложение, след което го преместете върху друго." "ОK" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 82db60284..6d1023545 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -77,8 +77,7 @@ "escriu la configuració i les dreceres de la pantalla d\'inici" "Permet que l\'aplicació canviï la configuració i les dreceres de la pantalla d\'inici." "S\'ha produït un problema en carregar el widget" - - + "Configuració" "Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar." "Rocket Launcher" "Carpeta sense nom" @@ -98,6 +97,9 @@ "NOU COMENÇAMENT" "Organitza el teu espai" "Toca i mantén premut el fons per gestionar el fons de pantalla, els widgets i la configuració." + "Fons de pantalla, widgets i configuració" + "Mantén premut el fons per fer personalitzacions." + "D\'ACORD" "Aquí hi ha una carpeta" "Per crear-ne una com aquesta, mantén premuda una aplicació i, a continuació, mou-la sobre una altra." "D\'acord" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 0d2a60768..f729a460e 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -77,8 +77,7 @@ "zápis nastavení a odkazů plochy" "Umožňuje aplikaci změnit nastavení a odkazy na ploše." "Problém s načtením widgetu" - - + "Nastavení" "Toto je systémová aplikace a nelze ji odinstalovat." "Rocket Launcher" "Složka bez názvu" @@ -98,6 +97,9 @@ "ZAČÍT S VÝCHOZÍM ROZVRŽENÍM" "Organizace prostoru" "Chcete-li spravovat tapetu, widgety a nastavení, dotkněte se pozadí a přidržte je." + "Tapety, widgety a nastavení" + "Pozadí můžete přizpůsobit klepnutím a podržením" + "ROZUMÍM" "Toto je složka" "Chcete-li vytvořit složku, přetáhněte aplikaci na jinou aplikaci." "OK" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 1a71c17b6..85159b2e1 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -77,8 +77,7 @@ "skrive indstillinger og genveje for startskærmen" "Tillader, at appen ændrer indstillingerne og genvejene på startskærmen." "Der er problemer med indlæsning af widgetten" - - + "Konfigurer" "Dette er en systemapp, som ikke kan afinstalleres." "Rocket Launcher" "Unavngiven mappe" @@ -98,6 +97,9 @@ "START PÅ EN FRISK" "Organiser din arbejdsplads" "Tryk på en baggrund, og hold fingeren nede for at administrere baggrunde, widgets og indstillinger." + "Baggrunde, widgets og indstillinger" + "Tryk på baggrunden, og hold fingeren nede for at tilpasse den" + "OK, FORSTÅET" "Her kan du se en mappe" "Du kan oprette en mappe magen til denne ved at trykke på en app og holde fingeren nede, mens du flytter appen til en anden mappe." "OK" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index f04fb1602..698a25413 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -77,8 +77,7 @@ "Einstellungen und Verknüpfungen für den Startbildschirm schreiben" "Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu ändern" "Problem beim Laden des Widgets" - - + "Einrichten" "Dies ist eine Systemanwendung, die nicht deinstalliert werden kann." "Rocket Launcher" "Unbenannter Ordner" @@ -98,6 +97,9 @@ "Standardübersicht verwenden" "Arbeitsbereich organisieren" "Hintergrund berühren und halten, um Hintergrund, Widgets und Einstellungen zu verwalten" + "Hintergründe, Widgets & Einstellungen" + "Berühren und halten Sie den Hintergrund, um ihn anzupassen." + "OK" "Hier ist ein Ordner" "Um einen Ordner zu erstellen, berühren und halten Sie eine App und verschieben Sie sie auf eine andere." "OK" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 2ee3c2166..3eec27d4f 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -97,6 +97,9 @@ "ΝΕΑ ΕΝΑΡΞΗ" "Οργανώστε το χώρο σας" "Αγγίξτε παρατεταμένα το φόντο για να διαχειριστείτε την ταπετσαρία, τα γραφικά στοιχεία και τις ρυθμίσεις." + "Ταπετσαρίες, γραφικά στοιχεία και ρυθμίσεις" + "Αγγίξτε παρατεταμένα το παρασκήνιο για προσαρμογή" + "ΕΓΙΝΕ" "Ορίστε ένας φάκελος" "Για να δημιουργήσετε έναν φάκελο σαν κι αυτόν, πατήστε παρατεταμένα μια εφαρμογή και στη συνέχεια, μετακινήστε τη πάνω σε μια άλλη." "OK" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index d127c27fc..615e5c90d 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -77,8 +77,7 @@ "write Home settings and shortcuts" "Allows the app to change the settings and shortcuts in Home." "Problem loading widget" - - + "Setup" "This is a system app and can\'t be uninstalled." "Rocket Launcher" "Unnamed Folder" @@ -98,6 +97,9 @@ "START AFRESH" "Organise your space" "Touch & hold background to manage wallpaper, widgets and settings." + "Wallpapers, widgets, & settings" + "Touch & hold background to customise" + "GOT IT" "Here\'s a folder" "To create one like this, touch & hold an app, then move it over another." "OK" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index d127c27fc..615e5c90d 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -77,8 +77,7 @@ "write Home settings and shortcuts" "Allows the app to change the settings and shortcuts in Home." "Problem loading widget" - - + "Setup" "This is a system app and can\'t be uninstalled." "Rocket Launcher" "Unnamed Folder" @@ -98,6 +97,9 @@ "START AFRESH" "Organise your space" "Touch & hold background to manage wallpaper, widgets and settings." + "Wallpapers, widgets, & settings" + "Touch & hold background to customise" + "GOT IT" "Here\'s a folder" "To create one like this, touch & hold an app, then move it over another." "OK" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 1d38832de..31afa3e52 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -77,8 +77,7 @@ "escribir configuración y accesos directos de la pantalla principal" "Permite que la aplicación cambie la configuración y los accesos directos de la pantalla principal." "Problema al cargar el widget" - - + "Configuración" "Esta es una aplicación del sistema y no se puede desinstalar." "Lanzacohetes" "Carpeta sin nombre" @@ -98,6 +97,9 @@ "EMPEZAR DE CERO" "Organiza tu espacio" "Mantén presionado el fondo para administrar el fondo de pantalla, los widgets y la configuración." + "Fondos, widgets y configuración" + "Mantén presionado el fondo para personalizarlo" + "ENTENDIDO" "Aquí tienes una carpeta" "Para crear una carpeta como esta, mantén presionada una aplicación y luego muévela sobre otra." "Aceptar" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 87982de52..4e9141208 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -77,8 +77,7 @@ "escribir información de accesos directos y de ajustes de la pantalla de inicio" "Permite que las aplicaciones cambien los ajustes y los accesos directos de la pantalla de inicio." "Problema al cargar el widget" - - + "Configuración" "Esta aplicación es del sistema y no se puede desinstalar." "Rocket Launcher" "Carpeta sin nombre" @@ -98,6 +97,9 @@ "AJUSTES PREDETERMINADOS" "Organiza tu espacio" "Mantén pulsado el fondo para gestionar el fondo de pantalla, los widgets y los ajustes." + "Fondos de pantalla, widgets y ajustes" + "Mantén pulsado el fondo para personalizarlo" + "ENTENDIDO" "Esto es una carpeta" "Para crear una carpeta como esta, mantén pulsada una aplicación y muévela sobre otra." "Aceptar" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index b33987ef9..fa352a1a3 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -77,8 +77,7 @@ "kirjuta avaekraani seaded ja otseteed" "Võimaldab rakendusel muuta avaekraanil seadeid ja otseteid." "Probleem vidina laadimisel" - - + "Seadistamine" "See on süsteemirakendus ja seda ei saa desinstallida." "Rocket Launcher" "Nimetu kaust" @@ -98,6 +97,9 @@ "ALUSTA ALGUSEST" "Korraldage oma ruumi" "Taustapildi, vidinate ja seadete haldamiseks puudutage tausta ning hoidke seda all." + "Taustapildid, vidinad ja seaded" + "Kohandamiseks puudutage ja hoidke tausta all" + "SELGE" "Siin on kaust" "Sarnase loomiseks vajutage ja hoidke rakendust all, seejärel viige see teise peale." "OK" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index f67facc55..13f40e223 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -77,8 +77,7 @@ "نوشتن تنظیمات و میان‌برهای صفحه اصلی" "به برنامه اجازه می‌دهد تنظیمات و میان‌برها را در صفحه اصلی تغییر دهد." "مشکل در بارگیری ابزارک" - - + "تنظیم" "این برنامه سیستمی است و حذف نصب نمی‌شود." "Rocket Launcher" "پوشه بی‌نام" @@ -98,6 +97,9 @@ "شروع تازه" "فضای خود را سازماندهی کنید" "برای مدیریت کاغذدیواری، ابزارک‌ها و تنظیمات، پس‌زمینه را لمس کرده و نگه‌دارید." + "کاغذدیواری‌ها، ابزارک‌ها و تنظیمات" + "برای سفارشی کردن، پس‌زمینه را لمس کنید و نگه‌دارید" + "متوجه شدم" "اینجا یک پوشه است" "برای ایجاد پوشه‌ای مثل این، یک برنامه را لمس کرده و نگه‌دارید، سپس آن را روی برنامه دیگر بیاندازید." "تأیید" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 70c517652..3990a163c 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -77,8 +77,7 @@ "kirjoita aloitusruudun asetuksia ja pikakuvakkeita" "Antaa sovelluksen muuttaa aloitusruudun asetuksia ja pikakuvakkeita." "Ongelma ladattaessa widgetiä" - - + "Asetus" "Tämä on järjestelmäsovellus, eikä sitä voi poistaa." "Sinko" "Nimetön kansio" @@ -98,6 +97,9 @@ "ALOITA ALUSTA" "Järjestä tilasi" "Hallitse taustakuvaa, widgetejä ja asetuksia koskettamalla taustaa pitkään." + "Taustakuvat, widgetit ja asetukset" + "Muokkaa taustaa koskettamalla ja painamalla pitkään" + "SELVÄ" "Tässä on kansio" "Luo se seuraavasti: kosketa sovellusta pitkään ja siirrä se sitten toisen päälle." "OK" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 5e2bed95b..878187403 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -77,8 +77,7 @@ "enregistrer les paramètres de la page d\'accueil et des raccourcis" "Permet à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil." "Problème lors du chargement du widget" - - + "Configuration" "Impossible de désinstaller cette application, car il s\'agit d\'une application système." "Lance-missile" "Dossier sans nom" @@ -98,6 +97,9 @@ "DISPOSITION PAR DÉFAUT" "Organiser son espace personnel" "Maintenez votre doigt sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres." + "Fonds d\'écran, widgets, et paramètres" + "Maintenez le doigt sur le fond d\'écran pour personnaliser" + "J\'ai compris" "Voici un dossier" "Pour créer un dossier comme ça, maintenez votre doigt sur une application, puis déplacez-la sur une autre." "OK" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 0f59ad7c9..31448f18b 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -77,8 +77,7 @@ "modifier les paramètres et les raccourcis de l\'écran d\'accueil" "Permettre à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil" "Problème lors du chargement du widget." - - + "Configuration" "Impossible de désinstaller cette application, car il s\'agit d\'une application système." "Rocket Launcher" "Dossier sans nom" @@ -98,6 +97,9 @@ "DISPOSITION PAR DÉFAUT" "Organisez votre espace" "Appuyez de manière prolongée sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres." + "Fonds d\'écran, widgets et paramètres" + "Appuyez de manière prolongée sur l\'arrière-plan pour le personnaliser." + "OK" "Voici un dossier" "Pour en créer un, appuyez de manière prolongée sur une application, puis déplacez-la vers une autre." "OK" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 0e2aa4fb2..9058b7895 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -77,8 +77,7 @@ "होम सेटिंग और शॉर्टकट लिखें" "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट बदलने देती है." "विजेट लोड करने में समस्‍या" - - + "सेटअप" "यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता." "रॉकेट लॉन्‍चर" "अनामित फ़ोल्डर" @@ -98,6 +97,9 @@ "फिर से शुरू करें" "अपने स्थान को व्यवस्थित करें" "वॉलपेपर, विजेट और सेटिंग प्रबंधित करने के लिए पृष्ठभूमि को स्पर्श करके रखें." + "वॉलपेपर, विजेट और सेटिंग" + "पृष्ठभूमि कस्टमाइज़ करने के लिए स्पर्श करके रखें" + "समझ लिया" "यहां एक फ़ोल्डर है" "इसके जैसा कोई एक बनाने के लिए, किसी ऐप्लिकेशन को स्पर्श करके रखें, फिर इसे किसी दूसरे पर ले जाएं." "ठीक" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index b1a3b2685..c881077b1 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -77,8 +77,7 @@ "pisanje postavki početnog zaslona i prečaca" "Aplikaciji omogućuje promjenu postavki i prečaca na početnom zaslonu." "Problem pri učitavanju widgeta" - - + "Postavljanje" "Ovo je aplikacija sustava i ne može se ukloniti." "Lansirna rampa" "Neimenovana mapa" @@ -98,6 +97,9 @@ "POKRENI NOVO" "Organizirajte svoj prostor" "Dodirnite i držite pozadinu da biste upravljali pozadinskom slikom, widgetima i postavkama." + "Pozadinske slike, widgeti i postavke" + "Dodirnite i zadržite pozadinu radi prilagodbe" + "SHVAĆAM" "Evo mape" "Da biste izradili ovakvu mapu, dodirnite i držite aplikaciju pa je pomaknite preko druge aplikacije." "U redu" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 148140624..08d309558 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -77,8 +77,7 @@ "Főoldal beállításainak és parancsikonjainak írása" "Lehetővé teszi az alkalmazás számára, hogy módosítsa a kezdőképernyő beállításait és parancsikonjait." "Probléma történt a modul betöltésekor" - - + "Beállítás" "Ez egy rendszeralkalmazás, és nem lehet eltávolítani." "Aknavető" "Névtelen mappa" @@ -98,6 +97,9 @@ "TELJESEN ÚJ" "Munkaterület rendezése" "Érintse meg és tartsa lenyomva a hátteret a háttérkép, modulok és beállítások kezeléséhez." + "Háttérképek, modulok és beállítások" + "Érintse meg és tartsa lenyomva a személyre szabáshoz" + "MEGÉRTETTEM" "Itt egy mappa" "Mappa létrehozásához érintse meg és tartsa lenyomva az alkalmazást, majd húzza egy másik fölé." "OK" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 0a3ff0ed5..4ec39c88c 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -77,8 +77,7 @@ "ստեղծել հիմնաէջի կարգավորումներ ու դյուրանցումներ" "Ծրագրին թույլ է տալիս փոփոխել հիմնաէջի կարգավորումներն ու դյուրանցումները:" "Վիջեթի բեռնման խնդիր կա" - - + "Կարգավորում" "Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:" "Հրթիռային թողարկիչ" "Անանուն թղթապանակ" @@ -98,6 +97,9 @@ "ՄԵԿՆԱՐԿԵԼ ԸՍՏ ԿԱՆԽԱԴՐՎԱԾԻ" "Կառավարեք ձեր տարածությունը" "Հպեք և պահեք հետնաշերտի վրա՝ պաստառները, վիջեթներն ու կարգավորումները կառավարելու համար:" + "Պաստառներ, վիջեթներ և կարգավորումներ" + "Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա" + "ՀԱՍԿԱՆԱԼԻ Է" "Ահա մի թղթապանակ" "Նման թղթապանակ ստեղծելու համար հպեք և պահեք որևէ ծրագրի վրա, ապա տեղաշարժեք այն մեկ ուրիշ ծրագրի վրա:" "Լավ" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index e0aa1fb38..137e3cc7d 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -77,8 +77,7 @@ "menulis setelan dan pintasan layar Utama" "Mengizinkan aplikasi mengubah setelan dan pintasan di layar Utama." "Masalah memuat widget" - - + "Siapkan" "Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya." "Rocket Launcher" "Folder Tanpa Nama" @@ -98,6 +97,9 @@ "MULAI DARI AWAL" "Kelola ruang Anda" "Sentuh lama latar belakang untuk mengelola wallpaper, widget, dan setelan." + "Wallpaper, widget, & setelan" + "Sentuh & tahan latar belakang untuk menyesuaikan" + "MENGERTI" "Ini adalah folder" "Untuk membuat seperti yang ini, sentuh lama aplikasi, lalu pindahkan ke atas aplikasi lain." "Oke" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 6f712f761..b01b25174 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -77,8 +77,7 @@ "creazione di impostazioni e scorciatoie in Home" "Consente all\'app di modificare le impostazioni e le scorciatoie in Home." "Errore durante il caricamento del widget" - - + "Configurazione" "Questa è un\'app di sistema e non può essere disinstallata." "Lanciamissili" "Cartella senza nome" @@ -98,6 +97,9 @@ "RICOMINCIA" "Organizza il tuo spazio" "Tocca e tieni premuto lo sfondo per gestire sfondi, widget e impostazioni." + "Sfondi, widget e impostazioni" + "Tocca lo sfondo e tieni premuto per personalizzare" + "OK" "Ecco una cartella" "Per crearne una simile, tocca un\'app e tieni premuto, dopodiché spostala sopra un\'altra." "OK" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index cb4bcc5d6..6318207ff 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -77,8 +77,7 @@ "כתוב הגדרות וקיצורי דרך של דף הבית" "מאפשר לאפליקציה לשנות את ההגדרות וקיצורי הדרך בדף הבית." "בעיה בטעינת ווידג\'ט" - - + "הגדר" "זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה." "Rocket Launcher" "תיקיה ללא שם" @@ -98,6 +97,9 @@ "התחל דף חדש" "ארגן את אזור העבודה שלך" "גע נגיעה רציפה ברקע כדי לנהל את הטפט, רכיבי הווידג\'ט וההגדרות." + "טפטים, ווידג\'טים והגדרות" + "גע והחזק ברקע לביצוע התאמה אישית" + "הבנתי" "הנה תיקייה" "כדי ליצור תיקייה כזו, גע נגיעה רציפה באפליקציה, ולאחר מכן גרור ושחרר אותו על-גבי אפליקציה אחרת." "אישור" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 36eaf5ab0..232845af0 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -77,8 +77,7 @@ "ホームの設定とショートカットの書き込み" "ホームの設定とショートカットの変更をアプリに許可します。" "ウィジェットを表示できません" - - + "セットアップ" "このシステムアプリはアンインストールできません。" "Rocket Launcher" "名前のないフォルダ" @@ -98,6 +97,9 @@ "初期状態にリセットする" "スペースを整理" "壁紙、ウィジェット、設定を管理するには、背景を押し続けます。" + "壁紙、ウィジェット、設定" + "カスタマイズするにはバックグラウンドを押し続けます" + "OK" "これがフォルダです" "これと同じフォルダを作成するには、アプリを押し続けてから別のアプリの上に移動します。" "OK" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index e354ab00b..4403c8b41 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -77,8 +77,7 @@ "მთავარი ეკრანის პარამეტრებისა და მალსახმობების ჩაწერა" "აპისთვის მთავარი ეკრანის პარამეტრებისა და მალსახმობების შეცვლის უფლების მიცემა." "პრობლემა ვიჯეტის ჩატვირთვისას" - - + "დაყენება" "ეს სისტემური აპია და მისი წაშლა შეუძლებელია." "ფეიერვერკი" "უსახელო საქაღალდე" @@ -98,6 +97,9 @@ "სტანდარტული განლაგება" "თქვენი სივრცის ორგანიზება" "თუ გსურთ ფონების, ვიჯეტების და პარამეტრების მართვა, შეეხეთ და არ აუშვათ ფონს." + "ფონები, ვიჯეტები, & პარამეტრები" + "მოსარგებათ შეეხეთ & დააყოვნეთ ფონზე" + "გასაგებია" "აი, საქაღალდე" "ასეთის შესაქმნელად, შეეხეთ და დააყოვნეთ აპზე, ხოლო შემდეგ გადააჩოჩეთ შემდეგზე." "კარგი" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 56c8bc716..bcd606000 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -77,8 +77,7 @@ "សរសេរ​ការ​កំណត់ ​និង​ផ្លូវកាត់​​លើ​អេក្រង់​ដើម" "អនុញ្ញាត​ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​កំណត់ និង​ផ្លូវ​កាត់​ក្នុង​អេក្រង់​ដើម។" "បញ្ហា​ក្នុង​ការ​ផ្ទុក​ធាតុ​​ក្រាហ្វិក" - - + "រៀបចំ" "នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។" "កម្មវិធី​ចាប់ផ្ដើម​រ៉ូកែត" "ថត​គ្មាន​ឈ្មោះ" @@ -87,7 +86,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍" + "សូម​ស្វាគមន៍​" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" @@ -98,6 +97,9 @@ "ចាប់ផ្ដើម​ធ្វើ​ឲ្យ​ស្រស់" "រៀបចំ​ចន្លោះ​របស់​អ្នក" "ប៉ះ & សង្កត់​លើ​ផ្ទៃ​ខាង​ក្រោម ដើម្បី​គ្រប់គ្រង​ផ្ទាំង​រូបភាព, ធាតុ​ក្រាហ្វិក និង​ការ​កំណត់។" + "ផ្ទាំងរូបភាព,ធាតុក្រាហ្វិក & ការកំណត់" + "ប៉ះ & សង្កត់​ផ្ទៃ​ខាង​ក្រោយ​ដើម្បី​ប្ដូរ​តាម​​តម្រូវ​ការ" + "យល់​ហើយ" "នេះ​ជា​ថត" "ដើម្បី​បង្កើត​មួយ​ដូច​នេះ ប៉ះ & សង្កត់​​លើ​កម្មវិធី បន្ទាប់​មក​ផ្លាស់ទី​វា​ទៅ​លើ​ធាតុ​មួយ​ផ្សេង​ទៀត។" "យល់ព្រម" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 0746de94c..41c854ec3 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -77,8 +77,7 @@ "홈 설정 및 바로가기 쓰기" "앱이 홈에 있는 설정 및 바로가기를 변경할 수 있도록 합니다." "위젯을 로드하는 중 문제가 발생했습니다." - - + "설정" "시스템 앱은 제거할 수 없습니다." "로켓 실행기" "이름이 없는 폴더" @@ -98,6 +97,9 @@ "새로 시작" "공간 관리하기" "배경화면, 위젯, 설정을 관리하려면 백그라운드를 길게 터치합니다." + "배경화면, 위젯, 설정" + "백그라운드를 길게 터치하여 맞춤설정합니다." + "확인" "폴더" "폴더를 만들려면 앱을 길게 터치한 다음 다른 앱 위에 올려 놓으세요." "확인" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index e628100d0..1d953ffde 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -36,7 +36,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ" + "ຍົກ​ເລີກ​" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -97,6 +97,9 @@ "ເລີ່ມຕົ້ນໃໝ່" "ຈັດການພື້ນທີ່ຂອງທ່ານ" "ແຕະຄ້າງໄວ້ທີ່ພາບພື້ນຫຼັງເພື່ອຈັດການພາບພື້ນຫຼັງ, ວິດເຈັດແລະການຕັ້ງຄ່າ." + "​ຮູບ​ພື້ນຫຼັງ, ວິດເຈັດ, & ​ການ​ຕັ້ງ​ຄ່າ" + "ແຕະທີ່​ພາບ​ພື້ນ​ຫລັງ​ຄ້າງ​ໄວ້​ເພື່ອ​ປັບ​ແຕ່ງ" + "ເຂົ້າໃຈແລ້ວ" "ນີ້ແມ່ນໂຟນເດີ" "ເພື່ອ​ສ້າງ​ອັນໃໝ່​ແບບນີ້ ໃຫ້​ແຕະ​ຄ້າງ​ໄວ້​ທີ່​ແອັບຯ​ທີ່​ຕ້ອງການ​ຍ້າຍ​ແລ້ວ​ລາກ​ມັນ​ໄປ​ຫາ​ໂຕ​ອື່ນ." "ຕົກລົງ" @@ -115,7 +118,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ" + "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 29cd23b6b..f7db79291 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -77,8 +77,7 @@ "rašyti pagrindinio puslapio nustatymus ir sparčiuosius klavišus" "Programai leidžiama keisti pagrindinio puslapio nustatymus ir sparčiuosius klavišus." "Problema įkeliant valdiklį" - - + "Sąranka" "Tai sistemos programa ir jos negalima pašalinti." "Rocket Launcher" "Aplankas be pavadinimo" @@ -98,6 +97,9 @@ "PRADĖTI IŠ NAUJO" "Tvarkykite savo vietą" "Palieskite ir laikykite foną, jei norite tvarkyti ekrano foną, valdiklius ir nustatymus." + "Ekrano fonai, valdikliai ir nustatymai" + "Jei norite tinkinti, palieskite ir palaikykite foną" + "SUPRATAU" "Štai aplankas" "Kad sukurtumėte tokį patį, palieskite ir laikykite programą, tada perkelkite ją virš kitos programos." "Gerai" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index b694dd093..75eb0548b 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -77,8 +77,7 @@ "rakstīt sākuma ekrāna iestatījumus un saīsnes" "Ļauj lietotnei mainīt iestatījumus un saīsnes sākuma ekrānā." "Ielādējot logrīku, radās problēma." - - + "Notiek iestatīšana" "Šī ir sistēmas lietotne, un to nevar atinstalēt." "Rocket Launcher" "Mape bez nosaukuma" @@ -98,6 +97,9 @@ "SĀKT NO SĀKUMA" "Kārtojiet savu darbvietu" "Pieskarieties fonam un turiet to, lai pārvaldītu fona tapeti, logrīkus un iestatījumus." + "Fona tapetes, logrīki un iestatījumi" + "Lai pielāgotu, pieskarieties fonam un turiet to nospiestu." + "SAPRATU!" "Lūk, mape!" "Lai izveidotu tādu pašu, pieskarieties lietotnei un turiet to, pēc tam pārvietojiet to virs citas lietotnes." "Labi" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 0d0b519cb..34fb79423 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -97,6 +97,9 @@ "ШИНЭЭР ЭХЛЭХ" "Өөрийнхөө зайг тохируулаарай" "Арын дэвсгэр дээр хүрээд & дарснаар ханын зураг, виджет болон тохиргоог өөрчилж болно." + "Дэвсгэр зураг, виджет, & тохиргоо" + "Тааруулахын тулд арын дэлгэцэнд хүрээд & барина уу" + "Ойлголоо" "Фолдер энд байна" "Үүнтэй адилханыг үүсгэхийн тулд апп дээр хүрч & бариад нөгөөхийн дээр зөөнө үү." "Тийм" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index fc2ec337a..3c5876268 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -77,8 +77,7 @@ "tulis tetapan dan pintasan Laman Utama" "Membenarkan apl menukar tetapan dan pintasan di Laman Utama." "Masalah memuatkan widget" - - + "Persediaan" "Ini ialah apl sistem dan tidak boleh dinyahpasang." "Pelancar Roket" "Folder Tanpa Nama" @@ -98,6 +97,9 @@ "MULAKAN YANG BAHARU" "Susun ruang anda" "Sentuh & tahan latar belakang untuk mengurus kertas dinding, widget dan tetapan." + "Kertas dinding, widget & tetapan" + "Sentuh & tahan latar belakang untuk memperibadikan" + "FAHAM" "Ini ada folder" "Untuk membuat satu folder seperti ini, sentuh & tahan apl, kemudian alihkan ke atas folder lain." "OK" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 71c2fed2a..ff8280e73 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -77,8 +77,7 @@ "angi startsideinnstillinger og -snarveier" "Lar appen endre innstillingene og snarveiene på startsiden." "Problem ved innlasting av modul" - - + "Konfigurering" "Dette er en systemapp som ikke kan avinstalleres." "Rocket Launcher" "Mappe uten navn" @@ -98,6 +97,9 @@ "START PÅ NYTT" "Organiser plassen din" "Trykk og hold på bakgrunnen for å administrere bakgrunnen, moduler og innstillinger." + "Bakgrunner, moduler og innstillinger" + "Trykk og hold på bakgrunnen for å tilpasse den" + "SKJØNNER" "Dette er en mappe" "For å opprette en som denne, trykker og holder du på en app og flytter den over en annen." "OK" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 95b872566..470eb8722 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -77,8 +77,7 @@ "instellingen en snelkoppelingen op de startpagina schrijven" "De app toestaan de instellingen en snelkoppelingen op de startpagina te wijzigen." "Probleem bij het laden van widget" - - + "Configuratie" "Dit is een systeemapp die niet kan worden verwijderd." "Rocket Launcher" "Naamloze map" @@ -98,6 +97,9 @@ "OPNIEUW BEGINNEN" "Uw ruimte indelen" "Blijf de achtergrond aanraken om de achtergrond, widgets en instellingen te beheren." + "Achtergronden, widgets en instellingen" + "Blijf de achtergrond aanraken om deze aan te passen" + "OK" "Dit is een map" "Als u een map zoals deze wilt maken, blijft u een app aanraken en schuift u deze boven op een andere app." "OK" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 95c8fe349..61a51e0a5 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -77,8 +77,7 @@ "zapisywanie ustawień i skrótów na ekranie głównym" "Umożliwia aplikacji zmianę ustawień i skrótów na ekranie głównym." "Problem podczas ładowania widżetu" - - + "Konfiguracja" "To aplikacja systemowa i nie można jej odinstalować." "Wyrzutnia rakiet" "Folder bez nazwy" @@ -98,6 +97,9 @@ "ZACZNIJ OD NOWA" "Uporządkuj obszar roboczy" "Kliknij i przytrzymaj tło, by zmienić tapetę, widżety lub ustawienia." + "Tapety, widżety i ustawienia" + "Kliknij i przytrzymaj tło, by dostosować" + "OK" "Tu jest folder" "Aby utworzyć taki sam, kliknij i przytrzymaj aplikację, a następnie przenieś ją na następną." "OK" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 6cecbf925..2fe062a4a 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -77,8 +77,7 @@ "escrever definições e atalhos do Ecrã principal" "Permite à aplicação alterar as definições e os atalhos no Ecrã Principal." "Problema ao carregar o widget" - - + "Configuração" "É uma aplicação de sistema e não pode ser desinstalada." "Lança-mísseis" "Pasta sem nome" @@ -98,6 +97,9 @@ "COMEÇAR DO INÍCIO" "Organizar o seu espaço" "Toque sem soltar no fundo para gerir a imagem de fundo, os widgets e as definições." + "Imagens de fundo, widgets e definições" + "Toque sem soltar no fundo para personalizar" + "COMPREENDI" "Eis uma pasta" "Para criar uma pasta, toque sem soltar numa aplicação e arraste-a para cima de outra aplicação." "OK" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index f24687fd2..20b193aa3 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -77,8 +77,7 @@ "gravar configurações e atalhos da tela inicial" "Permite que o aplicativo altere as configurações e os atalhos na tela inicial." "Problema ao carregar o widget" - - + "Configuração" "Este é um aplicativo do sistema e não pode ser desinstalado." "Rocket Launcher" "Pasta sem nome" @@ -98,6 +97,9 @@ "COMEÇAR DO ZERO" "Organize seu espaço" "Toque e mantenha pressionada a tela de fundo para gerenciar o plano de fundo, os widgets e as configurações." + "Plano de fundo, widgets e configurações" + "Toque e mantenha pressionado o segundo plano para personalizar" + "ENTENDI" "Aqui está uma pasta" "Para criar uma pasta como esta, mantenha pressionado um aplicativo e mova-o para cima de outro." "Ok" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 07cf8fbf9..04aebc8b9 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -77,8 +77,7 @@ "scrie setări și comenzi rapide pentru ecranul de pornire" "Permite aplicației să modifice setările și comenzile rapide din ecranul de pornire." "Problemă la încărcarea widgetului" - - + "Configurați" "Aceasta este o aplicație de sistem și nu poate fi dezinstalată." "Rocket Launcher" "Dosar fără nume" @@ -98,6 +97,9 @@ "REÎNCEPEȚI" "Organizați-vă spațiul" "Atingeți lung fundalul pentru a gestiona imaginea de fundal, widgeturile și setările." + "Imagini de fundal, widgeturi și setări" + "Atingeți lung fundalul pentru a-l personaliza" + "AM ÎNȚELES" "Iată un dosar" "Pentru a crea un dosar similar, atingeți și țineți degetul pe o aplicație, apoi mutați-o deasupra alteia." "OK" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index e065213a4..b1d7713a0 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -77,8 +77,7 @@ "Изменение настроек и ярлыков главного экрана" "Приложение сможет изменять настройки и ярлыки на главном экране." "Не удалось загрузить виджет" - - + "Настройка" "Это системное приложение, его нельзя удалить." "Rocket Launcher" "Папка без названия" @@ -98,6 +97,9 @@ "ИСПОЛЬЗОВАТЬ СТАНДАРТНЫЙ МАКЕТ" "Организация рабочего пространства" "Чтобы перейти к управлению обоями, виджетами и настройками, нажмите на фоновое изображение и удерживайте его." + "Обои, виджеты и настройки" + "Чтобы выполнить настройку, коснитесь фона и удерживайте его" + "ОК" "Это папка" "Чтобы создать папку, нажмите и удерживайте значок приложения, а затем перетащите его на другой значок." "ОК" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 4e4d88efb..a00367949 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -77,8 +77,7 @@ "zápis nastavení a odkazov plochy" "Povoľuje aplikácii zmeniť nastavenia a odkazy na ploche." "Problém s načítaním miniaplikácií" - - + "Nastavenie" "Toto je systémová aplikácia a nedá sa odinštalovať." "Raketomet" "Nepomenovaný priečinok" @@ -98,6 +97,9 @@ "ZAČAŤ S PREDVOLENÝM ROZLOŽENÍM" "Usporiadajte svoj priestor" "Ak chcete spravovať tapetu, miniaplikácie a nastavenia, dotknite sa pozadia a podržte." + "Pozadia, miniaplikácie a nastavenia" + "Ak si chcete pozadie prispôsobiť, klepnite naň a podržte ho" + "ROZUMIEM" "Tu je priečinok" "Ak chcete vytvoriť takýto priečinok, dotknite sa príslušnej aplikácie a podržte ju. Potom ju presuňte na druhú aplikáciu." "OK" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index f3fa342f2..9c5bebd15 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -77,8 +77,7 @@ "zapis nastavitev in bližnjic na začetnem zaslonu" "Aplikaciji dovoli spreminjanje nastavitev in bližnjic na začetnem zaslonu." "Težava pri nalaganju pripomočka" - - + "Nastavitev" "To je sistemska aplikacija in je ni mogoče odstraniti." "Raketno izstrelišče" "Neimenovana mapa" @@ -98,6 +97,9 @@ "SVEŽ ZAČETEK" "Organizirajte svoj prostor" "Če želite upravljati ozadje, pripomočke in nastavitve, se dotaknite ozadja in ga pridržite." + "Ozadja, pripomočki in nastavitve" + "Za prilagajanje se dotaknite ozadja in ga pridržite" + "V REDU" "To je mapa" "Če želite ustvariti mapo, podobno tej, se dotaknite aplikacije in jo pridržite, nato pa jo premaknite nad drugo." "V redu" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 1caf5495e..421f8d3ac 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -77,8 +77,7 @@ "уписивање подешавања и пречица на почетном екрану" "Дозвољава апликацији да мења подешавања и пречице на почетном екрану." "Проблем при учитавању виџета" - - + "Подешавање" "Ово је системска апликација и не може да се деинсталира." "Лансер ракета" "Неименовани директоријум" @@ -98,6 +97,9 @@ "ПОЧНИТЕ ИСПОЧЕТКА" "Организујте простор" "Додирните позадину и задржите да бисте управљали позадином, виџетима и подешавањима." + "Позадине, виџети и подешавања" + "Додирните и задржите позадину да бисте прилагодили" + "ВАЖИ" "Ево једног директоријума" "Да бисте направили директоријум попут овога, додирните и задржите апликацију, па је превуците преко друге." "Потврди" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index d33f533bb..e149c9efa 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -77,8 +77,7 @@ "skriva inställningar och genvägar för startsidan" "Tillåter att appen ändrar inställningar och genvägar på startsidan." "Det gick inte att läsa in widgeten" - - + "Konfiguration" "Det här är en systemapp som inte kan avinstalleras." "Rocket Launcher" "Namnlös mapp" @@ -98,6 +97,9 @@ "BÖRJA OM" "Organisera ditt utrymme" "Tryck länge på bakgrunden om du vill hantera bakgrundsbilder, widgetar och inställningar." + "Bakgrunder, widgetar och inställningar" + "Tryck länge på bakgrunden om du vill anpassa den" + "OK" "Det här är en mapp" "Skapa en till mapp av det här slaget genom att trycka och hålla ned en app och sedan dra den ovanpå en annan." "OK" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index f20819a79..07d091393 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -77,8 +77,7 @@ "andika mipangilio ya skrini ya Mwanzo na njia za mkato" "Huruhusu programu kubadilisha mipangilio na njia za mkato katika skrini ya Mwanzo." "Tatizo la kupakia wijeti" - - + "Sanidi" "Hii ni programu ya mfumo na haiwezi kuondolewa." "Kizinduzi cha Roketi" "Folda isiyo na jina" @@ -100,6 +99,9 @@ "ANZA UPYA" "Panga nafasi yako" "Gusa na ushikilie mandharinyuma ili udhibiti mandhari, wijeti, na mipangilio." + "Mandhari, wijeti, na mipangilio" + "Gusa na ushikilie mandhari ili uweke mapendeleo" + "NIMEELEWA" "Hii ni folda" "Ili kuunda kama hii, gusa na ushikilie programu, kisha ipitishe juu ya nyingine." "SAWA" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index ff0887a5e..beed89844 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -77,8 +77,7 @@ "เขียนการตั้งค่าและทางลัดหน้าแรกแล้ว" "อนุญาตให้แอปเปลี่ยนการตั้งค่าและทางลัดในหน้าแรก" "มีปัญหาขณะโหลดวิดเจ็ต" - - + "ตั้งค่า" "นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้" "Rocket Launcher" "โฟลเดอร์ที่ไม่มีชื่อ" @@ -98,6 +97,9 @@ "เริ่มต้นใหม่" "จัดระเบียบพื้นที่ของคุณ" "แตะพื้นหลังค้างไว้เพื่อจัดการวอลเปเปอร์ วิดเจ็ต และการตั้งค่า" + "วอลเปเปอร์ วิดเจ็ต และการตั้งค่า" + "แตะพื้นหลังค้างไว้เพื่อกำหนดค่า" + "รับทราบ" "นี่คือโฟลเดอร์" "หากต้องการสร้างโฟลเดอร์ลักษณะนี้ แตะแอปค้างไว้ แล้วย้ายไปทับอีกแอปหนึ่ง" "ตกลง" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index be5f8ceb4..7c6acd2ab 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -77,8 +77,7 @@ "magsulat ng mga setting at shortcut ng Home" "Pinapayagan ang app na baguhin ang mga setting at shortcut sa Home." "Problema sa pag-load ng widget" - - + "I-setup" "Isa itong app ng system at hindi maaaring i-uninstall." "Rocket Launcher" "Walang Pangalang Folder" @@ -98,6 +97,9 @@ "MAGSIMULA NANG BAGO" "Ayusin ang iyong espasyo" "Pindutin nang matagal ang background upang pamahalaan ang wallpaper, mga widget at setting" + "Mga wallpaper, widget at setting" + "Pindutin nang matagal ang background upang i-customize" + "NAKUHA KO" "Narito ang isang folder" "Upang gumawa ng katulad nito, pindutin nang matagal ang isang app, pagkatapos ay ilipat ito sa isa pang folder." "OK" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index f90a96d62..6c0bb69a7 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -77,8 +77,7 @@ "Ana ekran ayarlarını ve kısayollarını yaz" "Uygulamaya, Ana ekrandaki ayarları ve kısayolları değiştirme izni verir." "Widget yüklenirken sorun oluştu" - - + "Kurulum" "Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz." "Roket Fırlatıcı" "Adsız Klasör" @@ -98,6 +97,9 @@ "VARSAYILANI KULLAN" "Alanınızı düzenleyin" "Duvar kağıdını, widget\'ları ve ayarları yönetmek için arka plana uzun basın." + "Duvar kağıtları, widget\'lar ve ayarlar" + "Özelleştirmek için arka plana dokunun ve basılı tutun" + "TAMAM" "İşte bir klasör" "Buna benzer bir klasör oluşturmak için uygulamaya uzun basın ve sonra uygulamayı başka bir uygulamanın üzerine taşıyın." "Tamam" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index cb89a2aea..52f3761ac 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -77,8 +77,7 @@ "записувати налаштування та ярлики головного екрана" "Дозволяє програмі змінювати налаштування та ярлики на головному екрані." "Проблема із завантаженням віджета" - - + "Налаштування" "Це системна програма, її неможливо видалити." "Rocket Launcher" "Папка без назви" @@ -98,6 +97,9 @@ "ПАНЕЛЬ ЗАПУСКУ ЗА УМОВЧАННЯМ" "Організуйте робочий простір" "Натисніть і утримуйте фон, щоб керувати фоновим малюнком, віджетами та налаштуваннями." + "Фонові малюнки, віджети й налаштування" + "Натисніть і втримуйте фон, щоб налаштувати робочу область" + "ЗРОЗУМІЛО" "Це папка" "Щоб створити папку, натисніть і утримуйте програму, а потім перетягніть її на іншу." "OК" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 6102ceed2..489123089 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -77,8 +77,7 @@ "ghi cài đặt và lối tắt trên Màn hình chính" "Cho phép ứng dụng thay đổi cài đặt và lối tắt trên Màn hình chính." "Sự cố khi tải tiện ích con" - - + "Thiết lập" "Đây là ứng dụng hệ thống và không thể gỡ cài đặt." "Rocket Launcher" "Thư mục chưa đặt tên" @@ -98,6 +97,9 @@ "BẮT ĐẦU LÀM MỚI" "Sắp xếp không gian của bạn" "Chạm và giữ nền để quản lý hình nền, tiện ích con và cài đặt." + "Hình nền, tiện ích và cài đặt" + "Chạm và giữ nền để tùy chỉnh" + "OK" "Đây là một thư mục" "Để tạo thư mục như thế này, hãy chạm và giữ một ứng dụng, sau đó di chuyển ứng dụng đó lên trên một ứng dụng khác." "OK" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 3561aec7f..3365581be 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -77,8 +77,7 @@ "写入主屏幕设置和快捷方式" "允许应用更改主屏幕中的设置和快捷方式。" "加载小部件时出现问题" - - + "设置" "这是系统应用,无法卸载。" "火箭发射器" "未命名文件夹" @@ -98,6 +97,9 @@ "使用全新配置" "整理您的空间" "触摸并按住背景,即可管理壁纸、小部件和设置。" + "壁纸、小部件和设置" + "触摸并按住背景,即可进行个性化设置" + "知道了" "这是一个文件夹" "要创建一个类似的文件夹,请触摸并按住某个应用,然后将其移至另一个应用上。" "确定" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index d60af8a2e..e1c206339 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -77,8 +77,7 @@ "寫入主畫面的設定和捷徑" "允許應用程式更改主畫面中的設定和捷徑。" "載入小工具時發生問題" - - + "設定" "這是系統應用程式,無法將其解除安裝。" "Rocket Launcher" "未命名的資料夾" @@ -98,6 +97,9 @@ "重新開始" "管理您的空間" "輕觸並按住背景,即可管理桌布、小工具和設定。" + "桌布、小工具和設定" + "輕觸並按住背景即可自訂" + "知道了" "資料夾顯示如下" "如要建立類似的資料夾,請輕觸並按住某個應用程式,然後疊到另一個應用程式之上。" "確定" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 18d0890d4..2d400596f 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -77,8 +77,7 @@ "寫入主螢幕設定和捷徑" "允許應用程式變更主螢幕中的設定和捷徑。" "載入小工具時發生問題" - - + "設定" "這是系統應用程式,不可解除安裝。" "Rocket Launcher" "未命名的資料夾" @@ -98,6 +97,9 @@ "重新開始" "管理您的空間" "輕觸並按住背景,即可管理桌布、小工具和設定。" + "桌布、小工具和設定" + "輕觸並按住背景即可自訂" + "知道了" "資料夾顯示如下" "如要建立類似的資料夾,請輕觸並按住應用程式,然後將應用程式疊放在另一個應用程式上。" "確定" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 094c227b8..89cd5cc12 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -77,8 +77,7 @@ "bhala izilungiselelo zokuthi Ikhaya nezinqamuleli" "Ivumela uhlelo lokusebenza ukuthi lushintshe izilungiselelo nezinqamuleli Ekhaya." "Inkinga yokulayisha iwijethi" - - + "Ukumisa" "Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa." "Isiqalisi se-Rocket" "Ifolda engenagama" @@ -98,6 +97,9 @@ "QALISA KABUSHA" "Hlela isikhala sakho" "Thinta uphinde ubambe okungemuva ukuze uphathe isithombe sangemuva, amawijethi nezilungiselelo." + "Izithombe zangemuva, amawijethi, nezilungiselelo" + "Thinta uphinde ubambe ingemuva ukuze wenze ngokwezifiso" + "NGIYITHOLILE" "Nayi ifolda" "Ukuze udale eyodwa efana nale, thinta uphinde ubambe uhlelo lokusebenza, bese ulidlulisa ngaphezulu kwelinye." "KULUNGILE" -- cgit v1.2.3 From 25c829e618a54c0fa29dda39d0158a35c3856802 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Sun, 31 Aug 2014 23:55:12 -0700 Subject: Import translations. DO NOT MERGE Change-Id: Iec38e800c5675f32b551a26382a376130a1fd535 Auto-generated-cl: translation import --- res/values-fr-rCA/strings.xml | 2 +- res/values-ka-rGE/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 878187403..f42835014 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -97,7 +97,7 @@ "DISPOSITION PAR DÉFAUT" "Organiser son espace personnel" "Maintenez votre doigt sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres." - "Fonds d\'écran, widgets, et paramètres" + "Fonds d\'écran, widgets et paramètres" "Maintenez le doigt sur le fond d\'écran pour personnaliser" "J\'ai compris" "Voici un dossier" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 4403c8b41..2fb51f541 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -98,7 +98,7 @@ "თქვენი სივრცის ორგანიზება" "თუ გსურთ ფონების, ვიჯეტების და პარამეტრების მართვა, შეეხეთ და არ აუშვათ ფონს." "ფონები, ვიჯეტები, & პარამეტრები" - "მოსარგებათ შეეხეთ & დააყოვნეთ ფონზე" + "მოსარგებად შეეხეთ & დააყოვნეთ ფონზე" "გასაგებია" "აი, საქაღალდე" "ასეთის შესაქმნელად, შეეხეთ და დააყოვნეთ აპზე, ხოლო შემდეგ გადააჩოჩეთ შემდეგზე." -- cgit v1.2.3 From 54fe5eec64dfbc8d10c0fcd2d2c3c1d1a6c337c9 Mon Sep 17 00:00:00 2001 From: Derek Prothro Date: Fri, 12 Sep 2014 18:37:31 -0400 Subject: Import translations. DO NOT MERGE Original change: http://ag/527993 Auto-generated-cl: translation import Change-Id: I663b12fa2248cf137eb401535cfb5675d507007d --- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index bcd606000..72fc98a16 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -86,7 +86,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍​" + "សូម​ស្វាគមន៍" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1d953ffde..9ea30d182 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -36,7 +36,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ​" + "ຍົກ​ເລີກ" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -118,7 +118,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ​" + "ລຶບ" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." -- cgit v1.2.3 From 394ede2bc503b3bb1b309d0637e6cea2275e58c4 Mon Sep 17 00:00:00 2001 From: Derek Prothro Date: Fri, 12 Sep 2014 18:41:17 -0400 Subject: Import translations. DO NOT MERGE Original change: http://ag/537169 Auto-generated-cl: translation import Change-Id: I4597704c7522718c4fa877d8fff01dd7d44b872c --- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 72fc98a16..bcd606000 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -86,7 +86,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍" + "សូម​ស្វាគមន៍​" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 9ea30d182..1d953ffde 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -36,7 +36,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ" + "ຍົກ​ເລີກ​" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -118,7 +118,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ" + "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." -- cgit v1.2.3 From 3eaa6701dd71da33d451fdf3cbf7440c72471d66 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Tue, 16 Sep 2014 22:13:19 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I0b92d710dfc9c17bec79017586315299128f312a Auto-generated-cl: translation import --- res/values-am/strings.xml | 2 +- res/values-hi/strings.xml | 4 ++-- res/values-uk/strings.xml | 8 ++++---- res/values-zh-rHK/strings.xml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index d77744c94..25931db7d 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -96,7 +96,7 @@ "አዶዎችን ይቅዱ" "እንደ አዲስ ይጀምሩ" "ቦታዎን ያደራጁ" - "የግድግዳ ወረቀት፣ ምግብሮችን እና ቅንብሮችን ለማቀናበር ጀርባውን ይንኩ እና ይያዙት።" + "ልጣፍ ፣ ምግብሮችን እና ቅንብሮችን ለማቀናበር ጀርባውን ይንኩ እና ይያዙት።" "የግድግዳ ወረቀቶች፣ ንዑስ ፕሮግራሞች እና ቅንብሮች" "ለማበጀት ጀርባውን ነክተው ይያዙት" "ገባኝ" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 9058b7895..0ae90d469 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -27,7 +27,7 @@ "डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है" "विजेट" "विजेट" - "स्मृति दिखाएं" + "मेमोरी दिखाएं" "विजेट को चुनने के लिए स्‍पर्श करके रखें." "खरीदारी करें" "%1$d × %2$d" @@ -36,7 +36,7 @@ "फ़ोल्‍डर का नाम" "फ़ोल्‍डर का नाम बदलें" "ठीक" - "रद्द करें" + "रहने दें" "होम स्‍क्रीन में जोड़ें" "ऐप्लिकेशन" "शॉर्टकट" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 52f3761ac..e5b5cd8d2 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -23,7 +23,7 @@ "Головний екран" "Базові програми Android" - "Програму не встановлено." + "Додаток видалено." "Завантажений додаток вимкнено в безпечному режимі" "Віджети" "Віджети" @@ -38,7 +38,7 @@ "OК" "Скасувати" "Додати на головний екран" - "Програми" + "Додатки" "Ярлики" "Віджети" "На головних екранах більше немає місця." @@ -50,7 +50,7 @@ "Ярлик \"%s\" уже існує." "Вибрати ярлик" "Вибрати програму" - "Програми" + "Додатки" "Головний екран" "Вилучити" "Видалити" @@ -59,7 +59,7 @@ "Про програму" "Пошук" "Голосовий пошук" - "Програми" + "Додатки" "Вилучити" "Видалити оновлення" "Видалити програму" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index e1c206339..457d5be9e 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -69,9 +69,9 @@ "已選取 1 個資料夾" "已選取 1 個捷徑" "安裝捷徑" - "允許應用程式無需用戶許可也可新增捷徑。" + "允許應用程式無需使用者許可也可新增捷徑。" "解除安裝捷徑" - "允許應用程式無需用戶許可也可移除捷徑。" + "允許應用程式無需使用者許可也可移除捷徑。" "讀取主畫面的設定和捷徑" "允許應用程式讀取主畫面中的設定和捷徑。" "寫入主畫面的設定和捷徑" -- cgit v1.2.3 From 4ed56a25ea23c0a36cc6f05c9880fff86e3e18d5 Mon Sep 17 00:00:00 2001 From: Sameer Padala Date: Tue, 23 Sep 2014 18:04:38 -0700 Subject: Prevent the inlining of ProviderConfig.AUTHORITY This intern() statement prevents AUTHORITY from being inlined into LauncherProvider.AUTHORITY and LauncherSettings.Favorites.CONTENT_URI. If this string is inlined, then it can't be overridded by libraries that depend on it. Change-Id: I588b394291b70a01e3008c908a6ccb9b6cdfb6b9 --- src/com/android/launcher3/config/ProviderConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/config/ProviderConfig.java b/src/com/android/launcher3/config/ProviderConfig.java index db25076ea..e8930d063 100644 --- a/src/com/android/launcher3/config/ProviderConfig.java +++ b/src/com/android/launcher3/config/ProviderConfig.java @@ -18,5 +18,5 @@ package com.android.launcher3.config; public class ProviderConfig { - public static final String AUTHORITY = "com.android.launcher3.settings"; + public static final String AUTHORITY = "com.android.launcher3.settings".intern(); } -- cgit v1.2.3 From 90b43ad3434402990e7fcf5e417bd4bfbff1d75a Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 13 Oct 2014 06:08:16 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I9c6ef0fac498e06571d99b3117509d1fdeddd792 Auto-generated-cl: translation import --- res/values-af/strings.xml | 1 + res/values-am/strings.xml | 1 + res/values-ar/strings.xml | 1 + res/values-bg/strings.xml | 1 + res/values-ca/strings.xml | 1 + res/values-cs/strings.xml | 1 + res/values-da/strings.xml | 3 ++- res/values-de/strings.xml | 1 + res/values-el/strings.xml | 1 + res/values-en-rGB/strings.xml | 1 + res/values-en-rIN/strings.xml | 1 + res/values-es-rUS/strings.xml | 1 + res/values-es/strings.xml | 1 + res/values-et-rEE/strings.xml | 1 + res/values-fa/strings.xml | 1 + res/values-fi/strings.xml | 1 + res/values-fr-rCA/strings.xml | 1 + res/values-fr/strings.xml | 1 + res/values-hi/strings.xml | 1 + res/values-hr/strings.xml | 1 + res/values-hu/strings.xml | 1 + res/values-hy-rAM/strings.xml | 1 + res/values-in/strings.xml | 1 + res/values-it/strings.xml | 1 + res/values-iw/strings.xml | 1 + res/values-ja/strings.xml | 1 + res/values-ka-rGE/strings.xml | 1 + res/values-km-rKH/strings.xml | 1 + res/values-ko/strings.xml | 1 + res/values-lo-rLA/strings.xml | 1 + res/values-lt/strings.xml | 1 + res/values-lv/strings.xml | 1 + res/values-mn-rMN/strings.xml | 1 + res/values-ms-rMY/strings.xml | 2 ++ res/values-nb/strings.xml | 1 + res/values-nl/strings.xml | 1 + res/values-pl/strings.xml | 1 + res/values-pt-rPT/strings.xml | 1 + res/values-pt/strings.xml | 43 ++++++++++++++++++++++--------------------- res/values-ro/strings.xml | 1 + res/values-ru/strings.xml | 1 + res/values-sk/strings.xml | 3 ++- res/values-sl/strings.xml | 1 + res/values-sr/strings.xml | 1 + res/values-sv/strings.xml | 1 + res/values-sw/strings.xml | 1 + res/values-th/strings.xml | 1 + res/values-tl/strings.xml | 1 + res/values-tr/strings.xml | 1 + res/values-uk/strings.xml | 1 + res/values-vi/strings.xml | 1 + res/values-zh-rCN/strings.xml | 1 + res/values-zh-rHK/strings.xml | 1 + res/values-zh-rTW/strings.xml | 1 + res/values-zu/strings.xml | 1 + 55 files changed, 79 insertions(+), 23 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 4a4bb78a2..1c75b5a97 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -24,6 +24,7 @@ "Android-kernprogramme" "Program is nie geïnstalleer nie." + "Program is nie beskikbaar nie" "Afgelaaide program in veiligmodus gedeaktiveer" "Legstukke" "Legstukke" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 25931db7d..5938bf06e 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -24,6 +24,7 @@ "Android ዋና መተግበሪያዎች" "መተግበሪያ አልተጫነም።" + "መተግበሪያ አይገኝም" "የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል" "ፍርግሞች" "ፍርግሞች" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 8b6aa025c..41184a5f4 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -24,6 +24,7 @@ "‏تطبيقات Android الأساسية" "لم يتم تثبيت التطبيق." + "التطبيق ليس متاحًا" "تم تعطيل التطبيق الذي تم تنزيله في الوضع الآمن" "الأدوات" "الأدوات" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index dcd193050..05c5a7dc5 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -24,6 +24,7 @@ "Основни приложения на Android" "Приложението не е инсталирано." + "Приложението не е налично" "Изтегленото приложение е деактивирано в безопасния режим" "Приспособления" "Приспособления" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 6d1023545..616a3160b 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -24,6 +24,7 @@ "Aplicacions principals d\'Android" "L\'aplicació no s\'ha instal·lat." + "L\'aplicació no està disponible." "L\'aplicació que has baixat està desactivada al mode segur." "Widgets" "Widgets" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index f729a460e..9bce2e3ae 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "Aplikace není nainstalována." + "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" "Widgety" "Widgety" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 85159b2e1..216a01dd7 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -24,6 +24,7 @@ "Kerneapps i Android" "Appen er ikke installeret." + "Appen er ikke tilgængelig" "Downloadet app er deaktiveret i sikker tilstand" "Widgets" "Widgets" @@ -58,7 +59,7 @@ "Afinstaller" "Oplysninger om appen" "Søg" - "Stemmesøgning" + "Talesøgning" "Apps" "Fjern" "Afinstaller opdatering" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 698a25413..eda4920c8 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "App ist nicht installiert." + "App nicht verfügbar" "Heruntergeladene App im abgesicherten Modus deaktiviert" "Widgets" "Widgets" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 3eec27d4f..4567ea6a9 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -24,6 +24,7 @@ "Βασικές εφαρμογές Android" "Η εφαρμογή δεν έχει εγκατασταθεί." + "Η εφαρμογή δεν είναι διαθέσιμη" "Η λήψη εφαρμογών απενεργοποήθηκε στην Ασφαλή λειτουργία" "Γραφικά στοιχεία" "Γραφικά στοιχεία" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 615e5c90d..bf10a22c5 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "App isn\'t installed." + "App isn\'t available" "Downloaded app disabled in Safe mode" "Widgets" "Widgets" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 615e5c90d..bf10a22c5 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "App isn\'t installed." + "App isn\'t available" "Downloaded app disabled in Safe mode" "Widgets" "Widgets" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 31afa3e52..79eb9a19b 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -24,6 +24,7 @@ "Aplicaciones básicas de Android" "No se instaló la aplicación." + "La aplicación no está disponible." "Aplicación descargada inhabilitada en modo seguro" "Widgets" "Widgets" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 4e9141208..29144826f 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -24,6 +24,7 @@ "Aplicaciones básicas de Android" "La aplicación no está instalada." + "La aplicación no está disponible" "Aplicación descargada inhabilitada en modo seguro" "Widgets" "Widgets" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index fa352a1a3..8943b34d7 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -24,6 +24,7 @@ "Androidi tuumrakendused" "Rakendus pole installitud." + "Rakendus ei ole saadaval" "Allalaetud rakendus on turvarežiimis keelatud" "Vidinad" "Vidinad" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 13f40e223..6607843dd 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -24,6 +24,7 @@ "‏برنامه‌های Android Core" "برنامه نصب نشده است." + "برنامه در دسترس نیست" "برنامه دانلود شده در حالت ایمن غیرفعال شد" "ابزارک‌ها" "ابزارک‌ها" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 3990a163c..eccb06e1a 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -24,6 +24,7 @@ "Androidin ydinsovellukset" "Sovellusta ei ole asennettu." + "Sovellus ei ole käytettävissä" "Ladattu sovellus poistettiin käytöstä suojatussa tilassa" "Widgetit" "Widgetit" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index f42835014..aac11ed92 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -24,6 +24,7 @@ "Applications de base Android" "L\'application n\'est pas installée." + "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." "Widgets" "Widgets" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 31448f18b..b2eee9955 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -24,6 +24,7 @@ "Applications de base Android" "L\'application n\'est pas installée." + "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." "Widgets" "Widgets" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 0ae90d469..ac0681fab 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -24,6 +24,7 @@ "Android के मुख्य ऐप्लिकेशन" "एप्‍लिकेशन इंस्‍टॉल नहीं है." + "ऐप्स उपलब्ध नहीं है" "डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है" "विजेट" "विजेट" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index c881077b1..6e69684d9 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -24,6 +24,7 @@ "Matične aplikacije za Android" "Aplikacija nije instalirana." + "Aplikacija nije dostupna" "Preuzeta aplikacija onemogućena je u Sigurnom načinu rada" "Widgeti" "Widgeti" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 08d309558..1c11865fe 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -24,6 +24,7 @@ "Alap Android-alkalmazások" "Az alkalmazás nincs telepítve." + "Az alkalmazás nem érhető el" "A letöltött alkalmazás Csökkentett módban ki van kapcsolva" "Modulok" "Modulok" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 4ec39c88c..be5568c35 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "Ծրագիրը տեղադրված չէ:" + "Հավելվածը հասանելի չէ" "Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում" "Վիջեթներ" "Վիջեթներ" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 137e3cc7d..0aadeb340 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -24,6 +24,7 @@ "Aplikasi Inti Android" "Aplikasi tidak dipasang." + "Aplikasi tidak tersedia" "Aplikasi yang diunduh dinonaktifkan dalam mode Aman" "Widget" "Widget" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index b01b25174..77242dd57 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -24,6 +24,7 @@ "Applicazioni di base Android" "App non installata." + "App non disponibile" "L\'app scaricata è stata disattivata in modalità provvisoria" "Widget" "Widget" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 6318207ff..02012ce42 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -24,6 +24,7 @@ "‏אפליקציות הליבה של Android" "האפליקציה לא מותקנת." + "האפליקציה אינה זמינה" "אפליקציה שהורדת הושבתה במצב בטוח" "רכיבי ווידג\'ט" "רכיבי ווידג\'ט" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 232845af0..3ce781b8e 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "このアプリはインストールされていません。" + "このアプリは使用できません" "ダウンロードしたアプリは、セーフモードでは無効です" "ウィジェット" "ウィジェット" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 2fb51f541..31cdb40a5 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -24,6 +24,7 @@ "Android-ის ბირთვის აპები" "აპი არ არის დაყენებული." + "აპი მიუწვდომელია" "უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია" "ვიჯეტები" "ვიჯეტები" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index bcd606000..19dd31e58 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -24,6 +24,7 @@ "កម្មវិធី​​សំខាន់​ៗ​របស់ Android" "មិន​បាន​ដំឡើង​កម្មវិធី។" + "មិន​មាន​កម្មវិធី" "បាន​បិទ​កម្មវិធី​ដែល​បាន​ទាញ​យក​ក្នុង​របៀប​សុវត្ថិភាព" "ធាតុ​ក្រាហ្វិក" "ធាតុ​ក្រាហ្វិក" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 41c854ec3..88ea54e53 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -24,6 +24,7 @@ "Android 핵심 앱" "앱이 설치되지 않았습니다." + "앱을 사용할 수 없음" "다운로드한 앱은 안전 모드에서 사용할 수 없습니다." "위젯" "위젯" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1d953ffde..b30a69d1b 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -24,6 +24,7 @@ "ແອັບພລິເຄຊັນຫຼັກຂອງ Android" "ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ." + "ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້" "ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode" "ວິດເຈັດ" "ວິດເຈັດ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index f7db79291..72b029c8e 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -24,6 +24,7 @@ "Pagrindinės „Android“ programos" "Programa neįdiegta." + "Programa nepasiekiama" "Atsisiųsta programa išjungta Saugos režimu" "Valdikliai" "Valdikliai" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 75eb0548b..280bc0c40 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -24,6 +24,7 @@ "Android pamatlietotnes" "Lietotne nav instalēta." + "Lietotne nav pieejama." "Lejupielādētā lietotne ir atspējota drošajā režīmā." "Logrīki" "Logrīki" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 34fb79423..816803af0 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -24,6 +24,7 @@ "Андройд үндсэн апп" "Апп суугаагүй байна." + "Апп-г ашиглах боломжгүй" "Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн" "Виджет" "Виджет" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 3c5876268..07236d658 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -24,6 +24,8 @@ "Apl Teras Android" "Apl tidak dipasang." + + "Apl yang dimuat turun dilumpuhkan dalam mod Selamat" "Widget" "Widget" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index ff8280e73..13d8843a7 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -24,6 +24,7 @@ "Kjerneapper for Android" "Appen er ikke installert." + "Appen er ikke tilgjengelig" "En nedlastet app er deaktivert i sikker modus" "Moduler" "Moduler" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 470eb8722..c9f7e6714 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -24,6 +24,7 @@ "Android-kernapps" "App is niet geïnstalleerd." + "App is niet beschikbaar" "Gedownloade app uitgeschakeld in veilige modus" "Widgets" "Widgets" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 61a51e0a5..44e46e034 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -24,6 +24,7 @@ "Główne aplikacje Androida" "Aplikacja nie jest zainstalowana." + "Aplikacja niedostępna" "Pobrana aplikacja została wyłączona w trybie awaryjnym" "Widżety" "Widżety" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 2fe062a4a..fb3367803 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -24,6 +24,7 @@ "Aplicações principais do Android" "A aplicação não está instalada." + "A aplicação não está disponível" "Aplicação transferida desativada no Modo de segurança" "Widgets" "Widgets" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 20b193aa3..2f86d0bbe 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -21,9 +21,10 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Início" - "Principais aplicativos do Android" + "Principais apps do Android" - "O aplicativo não está instalado." + "O app não está instalado." + "O app não está disponível" "App transferido por download desativado no modo de segurança" "Widgets" "Widgets" @@ -38,7 +39,7 @@ "Ok" "Cancelar" "Adicionar à tela inicial" - "Aplicativos" + "Apps" "Atalhos" "Widgets" "Não há mais espaço nas telas iniciais." @@ -49,49 +50,49 @@ "O atalho \"%s\" foi removido." "O atalho \"%s\" já existe." "Selecione um atalho" - "Selecione um aplicativo" - "Aplicativos" + "Selecione um app" + "Apps" "Início" "Remover" "Desinstalar" "Remover" "Desinstalar" - "Informações do aplicativo" + "Informações do app" "Pesquisar" "Pesquisa por voz" - "Aplicativos" + "Apps" "Remover" "Desinstalar atualização" - "Desinstalar aplicativo" - "Detalhes do aplicativo" - "Um aplicativo selecionado" + "Desinstalar app" + "Detalhes do app" + "Um app selecionado" "Um widget selecionado" "Uma pasta selecionada" "Um atalho selecionado" "instalar atalhos" - "Permite que um aplicativo adicione atalhos sem intervenção do usuário." + "Permite que um app adicione atalhos sem intervenção do usuário." "desinstalar atalhos" - "Permite que o aplicativo remova atalhos sem a intervenção do usuário." + "Permite que o app remova atalhos sem a intervenção do usuário." "ler configurações e atalhos da tela inicial" - "Permite que o aplicativo leia as configurações e os atalhos na tela inicial." + "Permite que o app leia as configurações e os atalhos na tela inicial." "gravar configurações e atalhos da tela inicial" - "Permite que o aplicativo altere as configurações e os atalhos na tela inicial." + "Permite que o app altere as configurações e os atalhos na tela inicial." "Problema ao carregar o widget" "Configuração" - "Este é um aplicativo do sistema e não pode ser desinstalado." + "Este é um app do sistema e não pode ser desinstalado." "Rocket Launcher" "Pasta sem nome" "Tela inicial %1$d" "Página %1$d de %2$d" "Tela inicial %1$d de %2$d" - "Página de aplicativos, %1$d de %2$d" + "Página de apps, %1$d de %2$d" "Página de widgets, %1$d de %2$d" "Bem-vindo" "Fique à vontade." - "Crie mais telas para aplicativos e pastas" - "Copiar ícones de aplicativos" + "Crie mais telas para apps e pastas" + "Copiar ícones de apps" "Importar ícones e pastas de suas telas iniciais antigas?" "COPIAR ÍCONES" "COMEÇAR DO ZERO" @@ -101,7 +102,7 @@ "Toque e mantenha pressionado o segundo plano para personalizar" "ENTENDI" "Aqui está uma pasta" - "Para criar uma pasta como esta, mantenha pressionado um aplicativo e mova-o para cima de outro." + "Para criar uma pasta como esta, mantenha pressionado um app e mova-o para cima de outro." "Ok" "Pasta aberta, %1$d por %2$d" "Toque para fechar a pasta" @@ -120,6 +121,6 @@ "Remover tudo" "Remover" "Pesquisar" - "Este aplicativo não está instalado" - "O aplicativo deste ícone não está instalado. Você pode remover o ícone, ou procurar o aplicativo e instalá-lo manualmente." + "Este app não está instalado" + "O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente." diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 04aebc8b9..c6000ee2b 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "Aplicația nu este instalată." + "Aplicația nu este disponibilă" "Aplicația descărcată este dezactivată în modul de siguranță" "Widgeturi" "Widgeturi" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index b1d7713a0..134582d9f 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -24,6 +24,7 @@ "Основные приложения Android" "Приложение удалено" + "Приложение недоступно" "Скачанное приложение отключено в безопасном режиме" "Виджеты" "Виджеты" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index a00367949..d80b5ac41 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "Aplikácia nie je nainštalovaná." + "Aplikácia nie je k dispozícii" "Stiahnutá aplikácia je v núdzovom režime zakázaná" "Miniaplikácie" "Miniaplikácie" @@ -86,7 +87,7 @@ "Plocha %1$d z %2$d" "Stránka aplikácií %1$d z %2$d" "Stránka miniaplikácií %1$d z %2$d" - "Vitajte" + "Vitajte!" "Cíťte sa tu ako doma." diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 9c5bebd15..882ddb4b5 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -24,6 +24,7 @@ "Osnovne aplikacije sistema Android" "Aplikacija ni nameščena." + "Aplikacija ni na voljo" "Prenesena aplikacija je onemogočena v Varnem načinu" "Pripomočki" "Pripomočki" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 421f8d3ac..63f48f512 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -24,6 +24,7 @@ "Основне Android апликације" "Апликација није инсталирана." + "Апликација није доступна" "Преузета апликација је онемогућена у Безбедном режиму" "Виџети" "Виџети" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index e149c9efa..674f1dff2 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "Appen är inte installerad." + "Appen är inte tillgänglig" "Den hämtade appen inaktiverades i säkert läge" "Widgetar" "Widgetar" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 07d091393..94f53cebe 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -24,6 +24,7 @@ "Programu Msingi za Android" "Programu haijasakinishwa." + "Programu haipatikani" "Programu iliyopakuliwa imezimwa katika Hali Salama" "Wijeti" "Wijeti" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index beed89844..06ed6ec92 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -24,6 +24,7 @@ "แอปหลักของ Android" "ไม่ได้ติดตั้งแอป" + "แอปไม่พร้อมใช้งาน" "แอปที่ดาวน์โหลดถูกปิดในโหมดปลอดภัย" "วิดเจ็ต" "วิดเจ็ต" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 7c6acd2ab..3c877d074 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -24,6 +24,7 @@ "Android Core Apps" "Hindi naka-install ang app." + "Hindi available ang app" "Naka-disable ang na-download na app sa Safe mode" "Mga Widget" "Mga Widget" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 6c0bb69a7..2f67ad25d 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -24,6 +24,7 @@ "Android Çekirdek Uygulamaları" "Uygulama yüklü değil." + "Uygulama kullanılamıyor" "İndirilen uygulama Güvenli modda devre dışı bırakıldı" "Widget\'lar" "Widget\'lar" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index e5b5cd8d2..12c9dd49c 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -24,6 +24,7 @@ "Базові програми Android" "Додаток видалено." + "Додаток недоступний" "Завантажений додаток вимкнено в безпечному режимі" "Віджети" "Віджети" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 489123089..79db2c5e5 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -24,6 +24,7 @@ "Ứng dụng lõi Android" "Ứng dụng chưa được cài đặt." + "Ứng dụng không có sẵn" "Ứng dụng đã tải xuống bị tắt ở chế độ An toàn" "Tiện ích con" "Tiện ích con" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 3365581be..37923dd2a 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -24,6 +24,7 @@ "Android 核心应用" "未安装该应用。" + "应用不可用" "安全模式下不允许使用下载的此应用" "小部件" "小部件" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 457d5be9e..346e60a02 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -24,6 +24,7 @@ "Android 核心應用程式" "尚未安裝應用程式。" + "目前無法使用這個應用程式" "在安全模式中無法使用「已下載的應用程式」功能" "小工具" "小工具" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 2d400596f..a63fb69ef 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -24,6 +24,7 @@ "Android 核心應用程式" "應用程式未安裝。" + "應用程式目前無法使用" "在安全模式中無法使用「已下載的應用程式」功能" "小工具" "小工具" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 89cd5cc12..f94b166e0 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -24,6 +24,7 @@ "Izinhlelo zokusebenza ze-Android Core" "Uhlelo lokusebenza alufakiwe." + "Uhlelo lokusebenza alutholakali" "Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile" "Amawijethi" "Amawijethi" -- cgit v1.2.3 From e4f2fa7e111d567087a296ee3dc166cc2b74f5af Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 15 Oct 2014 10:45:24 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I90af76842559cbe6a1a9629b9291eac0ea62db27 Auto-generated-cl: translation import --- res/values-ms-rMY/strings.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 07236d658..32c8c0296 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -24,8 +24,7 @@ "Apl Teras Android" "Apl tidak dipasang." - - + "Apl tidak tersedia" "Apl yang dimuat turun dilumpuhkan dalam mod Selamat" "Widget" "Widget" -- cgit v1.2.3 From 9a8573250bbe0df0af0d275cb188bf9db7942e6f Mon Sep 17 00:00:00 2001 From: Cameron Neale Date: Thu, 16 Oct 2014 13:29:12 -0700 Subject: Removing duplicated and/or unused resources from WallpaperPicker that conflict with Launcher 3 resources. The resource duplicated between the two is now only held within Launcher3. These changes are necessary in order for GSA to build in Google3. Change-Id: I70f5a250db52303c876e6abedd9c6d32d1d4761e --- WallpaperPicker/res/values-sw600dp/config.xml | 18 ------------------ WallpaperPicker/res/values-sw720dp/dimens.xml | 5 ----- WallpaperPicker/res/values/config.xml | 1 - 3 files changed, 24 deletions(-) delete mode 100644 WallpaperPicker/res/values-sw600dp/config.xml diff --git a/WallpaperPicker/res/values-sw600dp/config.xml b/WallpaperPicker/res/values-sw600dp/config.xml deleted file mode 100644 index 62342dcc2..000000000 --- a/WallpaperPicker/res/values-sw600dp/config.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - true - diff --git a/WallpaperPicker/res/values-sw720dp/dimens.xml b/WallpaperPicker/res/values-sw720dp/dimens.xml index 9ae155b3c..27c4fd054 100644 --- a/WallpaperPicker/res/values-sw720dp/dimens.xml +++ b/WallpaperPicker/res/values-sw720dp/dimens.xml @@ -15,12 +15,7 @@ --> - 72dp - - 8dip - 8dip - 0dp diff --git a/WallpaperPicker/res/values/config.xml b/WallpaperPicker/res/values/config.xml index 71580b5b0..2f5174ce6 100644 --- a/WallpaperPicker/res/values/config.xml +++ b/WallpaperPicker/res/values/config.xml @@ -14,7 +14,6 @@ limitations under the License. --> - false false -- cgit v1.2.3 From 017c22dbb5cdd2ab9667d769c546b368be0215b0 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 20 Oct 2014 16:46:20 -0700 Subject: Import translations. DO NOT MERGE Change-Id: Idc5745455ed93fc24d00f55ef8912b58ccb7168c Auto-generated-cl: translation import --- res/values-af/strings.xml | 1 - res/values-am/strings.xml | 1 - res/values-ar/strings.xml | 1 - res/values-bg/strings.xml | 1 - res/values-ca/strings.xml | 1 - res/values-cs/strings.xml | 1 - res/values-da/strings.xml | 1 - res/values-de/strings.xml | 1 - res/values-el/strings.xml | 1 - res/values-en-rGB/strings.xml | 1 - res/values-en-rIN/strings.xml | 1 - res/values-es-rUS/strings.xml | 1 - res/values-es/strings.xml | 1 - res/values-et-rEE/strings.xml | 1 - res/values-fa/strings.xml | 1 - res/values-fi/strings.xml | 1 - res/values-fr-rCA/strings.xml | 1 - res/values-fr/strings.xml | 1 - res/values-hi/strings.xml | 15 +++++++-------- res/values-hr/strings.xml | 1 - res/values-hu/strings.xml | 1 - res/values-hy-rAM/strings.xml | 1 - res/values-in/strings.xml | 1 - res/values-it/strings.xml | 1 - res/values-iw/strings.xml | 1 - res/values-ja/strings.xml | 1 - res/values-ka-rGE/strings.xml | 1 - res/values-km-rKH/strings.xml | 1 - res/values-ko/strings.xml | 1 - res/values-lo-rLA/strings.xml | 1 - res/values-lt/strings.xml | 1 - res/values-lv/strings.xml | 1 - res/values-mn-rMN/strings.xml | 1 - res/values-ms-rMY/strings.xml | 1 - res/values-nb/strings.xml | 1 - res/values-nl/strings.xml | 1 - res/values-pl/strings.xml | 1 - res/values-pt-rPT/strings.xml | 1 - res/values-pt/strings.xml | 1 - res/values-ro/strings.xml | 1 - res/values-ru/strings.xml | 1 - res/values-sk/strings.xml | 1 - res/values-sl/strings.xml | 1 - res/values-sr/strings.xml | 1 - res/values-sv/strings.xml | 1 - res/values-sw/strings.xml | 1 - res/values-th/strings.xml | 1 - res/values-tl/strings.xml | 1 - res/values-tr/strings.xml | 1 - res/values-uk/strings.xml | 1 - res/values-vi/strings.xml | 1 - res/values-zh-rCN/strings.xml | 1 - res/values-zh-rHK/strings.xml | 1 - res/values-zh-rTW/strings.xml | 1 - res/values-zu/strings.xml | 1 - 55 files changed, 7 insertions(+), 62 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 1c75b5a97..9207dde7b 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -30,7 +30,6 @@ "Legstukke" "Wys Mem" "Raak en hou om \'n legstuk op te tel." - "Winkel" "%1$d × %2$d" "Kan nie item op hierdie Tuisskerm laat los nie." "Kies legstuk om te skep" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 5938bf06e..82765c231 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -30,7 +30,6 @@ "ፍርግሞች" "ማህደረ ማስታወሻ አሳይ" "ፍርግም ለማንሳት ይንኩ እና ይያዙት" - "ግዛ" "%1$d × %2$d" "ንጥሉን እዚህ የመነሻ ማያ ገጽ ላይ ማኖር አልተቻለም።" "ለመፍጠር መግብር ይምረጡ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 41184a5f4..5d4ae420e 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -30,7 +30,6 @@ "الأدوات" "عرض الذاكرة" "المس مع الاستمرار لاختيار إحدى الأدوات." - "تسوق" "%1$d × %2$d" "تعذر إسقاط العنصر على هذه الشاشة الرئيسية." "اختيار أداة لإنشائها" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 05c5a7dc5..1bdbb9c39 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -30,7 +30,6 @@ "Приспособления" "Показване на паметта" "Докоснете и задръжте за избор на приспособление." - "Пазаруване" "%1$d × %2$d" "Не можа да се премести на този начален екран." "Избор на приспособл. за създаване" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 616a3160b..9cb31d75b 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Mostra la memòria" "Mantén premut un widget per triar-lo." - "Compra" "%1$d × %2$d" "No s\'ha pogut deixar anar l\'element a Inici." "Tria el widget que vulguis crear" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 9bce2e3ae..f5a055496 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -30,7 +30,6 @@ "Widgety" "Zobrazit Mem" "Widget vyberete dotykem a podržením." - "Obchod" "%1$d × %2$d" "Na tuto plochu položku nelze přesunout." "Vyberte widget k vytvoření" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 216a01dd7..a9ded74ca 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Vis Mem" "Tryk på en widget, og hold den nede for at vælge." - "Køb i Google Play Butik" "%1$d × %2$d" "Elementet kunne ikke trækkes til startskærmen." "Vælg den widget, du vil oprette" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index eda4920c8..5bf1a7c00 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Speicher anzeigen" "Zum Hinzufügen Widget berühren und halten" - "Einkaufen" "%1$d × %2$d" "Element wurde nicht auf diesem Startbildschirm abgelegt." "Widget zum Erstellen auswählen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 4567ea6a9..49163379e 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -30,7 +30,6 @@ "Γραφικά στοιχεία" "Εμφάνιση Mem" "Αγγίξτε παρατεταμένα για να πάρετε ένα γραφ.στοιχ." - "Αγορά" "%1$d × %2$d" "Αδυναμία τοποθέτησης στοιχείου στην Αρχική οθόνη." "Επιλ. γραφ. στοιχείο για δημιουργία" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index bf10a22c5..53b4620bb 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Show Mem" "Touch & hold to pick up a widget." - "Shop" "%1$d × %2$d" "Couldn\'t drop item on this Home screen." "Choose widget to create" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index bf10a22c5..53b4620bb 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Show Mem" "Touch & hold to pick up a widget." - "Shop" "%1$d × %2$d" "Couldn\'t drop item on this Home screen." "Choose widget to create" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 79eb9a19b..dd5575b80 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Mostrar memoria" "Mantén presionado el widget que desees elegir." - "Comprar" "%1$d × %2$d" "Error al soltar elemento en la pantalla principal" "Elegir los widgets para crear" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 29144826f..e66b79e14 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Mostrar memoria" "Mantén pulsado el widget que quieras seleccionar." - "Tienda" "%1$d × %2$d" "Error al arrastrar elemento a pantalla de inicio." "Selecciona widget a añadir" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 8943b34d7..bd70aa4a7 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -30,7 +30,6 @@ "Vidinad" "Mälu kuvamine" "Vidina valimiseks vajutage ja hoidke seda all." - "Pood" "%1$d × %2$d" "Üksust ei saanud sellele avaekraanile tuua." "Valige loomiseks vidin" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 6607843dd..5b428ccfa 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -30,7 +30,6 @@ "ابزارک‌ها" "‏نمایش Mem" "برای انتخاب ابزارک لمس کنید و نگه دارید." - "فروشگاه" "%1$d × %2$d" "این مورد را نمی‌توان در این صفحه اصلی رها کرد." "انتخاب ابزارکی که باید ایجاد شود" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index eccb06e1a..63eb3f575 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -30,7 +30,6 @@ "Widgetit" "Näytä muisti" "Valitse widget painamalla sitä pitkään." - "Kauppa" "%1$d × %2$d" "Kohteen lisääminen tähän aloitusruutuun epäonnistui." "Valitse luotava widget" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index aac11ed92..fb78002b1 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Afficher la mémoire" "Maintenez un doigt sur le widget pour l\'ajouter." - "Magasiner" "%1$d × %2$d" "Imposs. de déposer l\'élément sur l\'écran d\'accueil" "Sélectionnez le widget à créer" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index b2eee9955..ce657b9cd 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Afficher la mémoire" "App. de manière prolongée pour sélectionner widget." - "Boutique" "%1$d x %2$d" "Impossible de déposer élément sur écran d\'accueil." "Sélectionner le widget à créer" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index ac0681fab..7bf9c85f8 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -26,11 +26,10 @@ "एप्‍लिकेशन इंस्‍टॉल नहीं है." "ऐप्स उपलब्ध नहीं है" "डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है" - "विजेट" - "विजेट" + "शॉर्टकट" + "शॉर्टकट" "मेमोरी दिखाएं" "विजेट को चुनने के लिए स्‍पर्श करके रखें." - "खरीदारी करें" "%1$d × %2$d" "आइटम को इस होम स्‍क्रीन पर नहीं छोड़ा जा सका." "बनाने के लिए विजेट चुनें" @@ -41,7 +40,7 @@ "होम स्‍क्रीन में जोड़ें" "ऐप्लिकेशन" "शॉर्टकट" - "विजेट" + "शॉर्टकट" "आपकी होम स्‍क्रीन पर स्थान शेष नहीं है." "इस होम स्‍क्रीन पर स्थान शेष नहीं है." "पसंदीदा ट्रे में और स्थान नहीं है" @@ -86,7 +85,7 @@ "पृष्ठ %2$d में से %1$d" "होम स्क्रीन %2$d में से %1$d" "ऐप्लिकेशन पृष्ठ %2$d में से %1$d" - "विजेट पृष्ठ %2$d में से %1$d" + "शॉर्टकट %2$d में से %1$d" "स्वागत है" "जैसा चाहें वैसा उपयोग करें." @@ -97,8 +96,8 @@ "आइकन की प्रतिलिपि बनाएं" "फिर से शुरू करें" "अपने स्थान को व्यवस्थित करें" - "वॉलपेपर, विजेट और सेटिंग प्रबंधित करने के लिए पृष्ठभूमि को स्पर्श करके रखें." - "वॉलपेपर, विजेट और सेटिंग" + "वॉलपेपर, शॉर्टकट और सेटिंग प्रबंधित करने के लिए पृष्ठभूमि को स्पर्श करके रखें." + "वॉलपेपर, शॉर्टकट और सेटिंग" "पृष्ठभूमि कस्टमाइज़ करने के लिए स्पर्श करके रखें" "समझ लिया" "यहां एक फ़ोल्डर है" @@ -110,7 +109,7 @@ "फ़ोल्डर बंद किया गया" "फ़ोल्डर का नाम बदलकर %1$s किया गया" "फ़ोल्डर: %1$s" - "विजेट" + "शॉर्टकट" "वॉलपेपर" "सेटिंग" "प्रतीक्षा में" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 6e69684d9..62c19165e 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -30,7 +30,6 @@ "Widgeti" "Prikaži mem" "Dodirnite i držite kako biste podigli widget." - "Kupi" "%1$d × %2$d" "Stavka nije ispuštena na ovaj početni zaslon." "Odabir widgeta za stvaranje" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 1c11865fe..0dcd5df2b 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -30,7 +30,6 @@ "Modulok" "Mem. megjelenítése" "Modul felvételéhez érintse meg, és tartsa lenyomva" - "Vásárlás" "%1$d × %2$d" "Nem lehet elemeket dobni erre a kezdőképernyőre." "A létrehozáshoz válasszon modult" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index be5568c35..bae306d56 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -30,7 +30,6 @@ "Վիջեթներ" "Ցուցադրել մեմը" "Հպեք և պահեք՝ վիջեթն ընտրելու համար:" - "Խանութ" "%1$d × %2$d" "Հնարավոր չէ տեղադրել տարրն այս հիմնական էկրանին:" "Ստեղծելու համար ընտրեք վիջեթը" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 0aadeb340..b8f6d9642 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -30,7 +30,6 @@ "Widget" "Tampilkan Memori" "Sentuh lama untuk memilih widget." - "Belanja" "%1$d × %2$d" "Tidak dapat melepas item ke layar Utama ini." "Pilih widget untuk membuat" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 77242dd57..7cd03a8e0 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -30,7 +30,6 @@ "Widget" "Mostra Mem" "Tocca e tieni premuto per scegliere un widget." - "Acquista" "%1$d × %2$d" "Rilascio elemento in schermata Home non riuscito." "Scegli il widget da creare" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 02012ce42..44dfac752 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -30,7 +30,6 @@ "רכיבי ווידג\'ט" "הצג זכרון" "גע נגיעה רציפה בווידג\'ט כדי לבחור בו." - "קנה" "%1$d × %2$d" "לא ניתן היה לשחרר את הפריט במסך דף הבית הזה." "בחר ווידג\'ט ליצירה" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 3ce781b8e..e52a4ab05 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -30,7 +30,6 @@ "ウィジェット" "メモリーを表示" "ウィジェットを追加するには押し続けます。" - "ショップ" "%1$dx%2$d" "このホーム画面にアイテムをドロップできませんでした" "作成するウィジェットの選択" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 31cdb40a5..5ac936c1c 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -30,7 +30,6 @@ "ვიჯეტები" "Mem-ის ჩვენება" "შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად." - "მაღაზია" "%1$d × %2$d" "ერთეულის მთავარ ეკრანზე ჩაგდება ვერ მოხერხდა." "აირჩიეთ ვიჯეტი შესაქმნელად" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 19dd31e58..0aec2b02d 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -30,7 +30,6 @@ "ធាតុ​ក្រាហ្វិក" "បង្ហាញ​ Mem" "ប៉ះ & សង្កត់ ដើម្បី​ជ្រើស​ធាតុ​ក្រាហ្វិក។" - "ហាងទំនិញ" "%1$d × %2$d" "មិន​អាច​ទម្លាក់​ធាតុ​លើ​អេក្រង់​ដើម​នេះ​ទេ" "ជ្រើស​ធាតុ​ក្រាហ្វិក ដើម្បី​​​បង្កើត" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 88ea54e53..88c4dedea 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -30,7 +30,6 @@ "위젯" "메모리 표시" "위젯을 선택하려면 길게 터치하세요." - "쇼핑하기" "%1$d×%2$d" "홈 화면에 항목을 놓을 수 없습니다." "만들 위젯 선택" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index b30a69d1b..41e41cef1 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -30,7 +30,6 @@ "ວິດເຈັດ" "ສະແດງຄວາມຈຳ" "ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ." - "ຮ້ານຄ້າ" "%1$d × %2$d" "ບໍ່ສາມາດວາງລາຍການໃສ່ໜ້າຈໍຫຼັກນີ້ໄດ້" "ເລືອກວິດເຈັດເພື່ອສ້າງມັນ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 72b029c8e..145b70018 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -30,7 +30,6 @@ "Valdikliai" "Rodyti atmintinę" "Palieskite ir laikykite, kad pasirinkt. valdiklį." - "Apsipirkti" "%1$d × %2$d" "Nepavyko nuvilkti elemento į šį pagrindinį ekraną." "Pasirinkite norimą kurti valdiklį" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 280bc0c40..af591e012 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -30,7 +30,6 @@ "Logrīki" "Rādīt atmiņu" "Lai izvēlētos logrīku, pieskarieties un turiet to." - "Iepirkties" "%1$d × %2$d" "Nevarēja nomest vienumu šajā sākuma ekrānā." "Izveidojamā logrīka izvēle" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 816803af0..faa52764d 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -30,7 +30,6 @@ "Виджет" "Мем харуулах" "Виджетийг авах бол хүрээд барина уу." - "Дэлгүүр" "%1$d × %2$d" "Энэ Нүүр дэлгэцэнд буулгах боломжгүй." "Үүсгэх виджетээ сонгоно уу" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 32c8c0296..d80feb9c3 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -30,7 +30,6 @@ "Widget" "Papar Mem" "Sentuh & tahan untuk mengambil widget." - "Beli-belah" "%1$d × %2$d" "Tidak dapat melepaskan item pada Skrin Utama." "Pilih widget yang hendak dibuat" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 13d8843a7..d0580a7bf 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -30,7 +30,6 @@ "Moduler" "Vis minne" "Trykk og hold inne for å plukke opp en modul." - "Butikk" "%1$d × %2$d" "Kunne ikke slippe elementet på denne startsiden." "Velg modul for oppretting" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index c9f7e6714..be588b211 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Geheugen weergeven" "Blijf aanraken om een widget toe te voegen." - "Winkelen" "%1$d × %2$d" "Kan item niet neerzetten in dit startscherm." "Widget selecteren om te maken" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 44e46e034..793a37190 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -30,7 +30,6 @@ "Widżety" "Pokaż pamięć" "Aby dodać widżet, kliknij go i przytrzymaj." - "Sklep" "%1$d × %2$d" "Nie można upuścić elementu na tym ekranie głównym." "Wybierz widżet, który chcesz dodać" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index fb3367803..11acb47db 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Mostrar mem" "Prima sem soltar para escolher um widget." - "Comprar" "%1$d × %2$d" "Não foi possível largar o item neste Ecrã Principal." "Escolher um widget para criar" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 2f86d0bbe..64b9bb12f 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -30,7 +30,6 @@ "Widgets" "Mostrar memória" "Toque e pressione para selecionar um widget." - "Comprar" "%1$d × %2$d" "Não foi possível soltar o item nesta tela inicial." "Selecione um widget para criar" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index c6000ee2b..4e36a66bc 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -30,7 +30,6 @@ "Widgeturi" "Afișați memoria" "Atingeți lung un widget pentru a-l alege." - "Cumpărați" "%1$d × %2$d" "Nu se poate plasa articolul pe ecranul de pornire." "Alegeți widgetul de creat" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 134582d9f..c0ee48cea 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -30,7 +30,6 @@ "Виджеты" "Сведения о памяти" "Чтобы выбрать виджет, нажмите на значок и удерживайте его." - "Google Play" "%1$d x %2$d" "Не удалось добавить элемент на главный экран" "Выберите виджет" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index d80b5ac41..e984278c3 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -30,7 +30,6 @@ "Miniaplikácie" "Zobraziť pamäť" "Miniaplikáciu pridáte stlačením a podržaním." - "Obchod" "%1$d × %2$d" "Položku sa nepodarilo presunúť na túto plochu." "Zvoľte miniaplikáciu na vytvorenie" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 882ddb4b5..804779166 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -30,7 +30,6 @@ "Pripomočki" "Pokaži pomnilnik" "Za izbiro pripomočka se ga dotaknite in pridržite." - "Nakup" "%1$d × %2$d" "Elementa ni mogoče spustiti na začetni zaslon." "Izberite pripomoček za ustvarjanje" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 63f48f512..31430fe4a 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -30,7 +30,6 @@ "Виџети" "Прикажи меморију" "Додирните и задржите да бисте изабрали виџет." - "Купујте" "%1$d×%2$d" "Није могуће отпустити ставку на почетни екран." "Избор виџета за прављење" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 674f1dff2..7f2af8a74 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -30,7 +30,6 @@ "Widgetar" "Visa Mem" "Tryck länge om du vill flytta en widget." - "Butik" "%1$d × %2$d" "Objektet kunde inte släppas på startskärmen." "Ange vilken widget du vill använda" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 94f53cebe..485157b6a 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -30,7 +30,6 @@ "Wijeti" "Onyesha Kumbukumbu" "Gusa na ushikilie ili kuteua wijeti." - "Nunua" "%1$d × %2$d" "Haikuweza kudondosha kipengee kwenye skrini hii ya Kwanza." "Chagua wijeti ili uunde" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 06ed6ec92..26c72f42c 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -30,7 +30,6 @@ "วิดเจ็ต" "แสดง Mem" "แตะค้างเพื่อรับวิดเจ็ต" - "เลือกซื้อ" "%1$d × %2$d" "ไม่สามารถวางรายการลงในหน้าจอหลักนี้" "เลือกวิดเจ็ตที่จะสร้าง" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 3c877d074..27481be93 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -30,7 +30,6 @@ "Mga Widget" "Ipakita ang Mem" "Pindutin nang matagal upang kumuha ng widget." - "Mamili" "%1$d × %2$d" "Hindi ma-drop ang item sa Home screen na ito." "Pumili ng widget na gagawin" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 2f67ad25d..3bea49669 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -30,7 +30,6 @@ "Widget\'lar" "Belleği Göster" "Widget seçmek için dokunun ve basılı tutun." - "Mağaza" "%1$d × %2$d" "Öğe bu Ana ekrana bırakılamadı." "Oluşturmak için widget seçin" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 12c9dd49c..86ca01b3c 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -30,7 +30,6 @@ "Віджети" "Показати пам’ять" "Натисніть і утримуйте, щоб вибрати віджет." - "Магазин" "%1$d × %2$d" "Не вдалося додати елемент на цей головний екран." "Вибрати віджет для створення" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 79db2c5e5..b8d006386 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -30,7 +30,6 @@ "Tiện ích con" "Hiển thị bộ nhớ" "Chạm và giữ để chọn tiện ích con." - "Mua" "%1$d × %2$d" "Không thể thả mục vào Màn hình chính này." "Chọn tiện ích con để tạo" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 37923dd2a..a0982359a 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -30,7 +30,6 @@ "小部件" "显示内存空间" "触摸并按住小部件即可选择。" - "商店" "%1$d × %2$d" "无法将相关内容拖放到此主屏幕上。" "选择要创建的小部件" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 346e60a02..796c72e53 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -30,7 +30,6 @@ "小工具" "顯示記憶體" "輕觸並按住小工具即可選取。" - "商店" "%1$d × %2$d" "無法將項目拖放至主畫面。" "選擇要建立的小工具" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index a63fb69ef..ff3fa8c18 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -30,7 +30,6 @@ "小工具" "顯示記憶體" "輕觸並按住小工具即可選取。" - "購物" "%1$d × %2$d" "無法將項目拖放至這個主螢幕上。" "選擇要建立的小工具" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index f94b166e0..891f6723a 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -30,7 +30,6 @@ "Amawijethi" "Bonisa i-Mem" "Thinta uphinde ubambe ukuze uphakamise iwijethi." - "Thenga" "%1$d × %2$d" "Ayikwazanga ukwehlisela into kulesi sikrini se-Ikhaya." "Khetha iwijethi ongayidala" -- cgit v1.2.3 From ed377249580968f76de7b1eff303cde77bfe6352 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 22 Oct 2014 13:01:39 -0700 Subject: Import translations. DO NOT MERGE Change-Id: Icff21d84b1dbb334bad3106c9e5e01168e3dca37 Auto-generated-cl: translation import --- res/values-af/strings.xml | 2 ++ res/values-am/strings.xml | 1 + res/values-ar/strings.xml | 1 + res/values-bg/strings.xml | 1 + res/values-ca/strings.xml | 1 + res/values-cs/strings.xml | 2 ++ res/values-da/strings.xml | 2 ++ res/values-de/strings.xml | 1 + res/values-el/strings.xml | 1 + res/values-en-rGB/strings.xml | 1 + res/values-en-rIN/strings.xml | 1 + res/values-es-rUS/strings.xml | 2 ++ res/values-es/strings.xml | 1 + res/values-et-rEE/strings.xml | 1 + res/values-fa/strings.xml | 2 ++ res/values-fi/strings.xml | 1 + res/values-fr-rCA/strings.xml | 2 ++ res/values-fr/strings.xml | 2 ++ res/values-hi/strings.xml | 1 + res/values-hr/strings.xml | 1 + res/values-hu/strings.xml | 1 + res/values-hy-rAM/strings.xml | 2 ++ res/values-in/strings.xml | 1 + res/values-it/strings.xml | 1 + res/values-iw/strings.xml | 1 + res/values-ja/strings.xml | 1 + res/values-ka-rGE/strings.xml | 2 ++ res/values-km-rKH/strings.xml | 1 + res/values-ko/strings.xml | 1 + res/values-lo-rLA/strings.xml | 2 ++ res/values-lt/strings.xml | 1 + res/values-lv/strings.xml | 1 + res/values-mn-rMN/strings.xml | 1 + res/values-ms-rMY/strings.xml | 2 ++ res/values-nb/strings.xml | 2 ++ res/values-nl/strings.xml | 1 + res/values-pl/strings.xml | 1 + res/values-pt-rPT/strings.xml | 1 + res/values-pt/strings.xml | 2 ++ res/values-ro/strings.xml | 1 + res/values-ru/strings.xml | 2 ++ res/values-sk/strings.xml | 2 ++ res/values-sl/strings.xml | 2 ++ res/values-sr/strings.xml | 1 + res/values-sv/strings.xml | 1 + res/values-sw/strings.xml | 2 ++ res/values-th/strings.xml | 1 + res/values-tl/strings.xml | 1 + res/values-tr/strings.xml | 2 ++ res/values-uk/strings.xml | 1 + res/values-vi/strings.xml | 1 + res/values-zh-rCN/strings.xml | 2 ++ res/values-zh-rHK/strings.xml | 1 + res/values-zh-rTW/strings.xml | 1 + res/values-zu/strings.xml | 1 + 55 files changed, 74 insertions(+) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 9207dde7b..8aa65a4fe 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -26,6 +26,8 @@ "Program is nie geïnstalleer nie." "Program is nie beskikbaar nie" "Afgelaaide program in veiligmodus gedeaktiveer" + + "Legstukke" "Legstukke" "Wys Mem" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 82765c231..032610965 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -26,6 +26,7 @@ "መተግበሪያ አልተጫነም።" "መተግበሪያ አይገኝም" "የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል" + "ምግብሮች በደህንነቱ የተጠበቀ ሁኔታ ተሰናክለዋል" "ፍርግሞች" "ፍርግሞች" "ማህደረ ማስታወሻ አሳይ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 5d4ae420e..919f0d020 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -26,6 +26,7 @@ "لم يتم تثبيت التطبيق." "التطبيق ليس متاحًا" "تم تعطيل التطبيق الذي تم تنزيله في الوضع الآمن" + "الأدوات معطلة في الوضع الآمن" "الأدوات" "الأدوات" "عرض الذاكرة" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 1bdbb9c39..6654f1dc0 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -26,6 +26,7 @@ "Приложението не е инсталирано." "Приложението не е налично" "Изтегленото приложение е деактивирано в безопасния режим" + "Приспособленията са деактивирани в безопасния режим" "Приспособления" "Приспособления" "Показване на паметта" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 9cb31d75b..019e82f88 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -26,6 +26,7 @@ "L\'aplicació no s\'ha instal·lat." "L\'aplicació no està disponible." "L\'aplicació que has baixat està desactivada al mode segur." + "En Mode segur, els widgets estan desactivats." "Widgets" "Widgets" "Mostra la memòria" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index f5a055496..4b05436d4 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -26,6 +26,8 @@ "Aplikace není nainstalována." "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" + + "Widgety" "Widgety" "Zobrazit Mem" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index a9ded74ca..d5352f20e 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -26,6 +26,8 @@ "Appen er ikke installeret." "Appen er ikke tilgængelig" "Downloadet app er deaktiveret i sikker tilstand" + + "Widgets" "Widgets" "Vis Mem" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 5bf1a7c00..7e4ae4773 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -26,6 +26,7 @@ "App ist nicht installiert." "App nicht verfügbar" "Heruntergeladene App im abgesicherten Modus deaktiviert" + "Widgets im abgesicherten Modus deaktiviert" "Widgets" "Widgets" "Speicher anzeigen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 49163379e..5a3633261 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -26,6 +26,7 @@ "Η εφαρμογή δεν έχει εγκατασταθεί." "Η εφαρμογή δεν είναι διαθέσιμη" "Η λήψη εφαρμογών απενεργοποήθηκε στην Ασφαλή λειτουργία" + "Τα γραφικά στοιχεία απενεργοποιήθηκαν στην ασφαλή λειτουργία" "Γραφικά στοιχεία" "Γραφικά στοιχεία" "Εμφάνιση Mem" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 53b4620bb..7e212224c 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -26,6 +26,7 @@ "App isn\'t installed." "App isn\'t available" "Downloaded app disabled in Safe mode" + "Widgets disabled in Safe mode" "Widgets" "Widgets" "Show Mem" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 53b4620bb..7e212224c 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -26,6 +26,7 @@ "App isn\'t installed." "App isn\'t available" "Downloaded app disabled in Safe mode" + "Widgets disabled in Safe mode" "Widgets" "Widgets" "Show Mem" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index dd5575b80..dc43f2d82 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -26,6 +26,8 @@ "No se instaló la aplicación." "La aplicación no está disponible." "Aplicación descargada inhabilitada en modo seguro" + + "Widgets" "Widgets" "Mostrar memoria" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index e66b79e14..67c283a02 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -26,6 +26,7 @@ "La aplicación no está instalada." "La aplicación no está disponible" "Aplicación descargada inhabilitada en modo seguro" + "Widgets inhabilitados en modo seguro" "Widgets" "Widgets" "Mostrar memoria" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index bd70aa4a7..1b9f1b793 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -26,6 +26,7 @@ "Rakendus pole installitud." "Rakendus ei ole saadaval" "Allalaetud rakendus on turvarežiimis keelatud" + "Turvarežiimis on vidinad keelatud" "Vidinad" "Vidinad" "Mälu kuvamine" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 5b428ccfa..c765d8fa0 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -26,6 +26,8 @@ "برنامه نصب نشده است." "برنامه در دسترس نیست" "برنامه دانلود شده در حالت ایمن غیرفعال شد" + + "ابزارک‌ها" "ابزارک‌ها" "‏نمایش Mem" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 63eb3f575..0004501c3 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -26,6 +26,7 @@ "Sovellusta ei ole asennettu." "Sovellus ei ole käytettävissä" "Ladattu sovellus poistettiin käytöstä suojatussa tilassa" + "Widgetit poistettu käytöstä vikasietotilassa" "Widgetit" "Widgetit" "Näytä muisti" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index fb78002b1..c1070f2ea 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -26,6 +26,8 @@ "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." + + "Widgets" "Widgets" "Afficher la mémoire" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index ce657b9cd..064d751ae 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -26,6 +26,8 @@ "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." + + "Widgets" "Widgets" "Afficher la mémoire" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 7bf9c85f8..f14eff7cf 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -26,6 +26,7 @@ "एप्‍लिकेशन इंस्‍टॉल नहीं है." "ऐप्स उपलब्ध नहीं है" "डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है" + "विजेट सुरक्षित मोड में अक्षम हैं" "शॉर्टकट" "शॉर्टकट" "मेमोरी दिखाएं" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 62c19165e..aa016484f 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -26,6 +26,7 @@ "Aplikacija nije instalirana." "Aplikacija nije dostupna" "Preuzeta aplikacija onemogućena je u Sigurnom načinu rada" + "Widgeti su onemogućeni u Sigurnom načinu rada" "Widgeti" "Widgeti" "Prikaži mem" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 0dcd5df2b..4416ab0aa 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -26,6 +26,7 @@ "Az alkalmazás nincs telepítve." "Az alkalmazás nem érhető el" "A letöltött alkalmazás Csökkentett módban ki van kapcsolva" + "A modulok ki vannak kapcsolva Csökkentett módban" "Modulok" "Modulok" "Mem. megjelenítése" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index bae306d56..9402a4265 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -26,6 +26,8 @@ "Ծրագիրը տեղադրված չէ:" "Հավելվածը հասանելի չէ" "Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում" + + "Վիջեթներ" "Վիջեթներ" "Ցուցադրել մեմը" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index b8f6d9642..2eb88c712 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -26,6 +26,7 @@ "Aplikasi tidak dipasang." "Aplikasi tidak tersedia" "Aplikasi yang diunduh dinonaktifkan dalam mode Aman" + "Widget dinonaktifkan dalam mode Aman" "Widget" "Widget" "Tampilkan Memori" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 7cd03a8e0..aaddcfc13 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -26,6 +26,7 @@ "App non installata." "App non disponibile" "L\'app scaricata è stata disattivata in modalità provvisoria" + "Widget disabilitati in modalità provvisoria" "Widget" "Widget" "Mostra Mem" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 44dfac752..fc059e05e 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -26,6 +26,7 @@ "האפליקציה לא מותקנת." "האפליקציה אינה זמינה" "אפליקציה שהורדת הושבתה במצב בטוח" + "ווידג\'טים מושבתים במצב בטוח" "רכיבי ווידג\'ט" "רכיבי ווידג\'ט" "הצג זכרון" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index e52a4ab05..d8ee943be 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -26,6 +26,7 @@ "このアプリはインストールされていません。" "このアプリは使用できません" "ダウンロードしたアプリは、セーフモードでは無効です" + "セーフモードではウィジェットは無効です" "ウィジェット" "ウィジェット" "メモリーを表示" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 5ac936c1c..809233195 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -26,6 +26,8 @@ "აპი არ არის დაყენებული." "აპი მიუწვდომელია" "უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია" + + "ვიჯეტები" "ვიჯეტები" "Mem-ის ჩვენება" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 0aec2b02d..390f0ee7b 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -26,6 +26,7 @@ "មិន​បាន​ដំឡើង​កម្មវិធី។" "មិន​មាន​កម្មវិធី" "បាន​បិទ​កម្មវិធី​ដែល​បាន​ទាញ​យក​ក្នុង​របៀប​សុវត្ថិភាព" + "បាន​បិទ​ធាតុ​ក្រាហ្វិក​ក្នុង​របៀប​សុវត្ថិភាព" "ធាតុ​ក្រាហ្វិក" "ធាតុ​ក្រាហ្វិក" "បង្ហាញ​ Mem" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 88c4dedea..d4702838c 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -26,6 +26,7 @@ "앱이 설치되지 않았습니다." "앱을 사용할 수 없음" "다운로드한 앱은 안전 모드에서 사용할 수 없습니다." + "안전 모드에서 위젯 사용 중지됨" "위젯" "위젯" "메모리 표시" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 41e41cef1..935e99768 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -26,6 +26,8 @@ "ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ." "ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້" "ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode" + + "ວິດເຈັດ" "ວິດເຈັດ" "ສະແດງຄວາມຈຳ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 145b70018..6e47f677d 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -26,6 +26,7 @@ "Programa neįdiegta." "Programa nepasiekiama" "Atsisiųsta programa išjungta Saugos režimu" + "Valdikliai išjungti Saugiame režime" "Valdikliai" "Valdikliai" "Rodyti atmintinę" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index af591e012..4079331ee 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -26,6 +26,7 @@ "Lietotne nav instalēta." "Lietotne nav pieejama." "Lejupielādētā lietotne ir atspējota drošajā režīmā." + "Logrīki atspējoti drošajā režīmā" "Logrīki" "Logrīki" "Rādīt atmiņu" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index faa52764d..a22b8fc28 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -26,6 +26,7 @@ "Апп суугаагүй байна." "Апп-г ашиглах боломжгүй" "Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн" + "Safe горимд виджетүүдийг идэвхгүйжүүлсэн" "Виджет" "Виджет" "Мем харуулах" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index d80feb9c3..1770e891f 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -26,6 +26,8 @@ "Apl tidak dipasang." "Apl tidak tersedia" "Apl yang dimuat turun dilumpuhkan dalam mod Selamat" + + "Widget" "Widget" "Papar Mem" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index d0580a7bf..6211361b3 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -26,6 +26,8 @@ "Appen er ikke installert." "Appen er ikke tilgjengelig" "En nedlastet app er deaktivert i sikker modus" + + "Moduler" "Moduler" "Vis minne" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index be588b211..3e0f6408f 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -26,6 +26,7 @@ "App is niet geïnstalleerd." "App is niet beschikbaar" "Gedownloade app uitgeschakeld in veilige modus" + "Widgets uitgeschakeld in Veilige modus" "Widgets" "Widgets" "Geheugen weergeven" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 793a37190..ee48b2441 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -26,6 +26,7 @@ "Aplikacja nie jest zainstalowana." "Aplikacja niedostępna" "Pobrana aplikacja została wyłączona w trybie awaryjnym" + "Widżety są wyłączone w trybie bezpiecznym" "Widżety" "Widżety" "Pokaż pamięć" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 11acb47db..e19fceac1 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -26,6 +26,7 @@ "A aplicação não está instalada." "A aplicação não está disponível" "Aplicação transferida desativada no Modo de segurança" + "Widgets desativados no Modo de segurança" "Widgets" "Widgets" "Mostrar mem" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 64b9bb12f..355e71a49 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -26,6 +26,8 @@ "O app não está instalado." "O app não está disponível" "App transferido por download desativado no modo de segurança" + + "Widgets" "Widgets" "Mostrar memória" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 4e36a66bc..a50939930 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -26,6 +26,7 @@ "Aplicația nu este instalată." "Aplicația nu este disponibilă" "Aplicația descărcată este dezactivată în modul de siguranță" + "Widgeturile sunt dezactivate în modul de siguranță" "Widgeturi" "Widgeturi" "Afișați memoria" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index c0ee48cea..7cbd9fa83 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -26,6 +26,8 @@ "Приложение удалено" "Приложение недоступно" "Скачанное приложение отключено в безопасном режиме" + + "Виджеты" "Виджеты" "Сведения о памяти" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index e984278c3..e3c242d05 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -26,6 +26,8 @@ "Aplikácia nie je nainštalovaná." "Aplikácia nie je k dispozícii" "Stiahnutá aplikácia je v núdzovom režime zakázaná" + + "Miniaplikácie" "Miniaplikácie" "Zobraziť pamäť" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 804779166..807b1b436 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -26,6 +26,8 @@ "Aplikacija ni nameščena." "Aplikacija ni na voljo" "Prenesena aplikacija je onemogočena v Varnem načinu" + + "Pripomočki" "Pripomočki" "Pokaži pomnilnik" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 31430fe4a..3509252e7 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -26,6 +26,7 @@ "Апликација није инсталирана." "Апликација није доступна" "Преузета апликација је онемогућена у Безбедном режиму" + "Виџети су онемогућени у Безбедном режиму" "Виџети" "Виџети" "Прикажи меморију" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 7f2af8a74..572309da3 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -26,6 +26,7 @@ "Appen är inte installerad." "Appen är inte tillgänglig" "Den hämtade appen inaktiverades i säkert läge" + "Widgets är inaktiverade i felsäkert läge" "Widgetar" "Widgetar" "Visa Mem" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 485157b6a..0284679c9 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -26,6 +26,8 @@ "Programu haijasakinishwa." "Programu haipatikani" "Programu iliyopakuliwa imezimwa katika Hali Salama" + + "Wijeti" "Wijeti" "Onyesha Kumbukumbu" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 26c72f42c..bc9ddfb79 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -26,6 +26,7 @@ "ไม่ได้ติดตั้งแอป" "แอปไม่พร้อมใช้งาน" "แอปที่ดาวน์โหลดถูกปิดในโหมดปลอดภัย" + "มีการปิดใช้งานวิดเจ็ตในเซฟโหมด" "วิดเจ็ต" "วิดเจ็ต" "แสดง Mem" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 27481be93..9078d159c 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -26,6 +26,7 @@ "Hindi naka-install ang app." "Hindi available ang app" "Naka-disable ang na-download na app sa Safe mode" + "Naka-disable ang mga widget sa Safe mode" "Mga Widget" "Mga Widget" "Ipakita ang Mem" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 3bea49669..b71748ef1 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -26,6 +26,8 @@ "Uygulama yüklü değil." "Uygulama kullanılamıyor" "İndirilen uygulama Güvenli modda devre dışı bırakıldı" + + "Widget\'lar" "Widget\'lar" "Belleği Göster" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 86ca01b3c..78cda535f 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -26,6 +26,7 @@ "Додаток видалено." "Додаток недоступний" "Завантажений додаток вимкнено в безпечному режимі" + "У безпечному режимі віджети вимкнено" "Віджети" "Віджети" "Показати пам’ять" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index b8d006386..61715c834 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -26,6 +26,7 @@ "Ứng dụng chưa được cài đặt." "Ứng dụng không có sẵn" "Ứng dụng đã tải xuống bị tắt ở chế độ An toàn" + "Tiện ích con bị vô hiệu hóa ở chế độ an toàn" "Tiện ích con" "Tiện ích con" "Hiển thị bộ nhớ" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index a0982359a..30e34b901 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -26,6 +26,8 @@ "未安装该应用。" "应用不可用" "安全模式下不允许使用下载的此应用" + + "小部件" "小部件" "显示内存空间" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 796c72e53..3656e2e22 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -26,6 +26,7 @@ "尚未安裝應用程式。" "目前無法使用這個應用程式" "在安全模式中無法使用「已下載的應用程式」功能" + "在安全模式下無法使用小工具" "小工具" "小工具" "顯示記憶體" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index ff3fa8c18..f9fdaa490 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -26,6 +26,7 @@ "應用程式未安裝。" "應用程式目前無法使用" "在安全模式中無法使用「已下載的應用程式」功能" + "在安全模式下無法使用小工具" "小工具" "小工具" "顯示記憶體" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 891f6723a..79f8b5407 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -26,6 +26,7 @@ "Uhlelo lokusebenza alufakiwe." "Uhlelo lokusebenza alutholakali" "Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile" + "Amawijethi akhutshaziwe kwimodi yokuphepha" "Amawijethi" "Amawijethi" "Bonisa i-Mem" -- cgit v1.2.3 From 7078af908063f60d7a1034b41d179b41ec0e8693 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 27 Oct 2014 11:07:12 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I004c34f906b345b3206cb45dff0710e243171a10 Auto-generated-cl: translation import --- res/values-af/strings.xml | 3 +-- res/values-cs/strings.xml | 3 +-- res/values-da/strings.xml | 3 +-- res/values-es-rUS/strings.xml | 3 +-- res/values-fa/strings.xml | 3 +-- res/values-fr-rCA/strings.xml | 3 +-- res/values-fr/strings.xml | 3 +-- res/values-hy-rAM/strings.xml | 3 +-- res/values-ka-rGE/strings.xml | 3 +-- res/values-lo-rLA/strings.xml | 3 +-- res/values-ms-rMY/strings.xml | 3 +-- res/values-nb/strings.xml | 3 +-- res/values-pt/strings.xml | 3 +-- res/values-ru/strings.xml | 3 +-- res/values-sk/strings.xml | 3 +-- res/values-sl/strings.xml | 3 +-- res/values-sw/strings.xml | 3 +-- res/values-tr/strings.xml | 3 +-- res/values-zh-rCN/strings.xml | 3 +-- res/values-zh-rHK/strings.xml | 2 +- 20 files changed, 20 insertions(+), 39 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 8aa65a4fe..5d01f360d 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -26,8 +26,7 @@ "Program is nie geïnstalleer nie." "Program is nie beskikbaar nie" "Afgelaaide program in veiligmodus gedeaktiveer" - - + "Legstukke gedeaktiveer in Veiligmodus" "Legstukke" "Legstukke" "Wys Mem" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 4b05436d4..95125bcb7 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -26,8 +26,7 @@ "Aplikace není nainstalována." "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" - - + "V Bezpečném režimu jsou widgety zakázány." "Widgety" "Widgety" "Zobrazit Mem" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index d5352f20e..c3bb08804 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -26,8 +26,7 @@ "Appen er ikke installeret." "Appen er ikke tilgængelig" "Downloadet app er deaktiveret i sikker tilstand" - - + "Widgets er deaktiveret i Beskyttet tilstand" "Widgets" "Widgets" "Vis Mem" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index dc43f2d82..847faac68 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -26,8 +26,7 @@ "No se instaló la aplicación." "La aplicación no está disponible." "Aplicación descargada inhabilitada en modo seguro" - - + "Widgets inhabilitados en modo seguro" "Widgets" "Widgets" "Mostrar memoria" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index c765d8fa0..ce10c2d85 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -26,8 +26,7 @@ "برنامه نصب نشده است." "برنامه در دسترس نیست" "برنامه دانلود شده در حالت ایمن غیرفعال شد" - - + "ابزارک‌ها در حالت ایمن غیرفعال هستند" "ابزارک‌ها" "ابزارک‌ها" "‏نمایش Mem" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index c1070f2ea..a94f571da 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -26,8 +26,7 @@ "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." - - + "Widgets désactivés en mode sans échec" "Widgets" "Widgets" "Afficher la mémoire" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 064d751ae..0f1aac475 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -26,8 +26,7 @@ "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." - - + "Les widgets sont désactivés en mode sécurisé." "Widgets" "Widgets" "Afficher la mémoire" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 9402a4265..dcff15c8f 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -26,8 +26,7 @@ "Ծրագիրը տեղադրված չէ:" "Հավելվածը հասանելի չէ" "Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում" - - + "Վիջեթներն անջատված են անվտանգ ռեժիմում" "Վիջեթներ" "Վիջեթներ" "Ցուցադրել մեմը" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 809233195..823d36a7c 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -26,8 +26,7 @@ "აპი არ არის დაყენებული." "აპი მიუწვდომელია" "უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია" - - + "უსაფრთხო რეჟიმში ვიჯეტი გამორთულია" "ვიჯეტები" "ვიჯეტები" "Mem-ის ჩვენება" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 935e99768..ac475036b 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -26,8 +26,7 @@ "ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ." "ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້" "ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode" - - + "​ວິດ​ເຈັດ​ຖືກ​ປິດ​ໃນ Safe mode" "ວິດເຈັດ" "ວິດເຈັດ" "ສະແດງຄວາມຈຳ" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 1770e891f..5f96ead43 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -26,8 +26,7 @@ "Apl tidak dipasang." "Apl tidak tersedia" "Apl yang dimuat turun dilumpuhkan dalam mod Selamat" - - + "Widget dilumpuhkan dalam mod Selamat" "Widget" "Widget" "Papar Mem" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 6211361b3..a7faeaf07 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -26,8 +26,7 @@ "Appen er ikke installert." "Appen er ikke tilgjengelig" "En nedlastet app er deaktivert i sikker modus" - - + "Moduler er deaktivert i sikker modus" "Moduler" "Moduler" "Vis minne" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 355e71a49..6c9a0d913 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -26,8 +26,7 @@ "O app não está instalado." "O app não está disponível" "App transferido por download desativado no modo de segurança" - - + "Widgets desativados no modo de segurança" "Widgets" "Widgets" "Mostrar memória" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 7cbd9fa83..8b9d7064c 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -26,8 +26,7 @@ "Приложение удалено" "Приложение недоступно" "Скачанное приложение отключено в безопасном режиме" - - + "Виджеты отключены в безопасном режиме" "Виджеты" "Виджеты" "Сведения о памяти" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index e3c242d05..2ffa4b457 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -26,8 +26,7 @@ "Aplikácia nie je nainštalovaná." "Aplikácia nie je k dispozícii" "Stiahnutá aplikácia je v núdzovom režime zakázaná" - - + "Miniaplikácie sú v núdzovom režime zakázané" "Miniaplikácie" "Miniaplikácie" "Zobraziť pamäť" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 807b1b436..f02ea27ac 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -26,8 +26,7 @@ "Aplikacija ni nameščena." "Aplikacija ni na voljo" "Prenesena aplikacija je onemogočena v Varnem načinu" - - + "Pripomočki so onemogočeni v varnem načinu" "Pripomočki" "Pripomočki" "Pokaži pomnilnik" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 0284679c9..05df3c4a0 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -26,8 +26,7 @@ "Programu haijasakinishwa." "Programu haipatikani" "Programu iliyopakuliwa imezimwa katika Hali Salama" - - + "Wijeti zimezimwa katika hali ya Usalama" "Wijeti" "Wijeti" "Onyesha Kumbukumbu" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index b71748ef1..341def708 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -26,8 +26,7 @@ "Uygulama yüklü değil." "Uygulama kullanılamıyor" "İndirilen uygulama Güvenli modda devre dışı bırakıldı" - - + "Güvenli modda widget\'lar devre dışı" "Widget\'lar" "Widget\'lar" "Belleği Göster" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 30e34b901..e716fdee2 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -26,8 +26,7 @@ "未安装该应用。" "应用不可用" "安全模式下不允许使用下载的此应用" - - + "安全模式下不允许使用小部件" "小部件" "小部件" "显示内存空间" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 3656e2e22..02c90f208 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -26,7 +26,7 @@ "尚未安裝應用程式。" "目前無法使用這個應用程式" "在安全模式中無法使用「已下載的應用程式」功能" - "在安全模式下無法使用小工具" + "在安全模式中無法使用小工具" "小工具" "小工具" "顯示記憶體" -- cgit v1.2.3 From 2775cb34b390ad52d0920bb533232571568c3f71 Mon Sep 17 00:00:00 2001 From: Cameron Neale Date: Fri, 31 Oct 2014 13:18:38 -0700 Subject: Changes made to accomodate GSA in Google3 w/ Robolectric dragViewOffset removed from WallpaperPicker as it was causing a duplicate resource conflict with launcher3 and was not used within the WallpaperPicker project. Empty tag added to WallpaperPicker manifest as required by Robolectric Removed tag from L3 manifest, unrecognized by Robolectric Change-Id: I3744fc50731f82c3e6fdc01d9819e9a0302dc503 --- WallpaperPicker/AndroidManifest.xml | 4 ++-- WallpaperPicker/res/values-sw720dp/dimens.xml | 23 ----------------------- res/values/strings.xml | 1 - 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 WallpaperPicker/res/values-sw720dp/dimens.xml diff --git a/WallpaperPicker/AndroidManifest.xml b/WallpaperPicker/AndroidManifest.xml index 86a94d078..5b6a0077d 100644 --- a/WallpaperPicker/AndroidManifest.xml +++ b/WallpaperPicker/AndroidManifest.xml @@ -4,7 +4,7 @@ android:versionCode="1" android:versionName="1.0" > - + - + diff --git a/WallpaperPicker/res/values-sw720dp/dimens.xml b/WallpaperPicker/res/values-sw720dp/dimens.xml deleted file mode 100644 index 27c4fd054..000000000 --- a/WallpaperPicker/res/values-sw720dp/dimens.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - 0dp - 0dp - diff --git a/res/values/strings.xml b/res/values/strings.xml index b3abe8fac..c5f17ac63 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -258,7 +258,6 @@ s --> OK Error: custom workspace layout passed in but custom cling was not overwritten - -- cgit v1.2.3 From 14d1af5dd6d74ce6e32acb46a8da092bdde61491 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 17 Nov 2014 10:26:59 -0800 Subject: Import translations. DO NOT MERGE Change-Id: Ia5e4b798ca68ced4d2b61d2d5d84d50f98b7bdff Auto-generated-cl: translation import --- res/values-af/strings.xml | 2 -- res/values-am/strings.xml | 2 -- res/values-ar/strings.xml | 2 -- res/values-bg/strings.xml | 2 -- res/values-ca/strings.xml | 2 -- res/values-cs/strings.xml | 2 -- res/values-da/strings.xml | 2 -- res/values-de/strings.xml | 2 -- res/values-el/strings.xml | 2 -- res/values-en-rGB/strings.xml | 2 -- res/values-en-rIN/strings.xml | 2 -- res/values-es-rUS/strings.xml | 2 -- res/values-es/strings.xml | 2 -- res/values-et-rEE/strings.xml | 2 -- res/values-fa/strings.xml | 2 -- res/values-fi/strings.xml | 2 -- res/values-fr-rCA/strings.xml | 2 -- res/values-fr/strings.xml | 2 -- res/values-hi/strings.xml | 2 -- res/values-hr/strings.xml | 2 -- res/values-hu/strings.xml | 2 -- res/values-hy-rAM/strings.xml | 2 -- res/values-in/strings.xml | 2 -- res/values-it/strings.xml | 2 -- res/values-iw/strings.xml | 2 -- res/values-ja/strings.xml | 2 -- res/values-ka-rGE/strings.xml | 2 -- res/values-km-rKH/strings.xml | 2 -- res/values-ko/strings.xml | 2 -- res/values-lo-rLA/strings.xml | 2 -- res/values-lt/strings.xml | 2 -- res/values-lv/strings.xml | 2 -- res/values-mn-rMN/strings.xml | 2 -- res/values-ms-rMY/strings.xml | 2 -- res/values-nb/strings.xml | 2 -- res/values-nl/strings.xml | 2 -- res/values-pl/strings.xml | 2 -- res/values-pt-rPT/strings.xml | 2 -- res/values-pt/strings.xml | 2 -- res/values-ro/strings.xml | 2 -- res/values-ru/strings.xml | 2 -- res/values-sk/strings.xml | 2 -- res/values-sl/strings.xml | 2 -- res/values-sr/strings.xml | 2 -- res/values-sv/strings.xml | 2 -- res/values-sw/strings.xml | 2 -- res/values-th/strings.xml | 2 -- res/values-tl/strings.xml | 2 -- res/values-tr/strings.xml | 2 -- res/values-uk/strings.xml | 2 -- res/values-vi/strings.xml | 2 -- res/values-zh-rCN/strings.xml | 2 -- res/values-zh-rHK/strings.xml | 2 -- res/values-zh-rTW/strings.xml | 2 -- res/values-zu/strings.xml | 2 -- 55 files changed, 110 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 5d01f360d..f37f08f3b 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -58,8 +58,6 @@ "Verwyder" "Deïnstalleer" "Programinligting" - "Soek" - "Stemsoektog" "Programme" "Verwyder" "Deïnstalleer opdatering" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 032610965..1a25f66cd 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -58,8 +58,6 @@ "አስወግድ" "አራግፍ" "የመተግበሪያ መረጃ" - "ፍለጋ" - "የድምፅ ፍለጋ" "መተግበሪያዎች" "አስወግድ" "ዝማኔ አራግፍ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 919f0d020..592be5841 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -58,8 +58,6 @@ "إزالة" "إزالة" "معلومات عن التطبيق" - "بحث" - "البحث الصوتي" "التطبيقات" "إزالة" "إزالة التحديث" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 6654f1dc0..3f710db30 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -58,8 +58,6 @@ "Премахване" "Деинсталиране" "Информация за приложението" - "Търсене" - "Гласово търсене" "Приложения" "Премахване" "Деинст. на актуализацията" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 019e82f88..89e582a81 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -58,8 +58,6 @@ "Suprimeix" "Desinstal·la" "Informació de l\'aplicació" - "Cerca" - "Cerca per veu" "Aplicacions" "Suprimeix" "Desinstal·la l\'actualització" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 95125bcb7..69ec22c41 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -58,8 +58,6 @@ "Odstranit" "Odinstalovat" "Informace o aplikaci" - "Hledat" - "Hlasové vyhledávání" "Aplikace" "Odstranit" "Odinstalovat aktualizaci" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index c3bb08804..5fa6aa6b6 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -58,8 +58,6 @@ "Fjern" "Afinstaller" "Oplysninger om appen" - "Søg" - "Talesøgning" "Apps" "Fjern" "Afinstaller opdatering" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 7e4ae4773..059983ecf 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -58,8 +58,6 @@ "Entfernen" "Deinstallieren" "App-Info" - "Suchen" - "Sprachsuche" "Apps" "Entfernen" "Update deinstallieren" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 5a3633261..12f0dfef1 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -58,8 +58,6 @@ "Κατάργηση" "Κατάργηση εγκατάστασης" "Πληροφορίες εφαρμογής" - "Αναζήτηση" - "Φωνητική αναζήτηση" "Εφαρμογές" "Κατάργηση" "Κατάργηση εγκατάστασης ενημέρωσης" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 7e212224c..0d004fef5 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -58,8 +58,6 @@ "Remove" "Uninstall" "App info" - "Search" - "Voice Search" "Apps" "Remove" "Uninstall update" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 7e212224c..0d004fef5 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -58,8 +58,6 @@ "Remove" "Uninstall" "App info" - "Search" - "Voice Search" "Apps" "Remove" "Uninstall update" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 847faac68..4408080e2 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -58,8 +58,6 @@ "Eliminar" "Desinstalar" "Información de la aplicación" - "Buscar" - "Búsqueda por voz" "Aplicaciones" "Eliminar" "Desinstalar actualización" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 67c283a02..898d0eaa5 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -58,8 +58,6 @@ "Eliminar" "Desinstalar" "Información de la aplicación" - "Buscar" - "Búsqueda por voz" "Aplicaciones" "Eliminar" "Desinstalar actualización" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 1b9f1b793..76b20aba7 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -58,8 +58,6 @@ "Eemalda" "Desinstalli" "Rakenduse teave" - "Otsing" - "Häälotsing" "Rakendused" "Eemalda" "Desinstalli värskendus" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index ce10c2d85..924cccf75 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -58,8 +58,6 @@ "حذف" "حذف نصب" "اطلاعات برنامه" - "جستجو" - "جستجوی شفاهی" "برنامه‌ها" "حذف" "حذف نصب به‌روزرسانی" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 0004501c3..a5da03f43 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -58,8 +58,6 @@ "Poista" "Poista" "Sovelluksen tiedot" - "Haku" - "Puhehaku" "Sovellukset" "Poista" "Poista päivitys" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index a94f571da..62a2b894e 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -58,8 +58,6 @@ "Supprimer" "Désinstaller" "Détails de l\'application" - "Rechercher" - "Recherche vocale" "Applications" "Supprimer" "Désinstaller la mise à jour" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 0f1aac475..d23b6a492 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -58,8 +58,6 @@ "Supprimer" "Désinstaller" "Informations sur l\'application" - "Rechercher" - "Recherche vocale" "Applications" "Supprimer" "Désinstaller la mise à jour" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index f14eff7cf..937248921 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -58,8 +58,6 @@ "निकालें" "अनइंस्टॉल करें" "ऐप्लिकेशन की जानकारी" - "खोजें" - "बोलकर खोजें" "ऐप्लिकेशन" "निकालें" "अपडेट अनइंस्‍टॉल करें" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index aa016484f..702b474ff 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -58,8 +58,6 @@ "Ukloni" "Deinstaliraj" "Informacije o aplikaciji" - "Pretraži" - "Glasovno pretraživanje" "Aplikacije" "Ukloni" "Deinstalacija ažuriranja" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 4416ab0aa..513548a2a 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -58,8 +58,6 @@ "Eltávolítás" "Eltávolítás" "Alkalmazásinformáció" - "Keresés" - "Hangalapú keresés" "Alkalmazások" "Eltávolítás" "Eltávolítja a frissítést" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index dcff15c8f..940c14bf2 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -58,8 +58,6 @@ "Հեռացնել" "Ապատեղադրել" "Ծրագրի տեղեկություններ" - "Որոնել" - "Ձայնային որոնում" "Ծրագրեր" "Հեռացնել" "Ապատեղադրել թարմացումը" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 2eb88c712..b0c5d139d 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -58,8 +58,6 @@ "Hapus" "Copot pemasangan" "Info aplikasi" - "Telusuri" - "Penelusuran Suara" "Aplikasi" "Hapus" "Copot pemasangan pembaruan" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index aaddcfc13..d78d0326d 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -58,8 +58,6 @@ "Rimuovi" "Disinstalla" "Informazioni app" - "Cerca" - "Ricerca vocale" "App" "Rimuovi" "Disinstalla aggiornamento" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index fc059e05e..61fb53efa 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -58,8 +58,6 @@ "הסר" "הסר התקנה" "פרטי אפליקציה" - "חפש" - "חיפוש קולי" "אפליקציות" "הסר" "הסר את התקנת העדכון" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index d8ee943be..ec872b8a9 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -58,8 +58,6 @@ "削除" "アンインストール" "アプリ情報" - "検索" - "音声検索" "アプリ" "削除" "更新をアンインストール" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 823d36a7c..b96d3d267 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -58,8 +58,6 @@ "წაშლა" "დეინსტალაცია" "აპის შესახებ" - "ძიება" - "ხმოვანი ძიება" "აპები" "წაშლა" "განახლების დეინსტალაცია" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 390f0ee7b..670ac70b5 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -58,8 +58,6 @@ "លុប​ចេញ" "លុប" "ព័ត៌មាន​កម្មវិធី" - "ស្វែងរក" - "ស្វែងរក​តាម​សំឡេង" "កម្មវិធី" "លុប​ចេញ" "លុប​បច្ចុប្បន្នភាព" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index d4702838c..c73b431bd 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -58,8 +58,6 @@ "삭제" "제거" "앱 정보" - "검색" - "음성 검색" "앱" "삭제" "업데이트 제거" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index ac475036b..1beb049e2 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -58,8 +58,6 @@ "ລຶບ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ຂໍ້ມູນແອັບຯ" - "ຊອກຫາ" - "ຊອກຫາດ້ວຍສຽງ" "ແອັບຯ" "ລຶບ" "ຖອນອັບເດດ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 6e47f677d..897c2fd9f 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -58,8 +58,6 @@ "Pašalinti" "Pašalinti" "Programos informacija" - "Ieškoti" - "Paieška balsu" "Programos" "Pašalinti" "Pašalinti naujinį" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 4079331ee..13a47606d 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -58,8 +58,6 @@ "Noņemt" "Atinstalēt" "Lietotnes informācija" - "Meklēt" - "Meklēšana ar balsi" "Lietotnes" "Noņemt" "Atinstalēt atjauninājumu" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index a22b8fc28..f258b4c8d 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -58,8 +58,6 @@ "Устгах" "Устгах" "Апп мэдээлэл" - "Хайх" - "Дуун хайлт" "Апп" "Устгах" "Шинэчлэлийг устгах" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 5f96ead43..532b6ad8b 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -58,8 +58,6 @@ "Alih keluar" "Nyahpasang" "Maklumat apl" - "Cari" - "Carian Suara" "Apl" "Alih keluar" "Nyahpasang kemas kini" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index a7faeaf07..a9bbe08ca 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -58,8 +58,6 @@ "Fjern" "Avinstaller" "App-info" - "Søk" - "Talesøk" "Apper" "Fjern" "Avinstaller oppdateringen" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 3e0f6408f..8a32fd1d9 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -58,8 +58,6 @@ "Verwijderen" "Verwijderen" "App-info" - "Zoeken" - "Gesproken zoekopdracht" "Apps" "Verwijderen" "Update verwijderen" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index ee48b2441..4eabae96e 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -58,8 +58,6 @@ "Usuń" "Odinstaluj" "Informacje o aplikacji" - "Szukaj" - "Wyszukiwanie głosowe" "Aplikacje" "Usuń" "Odinstaluj aktualizację" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index e19fceac1..4397ebf81 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -58,8 +58,6 @@ "Remover" "Desinstalar" "Informações da aplicação" - "Pesquisar" - "Pesquisa por Voz" "Aplicações" "Remover" "Desinstalar atualização" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 6c9a0d913..07c82143e 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -58,8 +58,6 @@ "Remover" "Desinstalar" "Informações do app" - "Pesquisar" - "Pesquisa por voz" "Apps" "Remover" "Desinstalar atualização" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index a50939930..54cb46c3f 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -58,8 +58,6 @@ "Eliminați" "Dezinstalați" "Informații despre aplicație" - "Căutați" - "Căutare vocală" "Aplicații" "Eliminați" "Dezinstalați actualizarea" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 8b9d7064c..70ef0f591 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -58,8 +58,6 @@ "Удалить" "Удалить" "О приложении" - "Поиск" - "Голосовой поиск" "Приложения" "Удалить" "Удалить обновление" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 2ffa4b457..9cd0fbe13 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -58,8 +58,6 @@ "Odstrániť" "Odinštalovať" "Informácie o aplikácii" - "Vyhľadať" - "Hlasové vyhľadávanie" "Aplikácie" "Odstrániť" "Odinštalovať aktualizáciu" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index f02ea27ac..d5a34d58f 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -58,8 +58,6 @@ "Odstrani" "Odstrani" "Podatki o aplikaciji" - "Iskanje" - "Glasovno iskanje" "Aplikacije" "Odstrani" "Odstrani posodobitev" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 3509252e7..9f40890c4 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -58,8 +58,6 @@ "Уклони" "Деинсталирај" "Информације о апликацији" - "Претражи" - "Гласовна претрага" "Апликације" "Уклони" "Деинсталирај ажурирање" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 572309da3..25f4d2623 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -58,8 +58,6 @@ "Ta bort" "Avinstallera" "Info om appen" - "Sök" - "Röstsökning" "Appar" "Ta bort" "Avinstallera uppdatering" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 05df3c4a0..fa810618b 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -58,8 +58,6 @@ "Ondoa" "Ondoa" "Maelezo ya programu" - "Tafuta" - "Kutafuta kwa Kutamka" "Programu" "Ondoa" "Ondoa sasisho" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index bc9ddfb79..0fc20d26f 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -58,8 +58,6 @@ "ลบ" "ถอนการติดตั้ง" "ข้อมูลแอป" - "ค้นหา" - "ค้นหาด้วยเสียง" "แอป" "ลบ" "ถอนการติดตั้งการอัปเดต" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 9078d159c..4d2f1d478 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -58,8 +58,6 @@ "Alisin" "I-uninstall" "Impormasyon ng app" - "Hanapin" - "Paghahanap Gamit ang Boses" "Apps" "Alisin" "I-uninstall ang update" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 341def708..6c78f6f63 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -58,8 +58,6 @@ "Kaldır" "Yüklemeyi kaldır" "Uygulama bilgileri" - "Ara" - "Sesli Arama" "Uygulamalar" "Kaldır" "Güncellemeyi kaldır" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 78cda535f..71e1816a3 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -58,8 +58,6 @@ "Вилучити" "Видалити" "Про програму" - "Пошук" - "Голосовий пошук" "Додатки" "Вилучити" "Видалити оновлення" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 61715c834..5bc16275e 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -58,8 +58,6 @@ "Xóa" "Gỡ cài đặt" "Thông tin ứng dụng" - "Tìm kiếm" - "Tìm kiếm bằng giọng nói" "Ứng dụng" "Xóa" "Gỡ cài đặt cập nhật" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index e716fdee2..b7c42fe5c 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -58,8 +58,6 @@ "删除" "卸载" "应用信息" - "搜索" - "语音搜索" "应用" "删除" "卸载更新内容" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 02c90f208..2eb700890 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -58,8 +58,6 @@ "移除" "解除安裝" "應用程式資料" - "搜尋" - "語音搜尋" "應用程式" "移除" "解除安裝更新" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index f9fdaa490..52e758396 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -58,8 +58,6 @@ "移除" "解除安裝" "應用程式資訊" - "搜尋" - "語音搜尋" "應用程式" "移除" "解除安裝更新" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 79f8b5407..aea09e845 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -58,8 +58,6 @@ "Susa" "Khipha" "Ulwazi lohlelo lokusebenza" - "Sesha" - "Ukusesha ngezwi" "Izinhlelo zokusebenza" "Susa" "Khipha isibuyekezo" -- cgit v1.2.3 From 7f4d6d6645682c4567b15e9f3e2a0350c85d809c Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 24 Nov 2014 06:21:29 -0800 Subject: Import translations. DO NOT MERGE Change-Id: Ib839840795a9298d8539b040c5a67366398ed663 Auto-generated-cl: translation import --- res/values-bn-rBD/strings.xml | 9 ++++++--- res/values-eu-rES/strings.xml | 9 ++++++--- res/values-gl-rES/strings.xml | 11 +++++++---- res/values-is-rIS/strings.xml | 9 ++++++--- res/values-kk-rKZ/strings.xml | 12 +++++++----- res/values-km-rKH/strings.xml | 2 +- res/values-kn-rIN/strings.xml | 9 ++++++--- res/values-ky-rKG/strings.xml | 12 +++++++----- res/values-lo-rLA/strings.xml | 4 ++-- res/values-mk-rMK/strings.xml | 11 +++++++---- res/values-ml-rIN/strings.xml | 9 ++++++--- res/values-mr-rIN/strings.xml | 9 ++++++--- res/values-my-rMM/strings.xml | 21 ++++++++++++--------- res/values-ne-rNP/strings.xml | 12 +++++++----- res/values-si-rLK/strings.xml | 12 +++++++----- res/values-ta-rIN/strings.xml | 33 ++++++++++++++++++--------------- res/values-te-rIN/strings.xml | 11 +++++++---- res/values-ur-rPK/strings.xml | 12 +++++++----- res/values-uz-rUZ/strings.xml | 12 +++++++----- 19 files changed, 132 insertions(+), 87 deletions(-) diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index f60f1c507..143420103 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -24,12 +24,13 @@ "Android প্রাথমিক অ্যাপ্লিকেশানগুলি" "অ্যাপ্লিকেশান ইনস্টল করা নেই৷" + "অ্যাপ্লিকেশান অনুপলব্ধ" "ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে" + "সুরক্ষিত মোডে উইজেট নিষ্ক্রিয় থাকে" "উইজেটগুলি" "উইজেটগুলি" "মেম দেখান" "একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷" - "দোকান" "%1$d × %2$d" "এই হোম স্ক্রীনে আইটেম রাখা যায়নি৷" "তৈরি করেতে উইজেট চয়ন করুন" @@ -57,8 +58,6 @@ "সরান" "আনইনস্টল করুন" "অ্যাপ্লিকেশানের তথ্য" - "অনুসন্ধান করুন" - "ভয়েস অনুসন্ধান" "অ্যাপ্লিকেশানগুলি" "সরান" "আপডেট আনইনস্টল করুন" @@ -77,6 +76,7 @@ "হোম সেটিংস এবং শর্টকাটগুলি লেখে" "হোমে অ্যাপ্লিকেশানটিকে সেটিংস এবং শর্টকাটগুলি পরিবর্তন করতে দেয়৷" "উইজেট লোড হতে সমস্যা হয়েছে" + "সেটআপ" "এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷" "রকেট লঞ্চার" "নামবিহীন ফোল্ডার" @@ -96,6 +96,9 @@ "নতুন করে শুরু করুন" "আপনার স্থান সংগঠিত করুন" "ওয়ালপেপার, উইজেট এবং সেটিংস পরিচালনা করতে পটভূমি স্পর্শ করে ধরে রাখুন৷" + "ওয়ালপেপার, উইজেট এবং সেটিংস" + "কাস্টমাইজ করার জন্য পটভূমি স্পর্শ করে ধরে থাকুন" + "বুঝেছি" "এখানে একটি ফোল্ডার আছে" "এটির মতো একটি তৈরি করতে, একটি অ্যাপ্লিকেশান স্পর্শ করে ধরে রাখুন, এবং তারপরে এটিকে অন্য একটির উপরে সরিয়ে নিয়ে যান৷" "ঠিক আছে" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 4a4c959f4..5b50e828c 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -24,12 +24,13 @@ "Android-en nukleoko aplikazioak" "Aplikazioa instalatu gabe dago." + "Ez dago erabilgarri aplikazioa" "Deskargatutako aplikazioa modu seguruan desgaitu da" + "Widgetak desgaitu egin dira modu seguruan" "Widgetak" "Widgetak" "Erakutsi memoria" "Eduki ukituta widgeta aukeratzeko." - "Denda" "%1$d × %2$d" "Ezin izan da elementua hasierako pantailan jaregin." "Aukeratu sortu beharreko widgeta" @@ -57,8 +58,6 @@ "Kendu" "Desinstalatu" "Aplikazioaren informazioa" - "Bilaketa" - "Ahots bidezko bilaketa" "Aplikazioak" "Kendu" "Desinstalatu eguneratzea" @@ -77,6 +76,7 @@ "Idatzi hasierako ezarpenak eta lasterbideak" "Hasierako pantailako ezarpenak eta lasterbideak aldatzea baimentzen die aplikazioei." "Arazo bat izan da widgeta kargatzean" + "Konfigurazioa" "Sistema-aplikazioa da hau eta ezin da desinstalatu." "Rocket Launcher" "Izenik gabeko karpeta" @@ -96,6 +96,9 @@ "HASI HUTSETIK" "Antolatu zure txokoa" "Eduki ukituta atzeko planoa horma-paperak, widgetak eta ezarpenak kudeatzeko." + "Horma-paperak, widgetak eta ezarpenak" + "Pertsonalizatzeko, eduki ukituta atzeko planoa" + "ADOS" "Hortxe duzu karpeta" "Horrelako bat sortzeko, eduki ukituta aplikazio bat eta eraman beste baten gainera." "Ados" diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index c053b07fe..96a53dc09 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -24,12 +24,13 @@ "Aplicacións básicas de Android" "A aplicación non está instalada" - "Desactivouse a aplicación descargada no modo seguro" + "A aplicación non está dispoñible" + "A aplicación que descargaches está desactivada no modo seguro" + "Os widgets están desactivados no modo seguro" "Widgets" "Widgets" "Mostrar memoria" "Mantén premido un widget para seleccionalo." - "Tenda" "%1$d × %2$d" "Non se puido engadir á pantalla de inicio." "Escolle o widget que queiras crear" @@ -57,8 +58,6 @@ "Eliminar" "Desinstalar" "Información da aplicación" - "Buscar" - "Busca de voz" "Aplicacións" "Eliminar" "Desinstalar actualización" @@ -77,6 +76,7 @@ "modificar a configuración e os atallos da pantalla de inicio" "Permite a unha aplicación cambiar a configuración e os atallos da pantalla de inicio." "Produciuse un problema ao cargar o widget" + "Configuración" "Esta aplicación é do sistema e non se pode desinstalar." "Lanzacohetes" "Cartafol sen nome" @@ -96,6 +96,9 @@ "COMEZAR DE CERO" "Organiza o espazo" "Mantén premido o fondo para xestionar o fondo de pantalla e máis." + "Fondos pantalla, widgets e configuración" + "Mantén tocado o segundo plano para personalizar" + "DE ACORDO" "Isto é un cartafol" "Para crear un igual, mantén premida a aplicación e móvea sobre outra." "Aceptar" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index 71f0edea9..a3fa0bc52 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -24,12 +24,13 @@ "Kjarnaforrit Android" "Forritið er ekki uppsett." + "Forritið er ekki í boði" "Sótt forrit er óvirkt í öryggisstillingu" + "Græjur eru óvirkar í öruggri stillingu" "Græjur" "Græjur" "Sýna minni" "Haltu fingri á græju til að grípa hana." - "Verslun" "%1$d × %2$d" "Ekki er hægt að sleppa atriði á þennan heimaskjá." "Veldu græju til að búa til" @@ -57,8 +58,6 @@ "Fjarlægja" "Eyða" "Upplýsingar um forrit" - "Leita" - "Raddleit" "Forrit" "Fjarlægja" "Fjarlægja uppfærslu" @@ -77,6 +76,7 @@ "skrifa stillingar og flýtileiðir heimaskjás" "Leyfir forriti að breyta stillingum og flýtileiðum heimaskjás." "Vandamál við að hlaða græju" + "Uppsetning" "Þetta er kerfisforrit sem ekki er hægt að fjarlægja." "Eldflaugapallur" "Ónefnd mappa" @@ -96,6 +96,9 @@ "BYRJA UPP Á NÝTT" "Settu hlutina á sína staði" "Haltu inni á bakgrunni til að stjórna veggfóðri, græjum og stillingum." + "Veggfóður, græjur og stillingar" + "Haltu fingri á bakgrunninum til að sérsníða hann" + "ÉG SKIL" "Hér er mappa" "Til að búa til svona skaltu draga forrit yfir á annað forrit." "Í lagi" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index 288129ace..c5b5a62f6 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -24,13 +24,13 @@ "Android Core қолданбалары" "Қолданба орнатылмаған." - - + "Қолданба қол жетімді емес" + "Жүктелген қолданба қауіпсіз режимде өшірілген" + "Қауіпсіз режимде виджеттер өшіріледі" "Виджеттер" "Виджеттер" "Жадты көрсету" "Виджетті таңдау үшін түртіп, мықтап ұстаңыз." - "Дүкен" "%1$d × %2$d" "Элементті осы Негізгі Экранға тастау орындалмады." "Жасақтау үшін виджет таңдау" @@ -58,8 +58,6 @@ "Алып тастау" "Алмау" "Қолданба ақпары" - "Іздеу" - "Дауыс арқылы іздеу" "Қолданбалар" "Алып тастау" "Жаңартуды алмау" @@ -78,6 +76,7 @@ "Негізгі экран параметрлері мен төте пернелерін жазу" "Қолданбаға Негізгі экрандағы параметрлер мен төте пернелерді өзгерту мүмкіндігін береді." "Виджетті жүктеу барысында мәселе орын алды" + "Орнату" "Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес." "Rocket Launcher" "Атауы жоқ қалта" @@ -97,6 +96,9 @@ "ЖАҢАДАН БАСТАУ" "Кеңістікті реттеу" "Артқы фонды, виджеттерді және параметрлерді басқару үшін артқы шебін түртіп, мықтап ұстаңыз." + "Тұсқағаздар, виджеттер және параметрлер" + "Теңшеу үшін фонды түртіп, ұстап тұрыңыз" + "ТҮСІНДІМ" "Міне, қалта." "Осы сияқты қалта жасау үшін, қолданбаны түртіп, мықтап ұстаңыз, одан кейін екіншісінің үстінен жылжытыңыз." "Жарайды" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 670ac70b5..3a1c82dcc 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -85,7 +85,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍​" + "សូម​ស្វាគមន៍" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index e84ece03c..7a070511d 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -24,12 +24,13 @@ "Android Core ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" "ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ" + "ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ" "ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" + "ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" "ವಿಜೆಟ್‌ಗಳು" "ವಿಜೆಟ್‌ಗಳು" "ಸ್ಮರಣೆ ತೋರಿಸು" "ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ & ಹಿಡಿದುಕೊಳ್ಳಿ." - "ಶಾಪ್‌" "%1$d × %2$d" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಐಟಂ ಅನ್ನು ಬಿಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ." "ರಚಿಸಲು ವಿಜೆಟ್‌ ಆಯ್ಕೆಮಾಡಿ" @@ -57,8 +58,6 @@ "ತೆಗೆದುಹಾಕು" "ಅಸ್ಥಾಪಿಸು" "ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ" - "ಹುಡುಕು" - "ಧ್ವನಿ ಹುಡುಕಾಟ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" "ತೆಗೆದುಹಾಕು" "ನವೀಕರಣವನ್ನು ಅಸ್ಥಾಪಿಸು" @@ -77,6 +76,7 @@ "ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಬರೆಯಿರಿ" "ಮುಖಪುಟದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ." "ವಿಜೆಟ್ ಲೋಡ್‌ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ" + "ಸೆಟಪ್" "ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ." "ರಾಕೆಟ್ ಲಾಂಚರ್" "ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್" @@ -96,6 +96,9 @@ "ಹೊಸದಾಗಿ ಪ್ರಾರಂಭಿಸು" "ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ವ್ಯವಸ್ಥಿತಗೊಳಿಸಿ" "ವಾಲ್‌ಪೇಪರ್‌, ವಿಜೆಟ್‌ಗಳು ಮತ್ತು ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ & ಹಿಡಿದುಕೊಳ್ಳಿ." + "ವಾಲ್‌ಪೇಪರ್‌ಗಳು, ವಿಜೆಟ್‌ಗಳು, & ಸೆಟ್ಟಿಂಗ್‌ಗಳು" + "ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ & ಒತ್ತಿ ಹಿಡಿಯಿರಿ" + "ಅರ್ಥವಾಯಿತು" "ಇಲ್ಲೊಂದು ಫೋಲ್ಡರ್ ಇದೆ" "ಈ ರೀತಿ ರಚಿಸಲು, ಸ್ಪರ್ಶಿಸಿ & ಆಪ್‌ ಹಿಡಿದುಕೊಂಡು ಮತ್ತೊಂದರ ಮೇಲೆ ಸರಿಸಿ." "ಸರಿ" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index 5b50555ae..a8a605b26 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -24,13 +24,13 @@ "Android Core колдонмолору" "Колдонмо орнотулган эмес." - - + "Колдонмо жеткиликтүү эмес" + "Жүктөп алынган колдонмо Коопсуз режиминде иштен чыгарылды" + "Виджеттер Коопсуз режимде өчүрүлгөн" "Виджеттер" "Виджеттер" "Мемди көргөзүү" "Виджетти тандаш үчүн, басып туруңуз" - "Дүкөн" "%1$d × %2$d" "Муну бул Үй экранына ыргытуу мүмкүн эмес." "Түзүлүүчү виджетти тандаңыз" @@ -58,8 +58,6 @@ "Алып салуу" "Чечип салуу" "Колдонмо тууралуу" - "Издөө" - "Үн менен издөө" "Колдонмолор" "Алып салуу" "Жаңыртууну чечип салуу" @@ -78,6 +76,7 @@ "Үйдүн тууралоолорун жана тез чакырмаларын жазуу" "Колдонмого Үйдүн тууралоолорун жана тез чакырмаларын өзгөртүүгө уруксат берет." "Виджетти жүктөөдө маселе бар" + "Орнотуу" "Бул системдик колдонмо жана аны чечкенге болбойт." "Rocket Launcher" "Аты жок фолдер" @@ -97,6 +96,9 @@ "ТАЗАСЫН БАШТОО" "Өз мейкиндигиңизди уюштуруңуз" "Тушкагаздарды, виджеттерди жана тууралоолорду башкаруу үчүн фонду басып туруңуз." + "Тушкагаздар, виджеттер & жөндөөлөр" + "Өзгөчөлөштүрүү үчүн фонго тийип & коё бербей туруңуз" + "ТҮШҮНДҮМ" "Мынакей фолдер" "Башкасын түзүш үчүн колдонмону басып туруп, башканын жанына жылдырыңыз." "OK" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1beb049e2..65f01b7f0 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ​" + "ຍົກ​ເລີກ" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -117,7 +117,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ​" + "ລຶບ" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index 0d775712f..dcc55d7b7 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -24,12 +24,13 @@ "Основни апликации на Android" "Апликацијата не е инсталирана." + "Апликацијата не е достапна" "Преземената апликација е оневозможена во безбеден режим" + "Додатоците се оневозможени во безбеден режим" "Виџети" "Виџети" "Прикажи „Мени“" "Допри и задржи за да се избере виџетот." - "Продавница" "%1$d × %2$d" "Не можеше да се спушти елемент на овој екран на почетната страница." "Избери виџет за да се создаде" @@ -57,8 +58,6 @@ "Отстрани" "Деинсталирај" "Информации за апликацијата" - "Пребарај" - "Гласовно пребарување" "Апликации" "Отстрани" "Деинсталирај ажурирање" @@ -77,6 +76,7 @@ "напиши подесувања и кратенки на почетна страница" "Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница." "Проблем при вчитувањето на виџетот" + "Поставување" "Ова е системска апликација и не може да се деинсталира." "Rocket Launcher" "Неименувана папка" @@ -91,11 +91,14 @@ "Создади повеќе екрани за апликации и папки" "Копирај икони за апликација" - "Увези икони и папки од старите екрани на почетната страница?" + "Зачувај икони и папки од твоите стари почетни страни?" "КОПИРАЈ ИКОНИ" "СТАРТУВАЈ ОД ПОЧЕТОК" "Организирајте го вашиот простор" "Допри и задржи ја заднината за управување со тапети, виџети и подесувања." + "Тапети, додатоци и поставки" + "Допрете и задржете на заднината за да приспособите" + "СФАТИВ" "Еве папка" "За да создадете ваква, допрете и држете ја апликацијата, а потоа поместете ја врз другата." "Во ред" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index 32329f1c5..cf5efaae2 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -24,12 +24,13 @@ "Android Core അപ്ലിക്കേഷനുകൾ" "അപ്ലിക്കേഷൻ ഇൻസ്‌റ്റാളുചെ‌യ്‌തിട്ടില്ല." + "അപ്ലിക്കേഷൻ ലഭ്യമല്ല" "ഡൗൺലോഡുചെയ്‌ത അപ്ലിക്കേഷൻ സുരക്ഷാ മോഡിൽ പ്രവർത്തനരഹിതമാക്കി" + "സുരക്ഷിത മോഡിൽ വിജറ്റുകൾ പ്രവർത്തനരഹിതമാക്കി" "വിജറ്റുകൾ" "വിജറ്റുകൾ" "മെമ്മറി കാണിക്കുക" "ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക." - "ഷോപ്പുചെയ്യുക" "%1$d × %2$d" "ഹോം സ്‌ക്രീനിൽ ഇനം വലിച്ചിടാനായില്ല." "സൃഷ്‌ടിക്കുന്നതിന് വിജറ്റ് തിരഞ്ഞെടുക്കുക" @@ -57,8 +58,6 @@ "നീക്കംചെയ്യുക" "അണ്‍ഇസ്റ്റാളുചെയ്യുക" "അപ്ലിക്കേഷൻ വിവരം" - "തിരയുക" - "വോയ്‌സ് തിരയൽ" "അപ്ലിക്കേഷനുകൾ" "നീക്കംചെയ്യുക" "അപ്‌ഡേറ്റ് അൺഇൻസ്റ്റാളുചെയ്യുക" @@ -77,6 +76,7 @@ "ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റൈറ്റുചെയ്യുക" "ഹോമിലെ ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും മാറ്റാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." "വിജറ്റ് ലോഡുചെയ്യുന്നതിൽ പ്രശ്നമുണ്ട്" + "സജ്ജീകരിക്കുക" "ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല." "റോക്കറ്റ് ലോഞ്ചർ" "പേരുനൽകാത്ത ഫോൾഡർ" @@ -96,6 +96,9 @@ "പുതുതായി ആരംഭിക്കുക" "നിങ്ങളുടെ ഇടം ഓർഗനൈസുചെയ്യുക" "വാൾപേപ്പർ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ നിയന്ത്രിക്കുന്നതിന് പശ്ചാത്തലം സ്‌പർശിച്ച് പിടിക്കുക." + "വാൾപേപ്പറുകൾ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ" + "ഇഷ്‌ടാനുസൃതമാക്കുന്നതിന് പശ്‌ചാത്തലം സ്‌പർശിച്ചുപിടിക്കുക" + "മനസ്സിലായി" "ഇവിടെയൊരു ഫോൾഡർ ഉണ്ട്" "ഇതുപോലൊന്ന് സൃഷ്‌ടിക്കുന്നതിന്, ഒരു അപ്ലിക്കേഷൻ സ്‌പർശിച്ച് പിടിച്ചുകൊണ്ട് അത് മറ്റൊന്നിലേക്ക് നീക്കുക." "ശരി" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index 2e51e7b26..098e83023 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -24,12 +24,13 @@ "Android Core Apps" "अॅप स्थापित केलेला नाही." + "अॅप उपलब्ध नाही" "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" + "विजेट सुरक्षित मोडमध्ये अक्षम झाले" "विजेट" "विजेट" "Mem दर्शवा" "विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." - "खरेदी करा" "%1$d × %2$d" "या मुख्य स्क्रीनवर आयटम ड्रॉप करू शकलो नाही." "तयार करण्यासाठी विजेट निवडा" @@ -57,8 +58,6 @@ "काढा" "विस्थापित करा" "अॅप माहिती" - "शोधा" - "व्हॉइस शोध" "अॅप्स" "काढा" "अद्यतन विस्थापित करा" @@ -77,6 +76,7 @@ "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते." "विजेट लोड करण्यात समस्या" + "सेटअप" "हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही." "रॉकेट लाँचर" "अनामित फोल्डर" @@ -96,6 +96,9 @@ "नव्याने प्रारंभ करा" "आपले स्थान व्यवस्थापित करा" "वॉलपेपर, विजेट आणि सेटिंग्ज व्यवस्थापित करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरून ठेवा." + "वॉलपेपर, विजेट आणि सेटिंग्ज" + "सानुकूल करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरुन ठेवा" + "समजले" "येथे एक फोल्डर आहे" "यासारखे एखादे तयार करण्यासाठी अॅप ला स्पर्श करा आणि धरून ठेवा, नंतर तो दुसर्‍यावर हलवा." "ठीक" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 8048b02a3..668803587 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -24,19 +24,20 @@ "Androidပင်မ အပ်ပလီကေးရှင်းများ" "အပ်ပလီကေးရှင်း မထည့်သွင်းထားပါ" + "App လက်လှမ်း မမှီပါ" "ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား" + "လုံခြုံရေး မုဒ်ထဲမှာ ဝီဂျက်များကို ပိတ်ထား" "ဝဒ်ဂျက်များ" "ဝဒ်ဂျက်များ" "Mem ကိုပြရန်" "ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ" - "စျေးဝယ်ရန်" "%1$d × %2$d" "ပင်မမျက်နှာစာတွင် အရာများ ချ လို့ မရတော့ပါ" "ဝဒ်ဂျက်တစ်ခုအား ပြုဖန်တီးရန် ရွေးပါ" "အကန့်အမည်" "အကန့်အမည်ပြောင်းရန်" "ကောင်းပြီ" - "ပယ်ဖျက်သည်" + "ထားတော့" "ပင်မမျက်နှာစာသို့ ထည့်ပါ" "အပ်ပလီကေးရှင်းများ" "အတိုကောက်မှတ်သားမှုများ" @@ -57,11 +58,9 @@ "ဖယ်ရှာခြင်း" "ဖယ်ရှားခြင်း" "အပ်ပလီကေးရှင်း အချက်အလက်များ" - "ရှာဖွေခြင်း" - "အသံဖြင့် ရှာဖွေခြင်း" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -77,6 +76,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -85,8 +85,8 @@ "ပင်မစာမျက်နှာ %1$d မှ %2$d" "အပ်ပလီကေးရှင်းပြ စာမျက်နှာ %1$d မှ %2$d" "ဝဒ်ဂျက်ပြ စာမျက်နှာ %1$d မှ %2$d" - "ကြိုဆိုပါသည်" - "ကိုယ့်အိမ်ကိုယ့်ယာလို သဘောထားပါ" + "မင်္ဂလာပါ" + "ကိုယ့်အိမ်လို သဘောထားပါ" "အပ်ပလီကေးရှင်း နှင့် အကန့်များအတွက် ဖန်သားပြင်မှာ ထပ်ထည့်ပါ" @@ -96,10 +96,13 @@ "START FRESH" "စနစ်တကျဖြစ်အောင် ပြုလုပ်ပါ" "နောက်ခံကို ဖိကိုင်၍ နောက်ခံပုံ၊ဝဒ်ဂျက်များ၊အပြင်အဆင်များကို ထိန်းချုပ်ပါ" + "နောက်ခံများ၊ ဝီဂျက်များ& ဆက်တင်များ" + "နောက်ခံကို စိတ်တိုင်းကျ ပြုလုပ်ရန် ထိလျက် & ကိုင်ထားပါ" + "ရပြီ" "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -108,7 +111,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index fe75ea70d..815873de9 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -24,13 +24,13 @@ "Android मूल अनुप्रयोगहरू" "अनुप्रयोग स्थापित छैन।" - - + "अनुप्रयोग उपलब्ध छैन" + "सुरक्षित मोडमा डाउनलोड गरेको अनुप्रयोग अक्षम गरिएको छ" + "सुरक्षित मोडमा विगेटहरू अक्षम गरियो" "विजेटहरू" "विजेटहरू" "Mem देखाउनुहोस्" "एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।" - "पसल" "%1$d × %2$d" "यो गृह स्क्रिनमा वस्तु खसाउन सकिँदैन।" "सृजना गर्नको लागि विजेट छान्नुहोस्" @@ -58,8 +58,6 @@ "हटाउनुहोस्" "हटाउनुहोस्" "अनुप्रयोग जानकारी" - "खोज्नुहोस्" - "ध्वनि खोज" "अनुप्रयोगहरू" "हटाउनुहोस्" "अद्यावधिक अस्थापित गर्नुहोस्" @@ -78,6 +76,7 @@ "गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्" "गृहमा एउटा अनुप्रयोगलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।" "समस्या लोडिङ गर्ने विजेट" + "सेटअप" "यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।" "रकेट लन्चर" "बेनाम फोल्डर" @@ -99,6 +98,9 @@ "START FRESH" "आफ्नो ठाउँ व्यवस्थापन गर्नुहोस्" "वालपेपर, विजेट र सेटिङ्स प्रबन्ध गर्न पृष्ठभूमिलाई टच गरेर होल्ड गर्नुहोस्।" + "वालपेपरहरू, विजेट; सेटिङहरू" + "छुनुहोस् ; अनुकूलन पृष्ठभूमि होल्ड गर्नुहोस्" + "बुझियो" "यहाँ एउटा फोल्डर छ" "यस्तै एक किसिमका सिर्जना गर्न, अनुप्रयोगलाई टच गरेर होल्ड गर्नुहोस्, त्यसपछि यसलाई अर्को माथि सार्नुहोस्।" "ठिक छ" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index d334da92b..ab3abb83b 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -24,13 +24,13 @@ "Android මධ්‍ය යෙදුම්" "යෙදුම ස්ථාපනය කර නැත." - - + "යෙදුම නොතිබේ" + "ආරක්ෂිත ආකාරය තුළ බාගන්න ලද යෙදුම් අබල කරන්න" + "සුරක්ෂිත ආකාරය තුළ විජටය අබල කරන ලදි" "විජට්" "විජට්" "Mem පෙන්වන්න" "විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න." - "සාප්පුයාම" "%1$d × %2$d" "මෙම මුල් පිටු තිරය වෙත අයිතමය ඇද හෙළිය නොහැකි විය." "සැදීමට විජට් එක තෝරන්න" @@ -58,8 +58,6 @@ "ඉවත් කරන්න" "අස්ථාපනය කරන්න" "යෙදුම් තොරතුරු" - "සොයන්න" - "හඬ සෙවීම" "යෙදුම්" "ඉවත් කරන්න" "යාවත්කාලිනය අස්ථාපනය කරන්න" @@ -78,6 +76,7 @@ "මුල් පිටු සැකසීම් සහ කෙටිමං ලියන්න" "මුල් පිටුවේ සැකසීම් සහ කෙටිමං ඉවත් කිරීමට යෙදුමට අවසර දෙයි." "ගැටලු පූරණ විජට් එක" + "ස්ථාපනය කරන්න" "මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක." "රොකට් ආරම්භකය" "නම් නොකළ ෆෝල්ඩරය" @@ -97,6 +96,9 @@ "අලුතින් පටන්ගන්න" "ඔබගේ ඉඩ සංවිධානය කරගන්න" "බිතුපත, විජට් සහ සැකසීම් කළමනාකරණය කිරීමට පසුබිම ස්පර්ශ කර අල්ලාගෙන සිටින්න." + "වෝල්පේපර, විජට්, සහ සැකසීම්" + "පසුබිම අභිරුචිකරණය කිරීමට ස්පර්ශ කර අල්ලා සිටින්න" + "තේරුණා" "මෙන්න ෆෝල්ඩරයක්" "මෙවැනි එකක් තැනීමට, යෙදුමක් තට්ටු කර අල්ලාගෙන සිටින්න, අනතුරුව එය තවත් එකක් උඩින් ගෙන යන්න." "හරි" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index f3aef1cd7..9dba6f669 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -24,12 +24,13 @@ "Android முக்கியப் பயன்பாடுகள்" "பயன்பாடு நிறுவப்படவில்லை." + "பயன்பாடு இல்லை" "இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது" - "விட்ஜெட்கள்" - "விட்ஜெட்கள்" + "பாதுகாப்புப் பயன்முறையில் விட்ஜெட்கள் முடக்கப்பட்டுள்ளன" + "ஷார்ட்கட்ஸ்" + "ஷார்ட்கட்ஸ்" "நினைவகத்தைக் காட்டு" - "விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் & பிடிக்கவும்." - "ஷாப்" + "விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்." "%1$d × %2$d" "உருப்படியை இந்த முகப்புத் திரையில் விட முடியவில்லை." "உருவாக்குவதற்கு விட்ஜெட்டைத் தேர்வுசெய்யவும்" @@ -40,7 +41,7 @@ "முகப்புத் திரையில் சேர்" "பயன்பாடுகள்" "குறுக்குவழிகள்" - "விட்ஜெட்கள்" + "ஷார்ட்கட்ஸ்" "உங்கள் முகப்புத் திரைகளில் வேறு இடம் இல்லை." "முகப்புத் திரையில் இடமில்லை." "பிடித்தவை ட்ரேயில் இடமில்லை" @@ -57,8 +58,6 @@ "அகற்று" "நிறுவல் நீக்கு" "பயன்பாட்டுத் தகவல்" - "தேடு" - "குரல் தேடல்" "பயன்பாடுகள்" "அகற்று" "புதுப்பிப்பை நிறுவல் நீக்கு" @@ -72,11 +71,12 @@ "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது." "குறுக்குவழிகளை நிறுவல் நீக்குதல்" "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளை அகற்ற பயன்பாட்டை அனுமதிக்கிறது." - "முகப்பின் அமைப்புகள் மற்றும் குறுக்குவழிகளைப் படித்தல்" - "முகப்பில் உள்ள அமைப்புகள் மற்றும் குறுக்குவழிகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது." - "முகப்பின் அமைப்புகள் மற்றும் குறுக்குவழிகளை எழுதுதல்" - "முகப்பில் உள்ள அமைப்புகள் மற்றும் குறுக்குவழிகளை மாற்ற பயன்பாட்டை அனுமதிக்கிறது." + "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்" + "முகப்பில் உள்ள அமைப்பு மற்றும் குறுக்குவழிகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது." + "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளை எழுதுதல்" + "முகப்பில் உள்ள அமைப்பு மற்றும் குறுக்குவழிகளை மாற்ற பயன்பாட்டை அனுமதிக்கிறது." "விட்ஜெட்டை ஏற்றுவதில் சிக்கல்" + "அமைவு" "இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது." "ராக்கெட் லாஞ்சர்" "பெயரிடப்படாத கோப்புறை" @@ -95,9 +95,12 @@ "ஐகான்களை நகலெடு" "புதிதாகத் தொடங்கு" "இடத்தை ஒழுங்கமைக்கவும்" - "வால்பேப்பர், விட்ஜெட்கள் மற்றும் அமைப்புகளை நிர்வகிப்பதற்கு பின்புலத்தைத் தொட்டுப் & பிடிக்கவும்." + "வால்பேப்பர், விட்ஜெட்கள், அமைப்பை நிர்வகிக்க பின்புலத்தைத் தொட்டுப் பிடிக்கவும்." + "வால்பேப்பர்கள், விட்ஜெட்கள் & அமைப்புகள்" + "தனிப்பயனாக்க, பின்னணியைத் தொட்டுப் பிடிக்கவும்" + "புரிந்தது" "இதோ கோப்புறை" - "இதுபோன்ற ஒன்றை உருவாக்கப் பயன்பாட்டைத் தொட்டுப் & பிடிக்கவும், பிறகு அதை வேறொன்றிற்கு நகர்த்தவும்." + "இதுபோன்ற ஒன்றை உருவாக்க பயன்பாட்டைத் தொட்டுப் பிடிக்கவும், பிறகு அதை வேறொன்றிற்கு நகர்த்தவும்." "சரி" "திறக்கப்பட்டக் கோப்புறை, %1$d x %2$d" "கோப்புறையை மூட, தொடவும்" @@ -105,9 +108,9 @@ "கோப்புறை மூடப்பட்டது" "கோப்புறை %1$s என மறுபெயரிடப்பட்டது" "கோப்புறை: %1$s" - "விட்ஜெட்கள்" + "ஷார்ட்கட்ஸ்" "வால்பேப்பர்கள்" - "அமைப்புகள்" + "அமைப்பு" "காத்திருக்கிறது" "பதிவிறக்குகிறது" "நிறுவுகிறது" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index b48d6b80d..bfbc7920f 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -24,12 +24,13 @@ "Android ప్రధాన అనువర్తనాలు" "అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు." - "సురక్షిత మోడ్‌లో డౌన్‌లోడ్ చేసిన అనువర్తనం నిలిపివేయబడింది" + "అనువర్తనం అందుబాటులో లేదు" + "డౌన్‌లోడ్ చేసిన అనువర్తనం సురక్షిత మోడ్‌లో నిలిపివేయబడింది" + "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" "విడ్జెట్‌లు" "విడ్జెట్‌లు" "మెమరీ చూపు" "విడ్జెట్‌ను ఎంచుకోవడానికి తాకి & నొక్కి పెట్టండి." - "షాపింగ్ చేయి" "%1$d × %2$d" "ఈ హోమ్ స్క్రీన్‌లో అంశాన్ని వదలడం సాధ్యపడలేదు." "సృష్టించాల్సిన విడ్జెట్ ఎంచుకోండి" @@ -57,8 +58,6 @@ "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" "అనువర్తన సమాచారం" - "శోధించు" - "వాయిస్ శోధన" "అనువర్తనాలు" "తీసివేయి" "నవీకరణను అన్‌ఇన్‌స్టాల్ చేయి" @@ -77,6 +76,7 @@ "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను వ్రాయడం" "హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను మార్చడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "విడ్జెట్‌ను లోడ్ చేయడంలో సమస్య" + "సెటప్ చేయి" "ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." "రాకెట్ లాంచర్" "పేరు లేని ఫోల్డర్" @@ -96,6 +96,9 @@ "తాజాగా ప్రారంభించు" "మీ స్థలాన్ని నిర్వహించండి" "వాల్‌., విడ్జె., సెట్టి. నిర్వ. నేపథ్యం తాకి & నొక్కి పెట్టండి." + "వాల్‌పేపర్‌లు, విడ్జెట్‌లు & సెట్టింగ్‌లు" + "అనుకూలీకరించడానికి నేపథ్యాన్ని నొక్కి & ఉంచండి" + "అర్థమైంది" "ఇక్కడ ఫోల్డర్ ఉంది" "ఇలాంటిది సృష్టించడానికి అనువర్తనాన్ని తాకి & నొక్కి పెట్టండి, ఆపై మరోదాని పైన ఉంచండి." "సరే" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index 33b502e9b..ba1d3fa9a 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -24,13 +24,13 @@ "‏Android کور ایپس" "ایپ انسٹال نہیں ہے۔" - - + "ایپ دستیاب نہیں ہے" + "ڈاؤن لوڈ کردہ ایپ کو محفوظ وضع میں غیر فعال کر دیا گیا" + "ویجیٹس کو محفوظ وضع میں غیر فعال کر دیا گیا" "ویجیٹس" "ویجیٹس" "‏Mem دکھائیں" "کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔" - "خریداری کریں" "%1$d × %2$d" "آئٹم کو اس ہوم اسکرین پر ڈراپ نہیں کیا جا سکا۔" "بنانے کیلئے ویجیٹ منتخب کریں" @@ -58,8 +58,6 @@ "ہٹائیں" "اَن انسٹال کریں" "ایپ کی معلومات" - "تلاش کریں" - "صوتی تلاش" "ایپس" "ہٹائیں" "اپ ڈیٹ اَن انسٹال کریں" @@ -78,6 +76,7 @@ "ہوم ترتیبات اور شارٹ کٹس کو لکھیں" "ایپ کو ہوم میں ترتیبات اور شارٹ کٹس کو تبدیل کرنے کی اجازت دیتا ہے۔" "ویجیٹ کو لوڈ کرنے میں مسئلہ" + "ترتیب دیں" "یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔" "راکٹ لانچر" "بلا نام فولڈر" @@ -97,6 +96,9 @@ "نئے سرے سے شروع کریں" "اپنی جگہ کو منظم کریں" "وال پیپر، ویجیٹس اور ترتیبات کا نظم کرنے کیلئے پس منظر کو ٹچ کریں اور پکڑ کر رکھیں۔" + "وال پیپرز، ویجیٹس اور ترتیبات" + "حسب ضرورت بنانے کیلئے پس منظر کو ٹچ کریں اور دبائے رکھیں" + "سمجھ آ گئی" "یہ ہے ایک فولڈر" "اس طرح کا ایک بنانے کیلئے، کسی ایپ کو ٹچ کریں اور پکڑ کر رکھیں، پھر اسے کسی دوسرے میں منتقل کریں۔" "ٹھیک ہے" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index 791f6c888..a3c5670a3 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -24,13 +24,13 @@ "Androidga asoslangan dasturlar" "Ilova o‘rnatilmadi." - - + "Ilova mavjud emas" + "Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi" + "Xavfsiz rejimda vidjetlar o‘chirib qo‘yilgan" "Vidjetlar" "Vidjetlar" "Xotirani ko‘rsatish" "Vidjetni tanlash uchun bosib turing." - "Do‘kon" "%1$d × %2$d" "Elementni ushbu \"Uy\" ekraniga tashlab bo‘lmadi." "Yaratish uchun vidjet tanlang" @@ -58,8 +58,6 @@ "O‘chirish" "O‘chirish" "Ilova ma’lumoti" - "Izlash" - "Ovozli qidiruv" "Ilovalar" "O‘chirish" "Yangilashni o‘chirish" @@ -78,6 +76,7 @@ "Uy sozlamalari va yorliqlarini yozish" "Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalrni o‘zgartirish uchun ruxsat beradi." "Vidjetni yuklashda muammo" + "Sozlash" "Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi." "Rocket Launcher" "Nomsiz jild" @@ -97,6 +96,9 @@ "YANGIDAN BOSHLASH" "Joylaringizni boshqaring" "Fon rasmi, vidjet va sozlamalarni boshqarish uchun orqa fonga bosib turing" + "Orqa fon rasmlari, vidjet va sozlamalar" + "Orqa fonni moslashtirish uchun uni bosing va ushlab turing" + "OK" "Mana sizga jild" "Bunga o‘xshaganini yaratish uchun bosib turing, keyin boshqasiga o‘ting." "OK" -- cgit v1.2.3 From 3672c18614e5bb827d2318587e7fd50069828986 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 3 Dec 2014 11:00:59 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I417c78f889994b02bff33793c866af0a1b13d47f Auto-generated-cl: translation import --- res/values-eu-rES/strings.xml | 2 +- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 ++-- res/values-my-rMM/strings.xml | 8 ++++---- res/values-sk/strings.xml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 5b50e828c..e02479c0e 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -86,7 +86,7 @@ "%1$d/%2$d aplikazio-orria" "%1$d/%2$d widget-orria" "Ongi etorri" - "Senti zaitez etxean bezala." + "Pertsonalizatu hasierako pantaila nahieran." "Sortu pantaila gehiago aplikazioak eta karpetak ezartzeko" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 3a1c82dcc..670ac70b5 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -85,7 +85,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍" + "សូម​ស្វាគមន៍​" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 65f01b7f0..1beb049e2 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ" + "ຍົກ​ເລີກ​" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -117,7 +117,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ" + "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 668803587..544ecdc7a 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -60,7 +60,7 @@ "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +76,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +102,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +111,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 9cd0fbe13..3f41f2d7e 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -93,7 +93,7 @@ "Kopírovanie ikon aplikácií" "Chcete importovať ikony a priečinky zo starých plôch?" "SKOPÍROVAŤ IKONY" - "ZAČAŤ S PREDVOLENÝM ROZLOŽENÍM" + "ZAČAŤ ODZNOVA" "Usporiadajte svoj priestor" "Ak chcete spravovať tapetu, miniaplikácie a nastavenia, dotknite sa pozadia a podržte." "Pozadia, miniaplikácie a nastavenia" -- cgit v1.2.3 From 5940042d39b576553c2499bcf3d0641281e6ad52 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 5 Mar 2014 18:07:04 -0800 Subject: Supporting custom widgets provided by launcher -> This change provides integration for widgets provided by the launcher package which can run arbitrary code. Change-Id: I6052da5c4afed7ee72e3b44d045b9c46f2d84c42 --- .gitignore | 4 + res/layout/dummy_widget.xml | 35 ++++++ res/values/dimens.xml | 2 + .../android/launcher3/AppWidgetResizeFrame.java | 24 ++-- .../android/launcher3/AppsCustomizePagedView.java | 55 +++++---- src/com/android/launcher3/CellLayout.java | 14 ++- src/com/android/launcher3/CustomAppWidget.java | 14 +++ src/com/android/launcher3/DeleteDropTarget.java | 6 +- src/com/android/launcher3/DeviceProfile.java | 25 ++-- src/com/android/launcher3/DummyWidget.java | 50 ++++++++ src/com/android/launcher3/Launcher.java | 136 +++++++++------------ .../android/launcher3/LauncherAppWidgetHost.java | 27 ++++ .../launcher3/LauncherAppWidgetHostView.java | 17 ++- .../android/launcher3/LauncherAppWidgetInfo.java | 16 ++- .../launcher3/LauncherAppWidgetProviderInfo.java | 89 ++++++++++++++ .../android/launcher3/LauncherBackupHelper.java | 16 +-- src/com/android/launcher3/LauncherModel.java | 123 ++++++++++++------- src/com/android/launcher3/LauncherSettings.java | 5 + src/com/android/launcher3/PagedViewWidget.java | 8 +- src/com/android/launcher3/PendingAddItemInfo.java | 31 ++--- .../launcher3/ShortcutAndWidgetContainer.java | 9 ++ src/com/android/launcher3/WidgetPreviewLoader.java | 18 ++- src/com/android/launcher3/Workspace.java | 26 ++-- .../launcher3/compat/AppWidgetManagerCompat.java | 9 +- .../compat/AppWidgetManagerCompatV16.java | 9 +- .../launcher3/compat/AppWidgetManagerCompatVL.java | 18 +-- 26 files changed, 555 insertions(+), 231 deletions(-) create mode 100644 res/layout/dummy_widget.xml create mode 100644 src/com/android/launcher3/CustomAppWidget.java create mode 100644 src/com/android/launcher3/DummyWidget.java create mode 100644 src/com/android/launcher3/LauncherAppWidgetProviderInfo.java diff --git a/.gitignore b/.gitignore index f830c66eb..fc1b944b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ db_files +*.iml +gen/ +tests/stress/gen/ +WallpaperPicker/gen/ diff --git a/res/layout/dummy_widget.xml b/res/layout/dummy_widget.xml new file mode 100644 index 000000000..a0fa8fc3e --- /dev/null +++ b/res/layout/dummy_widget.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index f12cb57ba..54689ec25 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -28,6 +28,8 @@ 80dp 20dp + 8dp + 240dp 165dp diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index f57f4d036..240250750 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -8,6 +8,7 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; +import android.content.res.Resources; import android.graphics.Rect; import android.view.Gravity; import android.widget.FrameLayout; @@ -78,13 +79,13 @@ public class AppWidgetResizeFrame extends FrameLayout { mLauncher = (Launcher) context; mCellLayout = cellLayout; mWidgetView = widgetView; - mResizeMode = widgetView.getAppWidgetInfo().resizeMode; + LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) + widgetView.getAppWidgetInfo(); + mResizeMode = info.resizeMode; mDragLayer = dragLayer; - final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo(); - int[] result = Launcher.getMinSpanForWidget(mLauncher, info); - mMinHSpan = result[0]; - mMinVSpan = result[1]; + mMinHSpan = info.minSpanX; + mMinVSpan = info.minSpanY; setBackgroundResource(R.drawable.widget_resize_frame_holo); setPadding(0, 0, 0, 0); @@ -114,8 +115,16 @@ public class AppWidgetResizeFrame extends FrameLayout { Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); addView(mBottomHandle, lp); - Rect p = AppWidgetHostView.getDefaultPaddingForWidget(context, - widgetView.getAppWidgetInfo().provider, null); + Rect p = new Rect(0, 0, 0, 0); + if (!info.isCustomWidget) { + p = AppWidgetHostView.getDefaultPaddingForWidget(context, + widgetView.getAppWidgetInfo().provider, null); + } else { + Resources r = context.getResources(); + int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding); + p.set(padding, padding, padding, padding); + } + mWidgetPaddingLeft = p.left; mWidgetPaddingTop = p.top; mWidgetPaddingRight = p.right; @@ -335,7 +344,6 @@ public class AppWidgetResizeFrame extends FrameLayout { static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher, int spanX, int spanY) { - getWidgetSizeRanges(launcher, spanX, spanY, mTmpRect); widgetView.updateAppWidgetSize(null, mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom); diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index c8187f068..9e7e523e0 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -383,12 +383,13 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Get the list of widgets and shortcuts mWidgets.clear(); for (Object o : widgetsAndShortcuts) { - if (o instanceof AppWidgetProviderInfo) { - AppWidgetProviderInfo widget = (AppWidgetProviderInfo) o; - if (!app.shouldShowAppOrWidgetProvider(widget.provider)) { + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo widget = (LauncherAppWidgetProviderInfo) o; + if (!app.shouldShowAppOrWidgetProvider(widget.provider) && !widget.isCustomWidget) { continue; } - if (widget.minWidth > 0 && widget.minHeight > 0) { + + if (widget.minSpanX > 0 && widget.minSpanY > 0) { // Ensure that all widgets we show can be added on a workspace of this size int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget); int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget); @@ -410,6 +411,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mWidgets.add(o); } } + updatePageCountsAndInvalidateData(); } @@ -503,8 +505,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } private void preloadWidget(final PendingAddWidgetInfo info) { - final AppWidgetProviderInfo pInfo = info.info; - final Bundle options = getDefaultOptionsForWidget(mLauncher, info); + final LauncherAppWidgetProviderInfo pInfo = info.info; + final Bundle options = pInfo.isCustomWidget ? null : + getDefaultOptionsForWidget(mLauncher, info); if (pInfo.configure != null) { info.bindOptions = options; @@ -515,11 +518,17 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mBindWidgetRunnable = new Runnable() { @Override public void run() { + if (pInfo.isCustomWidget) { + mWidgetCleanupState = WIDGET_BOUND; + return; + } + mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( mWidgetLoadingId, pInfo, options)) { mWidgetCleanupState = WIDGET_BOUND; } + } }; post(mBindWidgetRunnable); @@ -530,8 +539,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen if (mWidgetCleanupState != WIDGET_BOUND) { return; } - AppWidgetHostView hostView = mLauncher. - getAppWidgetHost().createView(getContext(), mWidgetLoadingId, pInfo); + AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( + getContext(), mWidgetLoadingId, pInfo); info.boundWidget = hostView; mWidgetCleanupState = WIDGET_INFLATED; hostView.setVisibility(INVISIBLE); @@ -575,7 +584,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen removeCallbacks(mInflateWidgetRunnable); } else if (mWidgetCleanupState == WIDGET_BOUND) { // Delete the widget id which was allocated - if (mWidgetLoadingId != -1) { + if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); } @@ -583,7 +592,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen removeCallbacks(mInflateWidgetRunnable); } else if (mWidgetCleanupState == WIDGET_INFLATED) { // Delete the widget id which was allocated - if (mWidgetLoadingId != -1) { + if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); } @@ -645,7 +654,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]); int[] previewSizeBeforeScale = new int[1]; - preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale); @@ -1125,20 +1133,13 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen PendingAddItemInfo createItemInfo = null; PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( R.layout.apps_customize_widget, layout, false); - if (rawInfo instanceof AppWidgetProviderInfo) { + + if (rawInfo instanceof LauncherAppWidgetProviderInfo) { // Fill in the widget information - AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; - createItemInfo = new PendingAddWidgetInfo(info, null, null); - - // Determine the widget spans and min resize spans. - int[] spanXY = Launcher.getSpanForWidget(mLauncher, info); - createItemInfo.spanX = spanXY[0]; - createItemInfo.spanY = spanXY[1]; - int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, info); - createItemInfo.minSpanX = minSpanXY[0]; - createItemInfo.minSpanY = minSpanXY[1]; - - widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, getWidgetPreviewLoader()); + LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) rawInfo; + createItemInfo = new PendingAddWidgetInfo(info, null); + + widget.applyFromAppWidgetProviderInfo(info, -1, getWidgetPreviewLoader()); widget.setTag(createItemInfo); widget.setShortPressListener(this); } else if (rawInfo instanceof ResolveInfo) { @@ -1151,6 +1152,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader()); widget.setTag(createItemInfo); } + widget.setOnClickListener(this); widget.setOnLongClickListener(this); widget.setOnTouchListener(this); @@ -1421,6 +1423,11 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen updatePageCountsAndInvalidateData(); } } + + public ArrayList getApps() { + return mApps; + } + private void addAppsWithoutInvalidate(ArrayList list) { // We add it in place, in alphabetical order int count = list.size(); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 0ff1ef4ad..7424d61c1 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -592,7 +592,7 @@ public class CellLayout extends ViewGroup { } public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params, - boolean markCells) { + boolean markCells, boolean inLayout) { final LayoutParams lp = params; // Hotseat icons - remove text @@ -613,8 +613,11 @@ public class CellLayout extends ViewGroup { if (lp.cellVSpan < 0) lp.cellVSpan = mCountY; child.setId(childId); - - mShortcutsAndWidgets.addView(child, index, lp); + if (inLayout) { + mShortcutsAndWidgets.addView(child, index, lp, true); + } else { + mShortcutsAndWidgets.addView(child, index, lp, false); + } if (markCells) markCellsAsOccupiedForView(child); @@ -623,6 +626,11 @@ public class CellLayout extends ViewGroup { return false; } + public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params, + boolean markCells) { + return addViewToCellLayout(child, index, childId, params, markCells, false); + } + @Override public void removeAllViews() { clearOccupiedCells(); diff --git a/src/com/android/launcher3/CustomAppWidget.java b/src/com/android/launcher3/CustomAppWidget.java new file mode 100644 index 000000000..1b4ed79c0 --- /dev/null +++ b/src/com/android/launcher3/CustomAppWidget.java @@ -0,0 +1,14 @@ +package com.android.launcher3; + +public interface CustomAppWidget { + public String getLabel(); + public int getPreviewImage(); + public int getIcon(); + public int getWidgetLayout(); + + public int getSpanX(); + public int getSpanY(); + public int getMinSpanX(); + public int getMinSpanY(); + public int getResizeMode(); +} diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index ea058ea71..5a5c0027e 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -105,6 +105,7 @@ public class DeleteDropTarget extends ButtonDropTarget { switch (addInfo.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: return true; } } @@ -146,6 +147,7 @@ public class DeleteDropTarget extends ButtonDropTarget { if (info instanceof ItemInfo) { ItemInfo item = (ItemInfo) info; if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET || + item.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { return true; } @@ -341,7 +343,9 @@ public class DeleteDropTarget extends ButtonDropTarget { final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item; final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost(); - if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) { + + if (appWidgetHost != null && !launcherAppWidgetInfo.isCustomWidget() + && launcherAppWidgetInfo.isWidgetIdValid()) { // Deleting an app widget ID is a void call but writes to disk before returning // to the caller... new AsyncTask() { diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index d6aadcee1..34e1f3c5f 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -67,8 +67,8 @@ public class DeviceProfile { String name; float minWidthDps; float minHeightDps; - float numRows; - float numColumns; + public float numRows; + public float numColumns; float numHotseatIcons; float iconSize; private float iconTextSize; @@ -84,13 +84,13 @@ public class DeviceProfile { boolean transposeLayoutWithOrientation; int desiredWorkspaceLeftRightMarginPx; - int edgeMarginPx; + public int edgeMarginPx; Rect defaultWidgetPadding; int widthPx; int heightPx; - int availableWidthPx; - int availableHeightPx; + public int availableWidthPx; + public int availableHeightPx; int defaultPageSpacingPx; int overviewModeMinIconZoneHeightPx; @@ -100,11 +100,12 @@ public class DeviceProfile { float overviewModeIconZoneRatio; float overviewModeScaleFactor; + public int cellWidthPx; + public int cellHeightPx; + int iconSizePx; int iconTextSizePx; int iconDrawablePaddingPx; - int cellWidthPx; - int cellHeightPx; int allAppsIconSizePx; int allAppsIconTextSizePx; int allAppsCellWidthPx; @@ -578,6 +579,16 @@ public class DeviceProfile { } } + public int getWorkspaceGridHeight() { + Rect p = getWorkspacePadding(); + return availableHeightPx - p.top - p.bottom; + } + + public int getWorkspaceGridWidth() { + Rect p = getWorkspacePadding(); + return availableWidthPx - p.left - p.right; + } + /** Returns the workspace padding in the specified orientation */ Rect getWorkspacePadding() { return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); diff --git a/src/com/android/launcher3/DummyWidget.java b/src/com/android/launcher3/DummyWidget.java new file mode 100644 index 000000000..59cd80501 --- /dev/null +++ b/src/com/android/launcher3/DummyWidget.java @@ -0,0 +1,50 @@ +package com.android.launcher3; + +import android.appwidget.AppWidgetProviderInfo; + +public class DummyWidget implements CustomAppWidget { + @Override + public String getLabel() { + return "Dumb Launcher Widget"; + } + + @Override + public int getPreviewImage() { + return 0; + } + + @Override + public int getIcon() { + return 0; + } + + @Override + public int getWidgetLayout() { + return R.layout.dummy_widget; + } + + @Override + public int getSpanX() { + return 2; + } + + @Override + public int getSpanY() { + return 2; + } + + @Override + public int getMinSpanX() { + return 1; + } + + @Override + public int getMinSpanY() { + return 1; + } + + @Override + public int getResizeMode() { + return AppWidgetProviderInfo.RESIZE_BOTH; + } +} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ac46fd33d..0ceb862a7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -260,7 +260,7 @@ public class Launcher extends Activity private LauncherAppWidgetHost mAppWidgetHost; private ItemInfo mPendingAddInfo = new ItemInfo(); - private AppWidgetProviderInfo mPendingAddWidgetInfo; + private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo; private int mPendingAddWidgetId = -1; private int[] mTmpAddItemCellCoordinates = new int[2]; @@ -351,6 +351,16 @@ public class Launcher extends Activity private BubbleTextView mWaitingForResume; + protected static HashMap sCustomAppWidgets = + new HashMap(); + + private static final boolean ENABLE_CUSTOM_WIDGET_TEST = false; + static { + if (ENABLE_CUSTOM_WIDGET_TEST) { + sCustomAppWidgets.put(DummyWidget.class.getName(), new DummyWidget()); + } + } + private Runnable mBuildLayersRunnable = new Runnable() { public void run() { if (mWorkspace != null) { @@ -760,7 +770,7 @@ public class Launcher extends Activity mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_OK) { - addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, + addAppWidgetImpl(appWidgetId, (PendingAddWidgetInfo) mPendingAddInfo, null, mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); } return; @@ -1487,7 +1497,7 @@ public class Launcher extends Activity * * @return A View inflated from layoutResId. */ - View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { + public View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false); favorite.applyFromShortcutInfo(info, mIconCache, true); favorite.setOnClickListener(this); @@ -1586,86 +1596,45 @@ public class Launcher extends Activity * @param appWidgetId The app widget id * @param cellInfo The position on screen where to create the widget. */ - private void completeAddAppWidget(final int appWidgetId, long container, long screenId, - AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) { - if (appWidgetInfo == null) { - appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); - } - - // Calculate the grid spans needed to fit this widget - CellLayout layout = getCellLayout(container, screenId); - - int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo); - int[] spanXY = getSpanForWidget(this, appWidgetInfo); + private void completeAddAppWidget(int appWidgetId, long container, long screenId, + AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) { - // Try finding open space on Launcher screen - // We have saved the position to which the widget was dragged-- this really only matters - // if we are placing widgets on a "spring-loaded" screen - int[] cellXY = mTmpAddItemCellCoordinates; - int[] touchXY = mPendingAddInfo.dropPos; - int[] finalSpan = new int[2]; - boolean foundCellSpan = false; - if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) { - cellXY[0] = mPendingAddInfo.cellX; - cellXY[1] = mPendingAddInfo.cellY; - spanXY[0] = mPendingAddInfo.spanX; - spanXY[1] = mPendingAddInfo.spanY; - foundCellSpan = true; - } else if (touchXY != null) { - // when dragging and dropping, just find the closest free spot - int[] result = layout.findNearestVacantArea( - touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0], - spanXY[1], cellXY, finalSpan); - spanXY[0] = finalSpan[0]; - spanXY[1] = finalSpan[1]; - foundCellSpan = (result != null); - } else { - foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]); + ItemInfo info = mPendingAddInfo; + if (appWidgetInfo == null) { + appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this, + mAppWidgetManager.getAppWidgetInfo(appWidgetId)); } - if (!foundCellSpan) { - if (appWidgetId != -1) { - // Deleting an app widget ID is a void call but writes to disk before returning - // to the caller... - new AsyncTask() { - public Void doInBackground(Void ... args) { - mAppWidgetHost.deleteAppWidgetId(appWidgetId); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); - } - showOutOfSpaceMessage(isHotseatLayout(layout)); - return; + if (appWidgetInfo.isCustomWidget) { + appWidgetId = LauncherAppWidgetInfo.CUSTOM_WIDGET_ID; } - // Build Launcher-specific widget info and save to database - LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId, - appWidgetInfo.provider); - launcherInfo.spanX = spanXY[0]; - launcherInfo.spanY = spanXY[1]; - launcherInfo.minSpanX = mPendingAddInfo.minSpanX; - launcherInfo.minSpanY = mPendingAddInfo.minSpanY; + LauncherAppWidgetInfo launcherInfo; + launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider); + launcherInfo.spanX = info.spanX; + launcherInfo.spanY = info.spanY; + launcherInfo.minSpanX = info.minSpanX; + launcherInfo.minSpanY = info.minSpanY; launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo); LauncherModel.addItemToDatabase(this, launcherInfo, - container, screenId, cellXY[0], cellXY[1], false); + container, screenId, info.cellX, info.cellY, false); if (!mRestoring) { if (hostView == null) { // Perform actual inflation because we're live - launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); - launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); + launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, + appWidgetInfo); } else { // The AppWidgetHostView has already been inflated and instantiated launcherInfo.hostView = hostView; } - launcherInfo.hostView.setTag(launcherInfo); launcherInfo.hostView.setVisibility(View.VISIBLE); launcherInfo.notifyWidgetSizeChanged(this); - mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, cellXY[0], cellXY[1], - launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); + mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, info.cellX, + info.cellY, launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo); } @@ -1896,6 +1865,10 @@ public class Launcher extends Activity Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); } + public ArrayList getAllAppsList() { + return mAppsCustomizeContent.getApps(); + } + public DragLayer getDragLayer() { return mDragLayer; } @@ -2284,14 +2257,14 @@ public class Launcher extends Activity mPendingAddInfo.dropPos = null; } - void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, - final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) { + void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, final + AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) { addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0); } - void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, - final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int - delay) { + void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, + final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo, + int delay) { if (appWidgetInfo.configure != null) { mPendingAddWidgetInfo = appWidgetInfo; mPendingAddWidgetId = appWidgetId; @@ -2585,7 +2558,8 @@ public class Launcher extends Activity int widgetId = info.appWidgetId; AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId); if (appWidgetInfo != null) { - mPendingAddWidgetInfo = appWidgetInfo; + mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo( + this, appWidgetInfo); mPendingAddInfo.copyFrom(info); mPendingAddWidgetId = widgetId; @@ -4373,12 +4347,12 @@ public class Launcher extends Activity } final Workspace workspace = mWorkspace; - AppWidgetProviderInfo appWidgetInfo; + LauncherAppWidgetProviderInfo appWidgetInfo = + LauncherModel.getProviderInfo(this, item.providerName); + if (!mIsSafeModeEnabled && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) { - - appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName); if (appWidgetInfo == null) { if (DEBUG_WIDGETS) { Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId @@ -4391,7 +4365,7 @@ public class Launcher extends Activity // Note: This assumes that the id remap broadcast is received before this step. // If that is not the case, the id remap will be ignored and user may see the // click to setup view. - PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null); + PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null); pendingInfo.spanX = item.spanX; pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; @@ -4428,9 +4402,9 @@ public class Launcher extends Activity if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { final int appWidgetId = item.appWidgetId; - appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); if (DEBUG_WIDGETS) { - Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider); + Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + + appWidgetInfo.provider); } item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); @@ -4449,7 +4423,9 @@ public class Launcher extends Activity workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX, item.cellY, item.spanX, item.spanY, false); - addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo); + if (!item.isCustomWidget()) { + addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo); + } workspace.requestLayout(); @@ -5125,6 +5101,14 @@ public class Launcher extends Activity } } + public static CustomAppWidget getCustomAppWidget(String name) { + return sCustomAppWidgets.get(name); + } + + public static HashMap getCustomAppWidgets() { + return sCustomAppWidgets; + } + public void dumpLogsToLocalData() { if (DEBUG_DUMP_LOG) { new AsyncTask() { diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index a309f268c..840508a1d 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -21,9 +21,11 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.os.TransactionTooLargeException; +import android.view.LayoutInflater; import java.util.ArrayList; + /** * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView} * which correctly captures all long-press events. This ensures that users can @@ -85,4 +87,29 @@ public class LauncherAppWidgetHost extends AppWidgetHost { callback.run(); } } + + public AppWidgetHostView createView(Context context, int appWidgetId, + LauncherAppWidgetProviderInfo appWidget) { + if (appWidget.isCustomWidget) { + LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context); + LayoutInflater inflater = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(appWidget.initialLayout, lahv); + lahv.setAppWidget(0, appWidget); + lahv.updateLastInflationOrientation(); + return lahv; + } else { + return super.createView(context, appWidgetId, appWidget); + } + } + + /** + * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. + */ + @Override + protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { + LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo( + mLauncher, appWidget); + super.onProviderChanged(appWidgetId, info); + } } diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index e39727b17..ebe55ab78 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -54,10 +55,14 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc return mInflater.inflate(R.layout.appwidget_error, this, false); } + public void updateLastInflationOrientation() { + mPreviousOrientation = mContext.getResources().getConfiguration().orientation; + } + @Override public void updateAppWidget(RemoteViews remoteViews) { // Store the orientation in which the widget was inflated - mPreviousOrientation = mContext.getResources().getConfiguration().orientation; + updateLastInflationOrientation(); super.updateAppWidget(remoteViews); } @@ -136,6 +141,16 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc mLongPressHelper.cancelLongPress(); } + @Override + public AppWidgetProviderInfo getAppWidgetInfo() { + AppWidgetProviderInfo info = super.getAppWidgetInfo(); + if (!(info instanceof LauncherAppWidgetProviderInfo)) { + throw new IllegalStateException("Launcher widget must have" + + "LauncherAppWidgetProviderInfo"); + } + return info; + } + @Override public void onTouchComplete() { if (!mLongPressHelper.hasPerformedLongPress()) { diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index 5c6535a24..aad18b578 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -55,6 +55,11 @@ public class LauncherAppWidgetInfo extends ItemInfo { */ static final int NO_ID = -1; + /** + * Indicates that this is a locally defined widget and hence has no system allocated id. + */ + static final int CUSTOM_WIDGET_ID = -100; + /** * Identifier for this widget when talking with * {@link android.appwidget.AppWidgetManager} for updates. @@ -86,7 +91,12 @@ public class LauncherAppWidgetInfo extends ItemInfo { AppWidgetHostView hostView = null; LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) { - itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + if (appWidgetId == CUSTOM_WIDGET_ID) { + itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + } else { + itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + } + this.appWidgetId = appWidgetId; this.providerName = providerName; @@ -99,6 +109,10 @@ public class LauncherAppWidgetInfo extends ItemInfo { restoreStatus = RESTORE_COMPLETED; } + public boolean isCustomWidget() { + return appWidgetId == CUSTOM_WIDGET_ID; + } + @Override void onAddToDatabase(Context context, ContentValues values) { super.onAddToDatabase(context, values); diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java new file mode 100644 index 000000000..e7f49b2ce --- /dev/null +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -0,0 +1,89 @@ +package com.android.launcher3; + +import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Parcel; + +import java.lang.reflect.Field; + +/** + * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords + * a common object for describing both framework provided AppWidgets as well as custom widgets + * (who's implementation is owned by the launcher). This object represents a widget type / class, + * as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo} + */ +public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { + + public boolean isCustomWidget = false; + int spanX = -1; + int spanY = -1; + int minSpanX = -1; + int minSpanY = -1; + + public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context, + AppWidgetProviderInfo info) { + + // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo + // into a parcel, and then construct a new LauncherAppWidgetProvider info from the + // associated super parcel constructor. This allows us to copy non-public members without + // using reflection. + Parcel p = Parcel.obtain(); + info.writeToParcel(p, 0); + p.setDataPosition(0); + LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p); + + int[] minResizeSpan = Launcher.getMinSpanForWidget(context, lawpi); + int[] span = Launcher.getSpanForWidget(context, lawpi); + + lawpi.spanX = span[0]; + lawpi.spanY = span[1]; + lawpi.minSpanX = minResizeSpan[0]; + lawpi.minSpanY = minResizeSpan[1]; + + return lawpi; + } + + public LauncherAppWidgetProviderInfo(Parcel in) { + super(in); + } + + public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) { + isCustomWidget = true; + + provider = new ComponentName(context, widget.getClass().getName()); + icon = widget.getIcon(); + label = widget.getLabel(); + previewImage = widget.getPreviewImage(); + initialLayout = widget.getWidgetLayout(); + resizeMode = widget.getResizeMode(); + + spanX = widget.getSpanX(); + spanY = widget.getSpanY(); + minSpanX = widget.getMinSpanX(); + minSpanY = widget.getMinSpanY(); + } + + public String getLabel(PackageManager packageManager) { + if (isCustomWidget) { + return label.toString().trim(); + } + return super.loadLabel(packageManager); + } + + public Drawable getIcon(Context context, IconCache cache) { + if (isCustomWidget) { + return cache.getFullResIcon(provider.getPackageName(), icon); + } + return super.loadIcon(context, cache.getFullResIconDpi()); + } + + public String toString() { + if (isCustomWidget) { + return "LauncherAppWidgetProviderInfo(" + provider + ")"; + } + return super.toString(); + } + } diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index c260fbc33..7f3a7985e 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -906,7 +906,8 @@ public class LauncherBackupHelper implements BackupHelper { /** Serialize a widget for persistence, including a checksum wrapper. */ private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache, ComponentName provider) { - final AppWidgetProviderInfo info = findAppWidgetProviderInfo(provider); + final LauncherAppWidgetProviderInfo info = + LauncherModel.getProviderInfo(mContext, provider); Widget widget = new Widget(); widget.provider = provider.flattenToShortString(); widget.label = info.label; @@ -1074,19 +1075,6 @@ public class LauncherBackupHelper implements BackupHelper { return wrapper.payload; } - private AppWidgetProviderInfo findAppWidgetProviderInfo(ComponentName component) { - if (mWidgetMap == null) { - List widgets = - AppWidgetManager.getInstance(mContext).getInstalledProviders(); - mWidgetMap = new HashMap(widgets.size()); - for (AppWidgetProviderInfo info : widgets) { - mWidgetMap.put(info.provider, info); - } - } - return mWidgetMap.get(component); - } - - private boolean initializeIconCache() { if (mIconCache != null) { return true; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 954887dc3..3c1cf485d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -17,7 +17,6 @@ package com.android.launcher3; import android.app.SearchManager; -import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -174,6 +173,10 @@ public class LauncherModel extends BroadcastReceiver // sBgWorkspaceScreens is the ordered set of workspace screens. static final ArrayList sBgWorkspaceScreens = new ArrayList(); + // sBgWidgetProviders is the set of widget providers including custom internal widgets + public static HashMap sBgWidgetProviders; + public static boolean sWidgetProvidersDirty; + // sPendingPackages is a set of packages which could be on sdcard and are not available yet static final HashMap> sPendingPackages = new HashMap>(); @@ -1265,7 +1268,6 @@ public class LauncherModel extends BroadcastReceiver PackageUpdatedTask.OP_UNAVAILABLE, packageNames, user)); } - } /** @@ -1837,7 +1839,6 @@ public class LauncherModel extends BroadcastReceiver final Context context = mContext; final ContentResolver contentResolver = context.getContentResolver(); final PackageManager manager = context.getPackageManager(); - final AppWidgetManager widgets = AppWidgetManager.getInstance(context); final boolean isSafeMode = manager.isSafeMode(); final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); final boolean isSdCardReady = context.registerReceiver(null, @@ -2174,7 +2175,11 @@ public class LauncherModel extends BroadcastReceiver break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: // Read all Launcher-specific widget details + boolean customWidget = itemType == + LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + int appWidgetId = c.getInt(appWidgetIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); id = c.getLong(idIndex); @@ -2188,14 +2193,16 @@ public class LauncherModel extends BroadcastReceiver final boolean wasProviderReady = (restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0; - final AppWidgetProviderInfo provider = isIdValid - ? widgets.getAppWidgetInfo(appWidgetId) - : findAppWidgetProviderInfoWithComponent(context, component); + final LauncherAppWidgetProviderInfo provider = + LauncherModel.getProviderInfo(context, + ComponentName.unflattenFromString(savedProvider)); final boolean isProviderReady = isValidProvider(provider); - if (!isSafeMode && wasProviderReady && !isProviderReady) { + if (!isSafeMode && !customWidget && + wasProviderReady && !isProviderReady) { String log = "Deleting widget that isn't installed anymore: " + "id=" + id + " appWidgetId=" + appWidgetId; + Log.e(TAG, log); Launcher.addDumpLog(TAG, log, false); itemsToRemove.add(id); @@ -2203,10 +2210,13 @@ public class LauncherModel extends BroadcastReceiver if (isProviderReady) { appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, provider.provider); - int[] minSpan = - Launcher.getMinSpanForWidget(context, provider); - appWidgetInfo.minSpanX = minSpan[0]; - appWidgetInfo.minSpanY = minSpan[1]; + + if (!customWidget) { + int[] minSpan = + Launcher.getMinSpanForWidget(context, provider); + appWidgetInfo.minSpanX = minSpan[0]; + appWidgetInfo.minSpanY = minSpan[1]; + } int status = restoreStatus; if (!wasProviderReady) { @@ -2251,6 +2261,12 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo.spanX = c.getInt(spanXIndex); appWidgetInfo.spanY = c.getInt(spanYIndex); + if (!customWidget) { + int[] minSpan = Launcher.getMinSpanForWidget(context, provider); + appWidgetInfo.minSpanX = minSpan[0]; + appWidgetInfo.minSpanY = minSpan[1]; + } + container = c.getInt(containerIndex); if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP && container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) { @@ -2266,17 +2282,21 @@ public class LauncherModel extends BroadcastReceiver break; } - String providerName = appWidgetInfo.providerName.flattenToString(); - if (!providerName.equals(savedProvider) || - (appWidgetInfo.restoreStatus != restoreStatus)) { - ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, - providerName); - values.put(LauncherSettings.Favorites.RESTORED, - appWidgetInfo.restoreStatus); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); + if (!customWidget) { + String providerName = + appWidgetInfo.providerName.flattenToString(); + if (!providerName.equals(savedProvider) || + (appWidgetInfo.restoreStatus != restoreStatus)) { + ContentValues values = new ContentValues(); + values.put( + LauncherSettings.Favorites.APPWIDGET_PROVIDER, + providerName); + values.put(LauncherSettings.Favorites.RESTORED, + appWidgetInfo.restoreStatus); + String where = BaseColumns._ID + "= ?"; + String[] args = {Long.toString(id)}; + contentResolver.update(contentUri, values, where, args); + } } sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo); sBgAppWidgets.add(appWidgetInfo); @@ -3292,12 +3312,44 @@ public class LauncherModel extends BroadcastReceiver } } + public static List getWidgetProviders(Context context) { + synchronized (sBgLock) { + if (sBgWidgetProviders != null && !sWidgetProvidersDirty) { + return new ArrayList(sBgWidgetProviders.values()); + } + sBgWidgetProviders = new HashMap(); + List widgets = + AppWidgetManagerCompat.getInstance(context).getAllProviders(); + LauncherAppWidgetProviderInfo info; + for (AppWidgetProviderInfo pInfo : widgets) { + info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); + sBgWidgetProviders.put(info.provider, info); + } + + Collection customWidgets = Launcher.getCustomAppWidgets().values(); + for (CustomAppWidget widget : customWidgets) { + info = new LauncherAppWidgetProviderInfo(context, widget); + sBgWidgetProviders.put(info.provider, info); + } + sWidgetProvidersDirty = false; + return new ArrayList(sBgWidgetProviders.values()); + } + } + + public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name) { + synchronized (sBgLock) { + if (sBgWidgetProviders == null) { + getWidgetProviders(ctx); + } + return sBgWidgetProviders.get(name); + } + } + // Returns a list of ResolveInfos/AppWindowInfos in sorted order public static ArrayList getSortedWidgetsAndShortcuts(Context context) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); - widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders()); - + widgetsAndShortcuts.addAll(getWidgetProviders(context)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context)); @@ -3583,21 +3635,6 @@ public class LauncherModel extends BroadcastReceiver } } - /** - * Attempts to find an AppWidgetProviderInfo that matches the given component. - */ - static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context, - ComponentName component) { - List widgets = - AppWidgetManager.getInstance(context).getInstalledProviders(); - for (AppWidgetProviderInfo info : widgets) { - if (info.provider.equals(component)) { - return info; - } - } - return null; - } - ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); @@ -3783,16 +3820,16 @@ public class LauncherModel extends BroadcastReceiver if (mLabelCache.containsKey(a)) { labelA = mLabelCache.get(a); } else { - labelA = (a instanceof AppWidgetProviderInfo) - ? mManager.loadLabel((AppWidgetProviderInfo) a) + labelA = (a instanceof LauncherAppWidgetProviderInfo) + ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a) : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim(); mLabelCache.put(a, labelA); } if (mLabelCache.containsKey(b)) { labelB = mLabelCache.get(b); } else { - labelB = (b instanceof AppWidgetProviderInfo) - ? mManager.loadLabel((AppWidgetProviderInfo) b) + labelB = (b instanceof LauncherAppWidgetProviderInfo) + ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a) : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim(); mLabelCache.put(b, labelB); } diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 355370283..65e3d2848 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -238,6 +238,11 @@ class LauncherSettings { */ static final int ITEM_TYPE_APPWIDGET = 4; + /** + * The favorite is a custom widget provided by the launcher + */ + static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5; + /** * The favorite is a clock */ diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java index e6e11a312..41270e3be 100644 --- a/src/com/android/launcher3/PagedViewWidget.java +++ b/src/com/android/launcher3/PagedViewWidget.java @@ -117,8 +117,8 @@ public class PagedViewWidget extends LinearLayout { } } - public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info, - int maxWidth, int[] cellSpan, WidgetPreviewLoader loader) { + public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, + int maxWidth, WidgetPreviewLoader loader) { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -132,8 +132,8 @@ public class PagedViewWidget extends LinearLayout { name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); final TextView dims = (TextView) findViewById(R.id.widget_dims); if (dims != null) { - int hSpan = Math.min(cellSpan[0], (int) grid.numColumns); - int vSpan = Math.min(cellSpan[1], (int) grid.numRows); + int hSpan = Math.min(info.spanX, (int) grid.numColumns); + int vSpan = Math.min(info.spanY, (int) grid.numRows); dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); } mWidgetPreviewLoader = loader; diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java index 967cc928e..ac54a262f 100644 --- a/src/com/android/launcher3/PendingAddItemInfo.java +++ b/src/com/android/launcher3/PendingAddItemInfo.java @@ -17,7 +17,6 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.os.Bundle; @@ -54,17 +53,16 @@ class PendingAddWidgetInfo extends PendingAddItemInfo { int minResizeHeight; int previewImage; int icon; - AppWidgetProviderInfo info; + LauncherAppWidgetProviderInfo info; AppWidgetHostView boundWidget; Bundle bindOptions = null; - // Any configuration data that we want to pass to a configuration activity when - // starting up a widget - String mimeType; - Parcelable configurationData; - - public PendingAddWidgetInfo(AppWidgetProviderInfo i, String dataMimeType, Parcelable data) { - itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) { + if (i.isCustomWidget) { + itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + } else { + itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + } this.info = i; componentName = i.provider; minWidth = i.minWidth; @@ -73,10 +71,15 @@ class PendingAddWidgetInfo extends PendingAddItemInfo { minResizeHeight = i.minResizeHeight; previewImage = i.previewImage; icon = i.icon; - if (dataMimeType != null && data != null) { - mimeType = dataMimeType; - configurationData = data; - } + + spanX = i.spanX; + spanY = i.spanY; + minSpanX = i.minSpanX; + minSpanY = i.minSpanY; + } + + public boolean isCustomWidget() { + return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } // Copy constructor @@ -89,8 +92,6 @@ class PendingAddWidgetInfo extends PendingAddItemInfo { icon = copy.icon; info = copy.info; boundWidget = copy.boundWidget; - mimeType = copy.mimeType; - configurationData = copy.configurationData; componentName = copy.componentName; itemType = copy.itemType; spanX = copy.spanX; diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index bb5601e90..ff0604540 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -23,6 +23,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; public class ShortcutAndWidgetContainer extends ViewGroup { static final String TAG = "CellLayoutChildren"; @@ -75,6 +76,14 @@ public class ShortcutAndWidgetContainer extends ViewGroup { return null; } + public void addView(View child, int index, LayoutParams params, boolean inLayout) { + if (!inLayout) { + addView(child, index, params); + } else { + addViewInLayout(child, index, params, false); + } + } + @Override protected void dispatchDraw(Canvas canvas) { @SuppressWarnings("all") // suppress dead code warning diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 9cedae0f9..8f2d3edc3 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -333,7 +333,6 @@ public class WidgetPreviewLoader { sb.setLength(0); } else { sb.append(SHORTCUT_PREFIX); - ResolveInfo info = (ResolveInfo) o; sb.append(new ComponentName(info.activityInfo.packageName, info.activityInfo.name).flattenToString()); @@ -479,20 +478,19 @@ public class WidgetPreviewLoader { preview.getHeight() != mPreviewBitmapHeight)) { throw new RuntimeException("Improperly sized bitmap passed as argument"); } - if (info instanceof AppWidgetProviderInfo) { - return generateWidgetPreview((AppWidgetProviderInfo) info, preview); + if (info instanceof LauncherAppWidgetProviderInfo) { + return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, preview); } else { return generateShortcutPreview( (ResolveInfo) info, mPreviewBitmapWidth, mPreviewBitmapHeight, preview); } } - public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, Bitmap preview) { - int[] cellSpans = Launcher.getSpanForWidget(mContext, info); - int maxWidth = maxWidthForWidgetPreview(cellSpans[0]); - int maxHeight = maxHeightForWidgetPreview(cellSpans[1]); - return generateWidgetPreview(info, cellSpans[0], cellSpans[1], - maxWidth, maxHeight, preview, null); + public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, Bitmap preview) { + int maxWidth = maxWidthForWidgetPreview(info.spanX); + int maxHeight = maxHeightForWidgetPreview(info.spanY); + return generateWidgetPreview(info, info.spanX, info.spanY, maxWidth, + maxHeight, preview, null); } public int maxWidthForWidgetPreview(int spanX) { @@ -505,7 +503,7 @@ public class WidgetPreviewLoader { mWidgetSpacingLayout.estimateCellHeight(spanY)); } - public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, int cellVSpan, + public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, int cellHSpan, int cellVSpan, int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) { // Load the preview image if possible if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 8bd799198..b6e85f304 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3178,9 +3178,9 @@ public class Workspace extends SmoothPagedView // in its final location final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; - AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo(); - if (pinfo != null && - pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { + LauncherAppWidgetProviderInfo pInfo = (LauncherAppWidgetProviderInfo) + hostView.getAppWidgetInfo(); + if (pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { final Runnable addResizeFrame = new Runnable() { public void run() { DragLayer dragLayer = mLauncher.getDragLayer(); @@ -3228,7 +3228,9 @@ public class Workspace extends SmoothPagedView mAnimatingViewIntoPlace = true; if (d.dragView.hasDrawn()) { final ItemInfo info = (ItemInfo) cell.getTag(); - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) { + boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET + || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + if (isWidget) { int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE : ANIMATE_INTO_POSITION_AND_DISAPPEAR; animateWidgetDrop(info, parent, d.dragView, @@ -3951,6 +3953,7 @@ public class Workspace extends SmoothPagedView // When dragging and dropping from customization tray, we deal with creating // widgets/shortcuts/folders in a slightly different way switch (pendingInfo.itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: int span[] = new int[2]; span[0] = item.spanX; @@ -3968,8 +3971,10 @@ public class Workspace extends SmoothPagedView } } }; - View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET - ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null; + boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET + || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + + View finalView = isWidget ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null; if (finalView instanceof AppWidgetHostView && updateWidgetSize) { AppWidgetHostView awhv = (AppWidgetHostView) finalView; @@ -3978,7 +3983,7 @@ public class Workspace extends SmoothPagedView } int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR; - if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && + if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null && ((PendingAddWidgetInfo) pendingInfo).info.configure != null) { animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN; } @@ -4130,11 +4135,14 @@ public class Workspace extends SmoothPagedView Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView"); mLauncher.getDragLayer().removeView(finalView); } + + boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET || + info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) { Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView); dragView.setCrossFadeBitmap(crossFadeBitmap); dragView.crossFade((int) (duration * 0.8f)); - } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) { + } else if (isWidget && external) { scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]); } @@ -4984,7 +4992,7 @@ public class Workspace extends SmoothPagedView if (!changedInfo.isEmpty()) { DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo, mLauncher.getAppWidgetHost()); - if (LauncherModel.findAppWidgetProviderInfoWithComponent(getContext(), + if (LauncherModel.getProviderInfo(getContext(), changedInfo.get(0).providerName) != null) { // Re-inflate the widgets which have changed status widgetRefresh.run(); diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index 6512d427e..e41a66fb4 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import com.android.launcher3.IconCache; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import java.util.List; @@ -63,20 +64,20 @@ public abstract class AppWidgetManagerCompat { public abstract List getAllProviders(); - public abstract String loadLabel(AppWidgetProviderInfo info); + public abstract String loadLabel(LauncherAppWidgetProviderInfo info); public abstract boolean bindAppWidgetIdIfAllowed( int appWidgetId, AppWidgetProviderInfo info, Bundle options); - public abstract UserHandleCompat getUser(AppWidgetProviderInfo info); + public abstract UserHandleCompat getUser(LauncherAppWidgetProviderInfo info); public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity, AppWidgetHost host, int requestCode); public abstract Drawable loadPreview(AppWidgetProviderInfo info); - public abstract Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache); + public abstract Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache); - public abstract Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap); + public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap); } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java index f599f4303..767f16f62 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java @@ -28,6 +28,7 @@ import android.os.Build; import android.os.Bundle; import com.android.launcher3.IconCache; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import java.util.List; @@ -44,7 +45,7 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { } @Override - public String loadLabel(AppWidgetProviderInfo info) { + public String loadLabel(LauncherAppWidgetProviderInfo info) { return info.label.trim(); } @@ -59,7 +60,7 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { } @Override - public UserHandleCompat getUser(AppWidgetProviderInfo info) { + public UserHandleCompat getUser(LauncherAppWidgetProviderInfo info) { return UserHandleCompat.myUserHandle(); } @@ -79,12 +80,12 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { } @Override - public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) { + public Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache) { return cache.getFullResIcon(info.provider.getPackageName(), info.icon); } @Override - public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) { + public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap) { return bitmap; } } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index c3853ab62..158607adf 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -38,6 +38,7 @@ import android.view.View; import android.widget.Toast; import com.android.launcher3.IconCache; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; import java.util.ArrayList; @@ -65,8 +66,8 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { } @Override - public String loadLabel(AppWidgetProviderInfo info) { - return info.loadLabel(mPm); + public String loadLabel(LauncherAppWidgetProviderInfo info) { + return info.getLabel(mPm); } @Override @@ -77,7 +78,10 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { } @Override - public UserHandleCompat getUser(AppWidgetProviderInfo info) { + public UserHandleCompat getUser(LauncherAppWidgetProviderInfo info) { + if (info.isCustomWidget) { + return UserHandleCompat.myUserHandle(); + } return UserHandleCompat.fromUser(info.getProfile()); } @@ -99,13 +103,13 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { } @Override - public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) { - return info.loadIcon(mContext, cache.getFullResIconDpi()); + public Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache) { + return info.getIcon(mContext, cache); } @Override - public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) { - if (info.getProfile().equals(android.os.Process.myUserHandle())) { + public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap) { + if (info.isCustomWidget || info.getProfile().equals(android.os.Process.myUserHandle())) { return bitmap; } -- cgit v1.2.3 From 0cca76c594bb6e8a8e22600146829db646cfce0c Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 8 Dec 2014 11:15:42 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I3eb761fffcf27ceb4d79749da589dbaa9e7b9444 Auto-generated-cl: translation import --- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 ++-- res/values-my-rMM/strings.xml | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 670ac70b5..3a1c82dcc 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -85,7 +85,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍​" + "សូម​ស្វាគមន៍" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1beb049e2..65f01b7f0 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ​" + "ຍົກ​ເລີກ" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -117,7 +117,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ​" + "ລຶບ" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 544ecdc7a..668803587 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -60,7 +60,7 @@ "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +76,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +102,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +111,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" -- cgit v1.2.3 From 1c34c828ae4bf639877f56a01926b33e86f2cb66 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 10 Dec 2014 07:43:52 -0800 Subject: Import translations. DO NOT MERGE Change-Id: Id66a36209915e770779ef3c41cf18135c5180ac3 Auto-generated-cl: translation import --- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 ++-- res/values-my-rMM/strings.xml | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 3a1c82dcc..670ac70b5 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -85,7 +85,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍" + "សូម​ស្វាគមន៍​" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 65f01b7f0..1beb049e2 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ" + "ຍົກ​ເລີກ​" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -117,7 +117,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ" + "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 668803587..544ecdc7a 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -60,7 +60,7 @@ "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +76,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +102,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +111,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" -- cgit v1.2.3 From 1bc1ae67142b88a952efc2ba4adea6cadb7164a1 Mon Sep 17 00:00:00 2001 From: Brian Parfett Date: Fri, 12 Dec 2014 12:30:00 -0800 Subject: Updating version codes from L to LOLLIPOP This is required so Launcher3 can compile within IntelliJ. Change-Id: I99a98a6d2326a8f8b0f0320430de9b555ea430c6 Bugs: b/18720914 --- src/com/android/launcher3/Utilities.java | 2 +- src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index c0f75918d..215d63d2e 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -108,7 +108,7 @@ public final class Utilities { * Indicates if the device is running LMP or higher. */ public static boolean isLmpOrAbove() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.L; + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } /** diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index 158607adf..6c3e092e8 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -44,7 +44,7 @@ import com.android.launcher3.R; import java.util.ArrayList; import java.util.List; -@TargetApi(Build.VERSION_CODES.L) +@TargetApi(Build.VERSION_CODES.LOLLIPOP) class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { private final UserManager mUserManager; -- cgit v1.2.3 From 7d176291680f3638143008c369fc20b354671d29 Mon Sep 17 00:00:00 2001 From: Dave Hawkey Date: Fri, 19 Dec 2014 14:13:30 -0700 Subject: Add syntax option to backup.proto. This is required to properly compile the file in google3. Change-Id: Ib9ff3163974a5180797c0ecadb833dae93619b2c --- protos/backup.proto | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protos/backup.proto b/protos/backup.proto index 8ae175234..44c4b09e3 100644 --- a/protos/backup.proto +++ b/protos/backup.proto @@ -14,7 +14,9 @@ * limitations under the License. */ - package launcher_backup; +syntax = "proto2"; + +package launcher_backup; option java_package = "com.android.launcher3.backup"; option java_outer_classname = "BackupProtos"; -- cgit v1.2.3 From e87e6abc3639559f2746171999fee352cb5bd946 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 21 Nov 2014 22:42:53 -0800 Subject: Removing dead code from Launcher provider > Removing DB upgrade logic for versions before 12. Launcher3 was forked off Launcher2 when DB version was already 12, so, there can never be a lower version of the DB. > Removing logic for auto import of launcher2 DB. This flow had been permanently disabled by a flag, and we show a confirmation prompt/cling before migration Change-Id: I2ac2f21fefc41ee247164566a088927263b09459 --- src/com/android/launcher3/LauncherProvider.java | 542 +----------------------- 1 file changed, 12 insertions(+), 530 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 1715b02bf..9233bcb0f 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -18,7 +18,6 @@ package com.android.launcher3; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentProviderOperation; @@ -36,11 +35,7 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; -import android.database.sqlite.SQLiteStatement; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; -import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -61,21 +56,17 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; + private static final int MIN_DATABASE_VERSION = 12; private static final int DATABASE_VERSION = 20; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; - // Should we attempt to load anything from the com.android.launcher2 provider? - static final boolean IMPORT_LAUNCHER2_DATABASE = false; - static final String TABLE_FAVORITES = "favorites"; static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens"; static final String PARAMETER_NOTIFY = "notify"; - static final String UPGRADED_FROM_OLD_DATABASE = - "UPGRADED_FROM_OLD_DATABASE"; - static final String EMPTY_DATABASE_CREATED = - "EMPTY_DATABASE_CREATED"; + static final String UPGRADED_FROM_OLD_DATABASE = "UPGRADED_FROM_OLD_DATABASE"; + static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; @@ -90,7 +81,6 @@ public class LauncherProvider extends ContentProvider { Uri.parse("content://" + AUTHORITY + "/appWidgetReset"); private DatabaseHelper mOpenHelper; - private static boolean sJustLoadedFromOldDb; @Override public boolean onCreate() { @@ -264,26 +254,6 @@ public class LauncherProvider extends ContentProvider { mOpenHelper.updateMaxScreenId(maxScreenId); } - /** - * @param Should we load the old db for upgrade? first run only. - */ - synchronized public boolean justLoadedOldDb() { - String spKey = LauncherAppState.getSharedPreferencesKey(); - SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE); - - boolean loadedOldDb = false || sJustLoadedFromOldDb; - - sJustLoadedFromOldDb = false; - if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) { - - SharedPreferences.Editor editor = sp.edit(); - editor.remove(UPGRADED_FROM_OLD_DATABASE); - editor.commit(); - loadedOldDb = true; - } - return loadedOldDb; - } - /** * Clears all the data for a fresh start. */ @@ -356,17 +326,6 @@ public class LauncherProvider extends ContentProvider { Uri.parse(getContext().getString(R.string.old_launcher_provider_uri))); } - private static interface ContentValuesCallback { - public void onRow(ContentValues values); - } - - private static boolean shouldImportLauncher2Database(Context context) { - boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet); - - // We don't import the old databse for tablets, as the grid size has changed. - return !isTablet && IMPORT_LAUNCHER2_DATABASE; - } - public void deleteDatabase() { // Are you sure? (y/n) final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -460,38 +419,9 @@ public class LauncherProvider extends ContentProvider { sendAppWidgetResetNotify(); } - if (shouldImportLauncher2Database(mContext)) { - // Try converting the old database - ContentValuesCallback permuteScreensCb = new ContentValuesCallback() { - public void onRow(ContentValues values) { - int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER); - if (container == Favorites.CONTAINER_DESKTOP) { - int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN); - screen = (int) upgradeLauncherDb_permuteScreens(screen); - values.put(LauncherSettings.Favorites.SCREEN, screen); - } - } - }; - Uri uri = Uri.parse("content://" + Settings.AUTHORITY + - "/old_favorites?notify=true"); - if (!convertDatabase(db, uri, permuteScreensCb, true)) { - // Try and upgrade from the Launcher2 db - uri = Uri.parse(mContext.getString(R.string.old_launcher_provider_uri)); - if (!convertDatabase(db, uri, permuteScreensCb, false)) { - // If we fail, then set a flag to load the default workspace - setFlagEmptyDbCreated(); - return; - } - } - // Right now, in non-default workspace cases, we want to run the final - // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so - // set that flag too. - setFlagJustLoadedOldDb(); - } else { - // Fresh and clean launcher DB. - mMaxItemId = initializeMaxItemId(db); - setFlagEmptyDbCreated(); - } + // Fresh and clean launcher DB. + mMaxItemId = initializeMaxItemId(db); + setFlagEmptyDbCreated(); } private void addWorkspacesTable(SQLiteDatabase db) { @@ -551,213 +481,16 @@ public class LauncherProvider extends ContentProvider { editor.commit(); } - // We rearrange the screens from the old launcher - // 12345 -> 34512 - private long upgradeLauncherDb_permuteScreens(long screen) { - if (screen >= 2) { - return screen - 2; - } else { - return screen + 3; - } - } - - private boolean convertDatabase(SQLiteDatabase db, Uri uri, - ContentValuesCallback cb, boolean deleteRows) { - if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade"); - boolean converted = false; - - final ContentResolver resolver = mContext.getContentResolver(); - Cursor cursor = null; - - try { - cursor = resolver.query(uri, null, null, null, null); - } catch (Exception e) { - // Ignore - } - - // We already have a favorites database in the old provider - if (cursor != null) { - try { - if (cursor.getCount() > 0) { - converted = copyFromCursor(db, cursor, cb) > 0; - if (converted && deleteRows) { - resolver.delete(uri, null, null); - } - } - } finally { - cursor.close(); - } - } - - if (converted) { - // Convert widgets from this import into widgets - if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade"); - convertWidgets(db); - - // Update max item id - mMaxItemId = initializeMaxItemId(db); - if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId); - } - - return converted; - } - - private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) { - final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); - final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); - final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); - final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE); - final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); - final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); - final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); - final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); - final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); - final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); - final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); - final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); - - ContentValues[] rows = new ContentValues[c.getCount()]; - int i = 0; - while (c.moveToNext()) { - ContentValues values = new ContentValues(c.getColumnCount()); - values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex)); - values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex)); - values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex)); - values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex)); - values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex)); - values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex)); - values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex)); - values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex)); - values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex)); - values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1); - values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex)); - values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex)); - values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex)); - values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex)); - values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex)); - if (cb != null) { - cb.onRow(values); - } - rows[i++] = values; - } - - int total = 0; - if (i > 0) { - db.beginTransaction(); - try { - int numValues = rows.length; - for (i = 0; i < numValues; i++) { - if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) { - return 0; - } else { - total++; - } - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - return total; - } - @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion); int version = oldVersion; - if (version < 3) { - // upgrade 1,2 -> 3 added appWidgetId column - db.beginTransaction(); - try { - // Insert new column for holding appWidgetIds - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;"); - db.setTransactionSuccessful(); - version = 3; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); - } - - // Convert existing widgets only if table upgrade was successful - if (version == 3) { - convertWidgets(db); - } - } - - if (version < 4) { - version = 4; - } - - // Where's version 5? - // - Donut and sholes on 2.0 shipped with version 4 of launcher1. - // - Passion shipped on 2.1 with version 6 of launcher3 - // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1 - // but version 5 on there was the updateContactsShortcuts change - // which was version 6 in launcher 2 (first shipped on passion 2.1r1). - // The updateContactsShortcuts change is idempotent, so running it twice - // is okay so we'll do that when upgrading the devices that shipped with it. - if (version < 6) { - // We went from 3 to 5 screens. Move everything 1 to the right - db.beginTransaction(); - try { - db.execSQL("UPDATE favorites SET screen=(screen + 1);"); - db.setTransactionSuccessful(); - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); - } - - // We added the fast track. - if (updateContactsShortcuts(db)) { - version = 6; - } - } - - if (version < 7) { - // Version 7 gets rid of the special search widget. - convertWidgets(db); - version = 7; - } - - if (version < 8) { - // Version 8 (froyo) has the icons all normalized. This should - // already be the case in practice, but we now rely on it and don't - // resample the images each time. - normalizeIcons(db); - version = 8; - } - - if (version < 9) { - // The max id is not yet set at this point (onUpgrade is triggered in the ctor - // before it gets a change to get set, so we need to read it here when we use it) - if (mMaxItemId == -1) { - mMaxItemId = initializeMaxItemId(db); - } - - // Add default hotseat icons - loadFavorites(db, new DefaultLayoutParser(mContext, mAppWidgetHost, this, - mContext.getResources(), R.xml.update_workspace)); - version = 9; - } - - // We bumped the version three time during JB, once to update the launch flags, once to - // update the override for the default launch animation and once to set the mimetype - // to improve startup performance - if (version < 12) { - // Contact shortcuts need a different set of flags to be launched now - // The updateContactsShortcuts change is idempotent, so we can keep using it like - // back in the Donut days - updateContactsShortcuts(db); - version = 12; + if (version < MIN_DATABASE_VERSION) { + // The version cannot be lower that this, as Launcher3 never supported a lower + // version of the DB. + createEmptyDB(db); + version = DATABASE_VERSION; } if (version < 13) { @@ -765,10 +498,6 @@ public class LauncherProvider extends ContentProvider { // to persist workspace screens and their relative order. mMaxScreenId = 0; - // This will never happen in the wild, but when we switch to using workspace - // screen ids, redo the import from old launcher. - sJustLoadedFromOldDb = true; - addWorkspacesTable(db); version = 13; } @@ -858,10 +587,7 @@ public class LauncherProvider extends ContentProvider { if (version != DATABASE_VERSION) { Log.w(TAG, "Destroying all old data."); - db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); - db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS); - - onCreate(db); + createEmptyDB(db); } } @@ -906,142 +632,6 @@ public class LauncherProvider extends ContentProvider { return true; } - private boolean updateContactsShortcuts(SQLiteDatabase db) { - final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, - new int[] { Favorites.ITEM_TYPE_SHORTCUT }); - - Cursor c = null; - final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT"; - db.beginTransaction(); - try { - // Select and iterate through each matching widget - c = db.query(TABLE_FAVORITES, - new String[] { Favorites._ID, Favorites.INTENT }, - selectWhere, null, null, null, null); - if (c == null) return false; - - if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount()); - - final int idIndex = c.getColumnIndex(Favorites._ID); - final int intentIndex = c.getColumnIndex(Favorites.INTENT); - - while (c.moveToNext()) { - long favoriteId = c.getLong(idIndex); - final String intentUri = c.getString(intentIndex); - if (intentUri != null) { - try { - final Intent intent = Intent.parseUri(intentUri, 0); - android.util.Log.d("Home", intent.toString()); - final Uri uri = intent.getData(); - if (uri != null) { - final String data = uri.toString(); - if ((Intent.ACTION_VIEW.equals(intent.getAction()) || - actionQuickContact.equals(intent.getAction())) && - (data.startsWith("content://contacts/people/") || - data.startsWith("content://com.android.contacts/" + - "contacts/lookup/"))) { - - final Intent newIntent = new Intent(actionQuickContact); - // When starting from the launcher, start in a new, cleared task - // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we - // clear the whole thing preemptively here since - // QuickContactActivity will finish itself when launching other - // detail activities. - newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TASK); - newIntent.putExtra( - Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true); - newIntent.setData(uri); - // Determine the type and also put that in the shortcut - // (that can speed up launch a bit) - newIntent.setDataAndType(uri, newIntent.resolveType(mContext)); - - final ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.INTENT, - newIntent.toUri(0)); - - String updateWhere = Favorites._ID + "=" + favoriteId; - db.update(TABLE_FAVORITES, values, updateWhere, null); - } - } - } catch (RuntimeException ex) { - Log.e(TAG, "Problem upgrading shortcut", ex); - } catch (URISyntaxException e) { - Log.e(TAG, "Problem upgrading shortcut", e); - } - } - } - - db.setTransactionSuccessful(); - } catch (SQLException ex) { - Log.w(TAG, "Problem while upgrading contacts", ex); - return false; - } finally { - db.endTransaction(); - if (c != null) { - c.close(); - } - } - - return true; - } - - private void normalizeIcons(SQLiteDatabase db) { - Log.d(TAG, "normalizing icons"); - - db.beginTransaction(); - Cursor c = null; - SQLiteStatement update = null; - try { - boolean logged = false; - update = db.compileStatement("UPDATE favorites " - + "SET icon=? WHERE _id=?"); - - c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" + - Favorites.ICON_TYPE_BITMAP, null); - - final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); - final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON); - - while (c.moveToNext()) { - long id = c.getLong(idIndex); - byte[] data = c.getBlob(iconIndex); - try { - Bitmap bitmap = Utilities.createIconBitmap( - BitmapFactory.decodeByteArray(data, 0, data.length), - mContext); - if (bitmap != null) { - update.bindLong(1, id); - data = ItemInfo.flattenBitmap(bitmap); - if (data != null) { - update.bindBlob(2, data); - update.execute(); - } - bitmap.recycle(); - } - } catch (Exception e) { - if (!logged) { - Log.e(TAG, "Failed normalizing icon " + id, e); - } else { - Log.e(TAG, "Also failed normalizing icon " + id); - } - logged = true; - } - } - db.setTransactionSuccessful(); - } catch (SQLException ex) { - Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); - } finally { - db.endTransaction(); - if (update != null) { - update.close(); - } - if (c != null) { - c.close(); - } - } - } - // Generates a new ID to use for an object in your database. This method should be only // called from the main UI thread. As an exception, we do call it when we call the // constructor from the worker thread; however, this doesn't extend until after the @@ -1137,94 +727,6 @@ public class LauncherProvider extends ContentProvider { return id; } - /** - * Upgrade existing clock and photo frame widgets into their new widget - * equivalents. - */ - private void convertWidgets(SQLiteDatabase db) { - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); - final int[] bindSources = new int[] { - Favorites.ITEM_TYPE_WIDGET_CLOCK, - Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME, - Favorites.ITEM_TYPE_WIDGET_SEARCH, - }; - - final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources); - - Cursor c = null; - - db.beginTransaction(); - try { - // Select and iterate through each matching widget - c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE }, - selectWhere, null, null, null, null); - - if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount()); - - final ContentValues values = new ContentValues(); - while (c != null && c.moveToNext()) { - long favoriteId = c.getLong(0); - int favoriteType = c.getInt(1); - - // Allocate and update database with new appWidgetId - try { - int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); - - if (LOGD) { - Log.d(TAG, "allocated appWidgetId=" + appWidgetId - + " for favoriteId=" + favoriteId); - } - values.clear(); - values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET); - values.put(Favorites.APPWIDGET_ID, appWidgetId); - - // Original widgets might not have valid spans when upgrading - if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) { - values.put(LauncherSettings.Favorites.SPANX, 4); - values.put(LauncherSettings.Favorites.SPANY, 1); - } else { - values.put(LauncherSettings.Favorites.SPANX, 2); - values.put(LauncherSettings.Favorites.SPANY, 2); - } - - String updateWhere = Favorites._ID + "=" + favoriteId; - db.update(TABLE_FAVORITES, values, updateWhere, null); - - if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) { - // TODO: check return value - appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - new ComponentName("com.android.alarmclock", - "com.android.alarmclock.AnalogAppWidgetProvider")); - } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) { - // TODO: check return value - appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - new ComponentName("com.android.camera", - "com.android.camera.PhotoAppWidgetProvider")); - } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) { - // TODO: check return value - appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - getSearchWidgetProvider()); - } - } catch (RuntimeException ex) { - Log.e(TAG, "Problem allocating appWidgetId", ex); - } - } - - db.setTransactionSuccessful(); - } catch (SQLException ex) { - Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); - } finally { - db.endTransaction(); - if (c != null) { - c.close(); - } - } - - // Update max item id - mMaxItemId = initializeMaxItemId(db); - if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId); - } - private boolean initializeExternalAdd(ContentValues values) { // 1. Ensure that externally added items have a valid item id long id = generateNewItemId(); @@ -1339,11 +841,6 @@ public class LauncherProvider extends ContentProvider { return count; } - private ComponentName getSearchWidgetProvider() { - AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(mContext); - return (searchProvider == null) ? null : searchProvider.provider; - } - private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) { final ContentResolver resolver = mContext.getContentResolver(); Cursor c = null; @@ -1638,21 +1135,6 @@ public class LauncherProvider extends ContentProvider { } } - /** - * Build a query string that will match any row where the column matches - * anything in the values list. - */ - private static String buildOrWhereString(String column, int[] values) { - StringBuilder selectWhere = new StringBuilder(); - for (int i = values.length - 1; i >= 0; i--) { - selectWhere.append(column).append("=").append(values[i]); - if (i > 0) { - selectWhere.append(" OR "); - } - } - return selectWhere.toString(); - } - static class SqlArguments { public final String table; public final String where; -- cgit v1.2.3 From 08f7261d11a53ae4b330ad4fa897b8519de3d750 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 5 Jan 2015 13:41:43 -0800 Subject: Adding a rank column for itemInfo > Rank is used to determine position of an item in a folder. Bug: 18590192 Change-Id: I2826a7c570b4cc58e719d685f17a24ec6133804f --- src/com/android/launcher3/Folder.java | 71 +++++----------------- src/com/android/launcher3/ItemInfo.java | 12 ++-- .../launcher3/LauncherBackupAgentHelper.java | 5 ++ .../android/launcher3/LauncherBackupHelper.java | 4 +- src/com/android/launcher3/LauncherModel.java | 10 ++- src/com/android/launcher3/LauncherProvider.java | 48 ++++++++++++++- src/com/android/launcher3/LauncherSettings.java | 7 +++ src/com/android/launcher3/Utilities.java | 9 +++ 8 files changed, 100 insertions(+), 66 deletions(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 1890af47d..66b656882 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -71,8 +71,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList static final int STATE_ANIMATING = 1; static final int STATE_OPEN = 2; - private static final int CLOSE_FOLDER_DELAY_MS = 150; - private int mExpandDuration; private int mMaterialExpandDuration; private int mMaterialExpandStagger; @@ -342,60 +340,33 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mInfo; } - private class GridComparator implements Comparator { - int mNumCols; - public GridComparator(int numCols) { - mNumCols = numCols; - } - - @Override - public int compare(ShortcutInfo lhs, ShortcutInfo rhs) { - int lhIndex = lhs.cellY * mNumCols + lhs.cellX; - int rhIndex = rhs.cellY * mNumCols + rhs.cellX; - return (lhIndex - rhIndex); - } - } - - private void placeInReadingOrder(ArrayList items) { - int maxX = 0; - int count = items.size(); - for (int i = 0; i < count; i++) { - ShortcutInfo item = items.get(i); - if (item.cellX > maxX) { - maxX = item.cellX; - } - } - - GridComparator gridComparator = new GridComparator(maxX + 1); - Collections.sort(items, gridComparator); - final int countX = mContent.getCountX(); - for (int i = 0; i < count; i++) { - int x = i % countX; - int y = i / countX; - ShortcutInfo item = items.get(i); - item.cellX = x; - item.cellY = y; - } - } - void bind(FolderInfo info) { mInfo = info; ArrayList children = info.contents; ArrayList overflow = new ArrayList(); - setupContentForNumItems(children.size()); - placeInReadingOrder(children); - int count = 0; + + final int totalChildren = children.size(); + setupContentForNumItems(totalChildren); + + // Arrange children in the grid based on the rank. + Collections.sort(children, Utilities.RANK_COMPARATOR); + final int countX = mContent.getCountX(); + + int visibleChildren = 0; for (int i = 0; i < children.size(); i++) { - ShortcutInfo child = (ShortcutInfo) children.get(i); + ShortcutInfo child = children.get(i); + child.cellX = i % countX; + child.cellY = i / countX; + if (createAndAddShortcut(child) == null) { overflow.add(child); } else { - count++; + visibleChildren++; } } // We rearrange the items in case there are any empty gaps - setupContentForNumItems(count); + setupContentForNumItems(visibleChildren); // If our folder has too many items we prune them from the list. This is an issue // when upgrading from the old Folders implementation which could contain an unlimited @@ -414,7 +385,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } else { mFolderName.setText(""); } - updateItemLocationsInDatabase(); // In case any children didn't come across during loading, clean up the folder accordingly mFolderIcon.post(new Runnable() { @@ -914,22 +884,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Do nothing } - private void updateItemLocationsInDatabase() { - ArrayList list = getItemsInReadingOrder(); - for (int i = 0; i < list.size(); i++) { - View v = list.get(i); - ItemInfo info = (ItemInfo) v.getTag(); - LauncherModel.moveItemInDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY); - } - } - private void updateItemLocationsInDatabaseBatch() { ArrayList list = getItemsInReadingOrder(); ArrayList items = new ArrayList(); for (int i = 0; i < list.size(); i++) { View v = list.get(i); ItemInfo info = (ItemInfo) v.getTag(); + info.rank = i; items.add(info); } diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index 09b77f756..aff8323c2 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -97,6 +97,11 @@ public class ItemInfo { */ public int minSpanY = 1; + /** + * Indicates the position in an ordered list. + */ + public int rank = 0; + /** * Indicates that this item needs to be updated in the db */ @@ -135,6 +140,7 @@ public class ItemInfo { cellY = info.cellY; spanX = info.spanX; spanY = info.spanY; + rank = info.rank; screenId = info.screenId; itemType = info.itemType; container = info.container; @@ -161,6 +167,7 @@ public class ItemInfo { values.put(LauncherSettings.Favorites.CELLY, cellY); values.put(LauncherSettings.Favorites.SPANX, spanX); values.put(LauncherSettings.Favorites.SPANY, spanY); + values.put(LauncherSettings.Favorites.RANK, rank); long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber); @@ -170,11 +177,6 @@ public class ItemInfo { } } - void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) { - values.put(LauncherSettings.Favorites.CELLX, cellX); - values.put(LauncherSettings.Favorites.CELLY, cellY); - } - static byte[] flattenBitmap(Bitmap bitmap) { // Try go guesstimate how much space the icon will take when serialized // to avoid unnecessary allocations/copies during the write. diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 3868a57f1..3d7d3f271 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -90,6 +90,11 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { if (hasData && mHelper.restoreSuccessful) { LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated(); LauncherClings.synchonouslyMarkFirstRunClingDismissed(this); + + // TODO: Update the backup set to include rank. + if (mHelper.restoredBackupVersion <= 2) { + LauncherAppState.getLauncherProvider().updateFolderItemsRank(); + } } else { if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB"); LauncherAppState.getLauncherProvider().createEmptyDB(); diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 895a569d2..4faa8ace8 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -19,7 +19,6 @@ import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; import android.app.backup.BackupHelper; import android.app.backup.BackupManager; -import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentResolver; @@ -60,7 +59,6 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.zip.CRC32; /** @@ -148,6 +146,7 @@ public class LauncherBackupHelper implements BackupHelper { private DeviceProfieData mCurrentProfile; boolean restoreSuccessful; + int restoredBackupVersion = 1; public LauncherBackupHelper(Context context) { mContext = context; @@ -299,6 +298,7 @@ public class LauncherBackupHelper implements BackupHelper { MessageNano.mergeFrom(journal, readCheckedBytes(mBuffer, dataSize)); applyJournal(journal); restoreSuccessful = isBackupCompatible(journal); + restoredBackupVersion = journal.backupVersion; return; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3c1cf485d..95ebaec87 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -768,6 +768,7 @@ public class LauncherModel extends BroadcastReceiver values.put(LauncherSettings.Favorites.CONTAINER, item.container); values.put(LauncherSettings.Favorites.CELLX, item.cellX); values.put(LauncherSettings.Favorites.CELLY, item.cellY); + values.put(LauncherSettings.Favorites.RANK, item.rank); values.put(LauncherSettings.Favorites.SCREEN, item.screenId); updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase"); @@ -801,6 +802,7 @@ public class LauncherModel extends BroadcastReceiver values.put(LauncherSettings.Favorites.CONTAINER, item.container); values.put(LauncherSettings.Favorites.CELLX, item.cellX); values.put(LauncherSettings.Favorites.CELLY, item.cellY); + values.put(LauncherSettings.Favorites.RANK, item.rank); values.put(LauncherSettings.Favorites.SCREEN, item.screenId); contentValues.add(values); @@ -832,6 +834,7 @@ public class LauncherModel extends BroadcastReceiver values.put(LauncherSettings.Favorites.CONTAINER, item.container); values.put(LauncherSettings.Favorites.CELLX, item.cellX); values.put(LauncherSettings.Favorites.CELLY, item.cellY); + values.put(LauncherSettings.Favorites.RANK, item.rank); values.put(LauncherSettings.Favorites.SPANX, item.spanX); values.put(LauncherSettings.Favorites.SPANY, item.spanY); values.put(LauncherSettings.Favorites.SCREEN, item.screenId); @@ -845,7 +848,6 @@ public class LauncherModel extends BroadcastReceiver static void updateItemInDatabase(Context context, final ItemInfo item) { final ContentValues values = new ContentValues(); item.onAddToDatabase(context, values); - item.updateValuesWithCoordinates(values, item.cellX, item.cellY); updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase"); } @@ -906,6 +908,7 @@ public class LauncherModel extends BroadcastReceiver final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); + final int rankIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.RANK); final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID); @@ -915,6 +918,7 @@ public class LauncherModel extends BroadcastReceiver ItemInfo item = new ItemInfo(); item.cellX = c.getInt(cellXIndex); item.cellY = c.getInt(cellYIndex); + item.rank = c.getInt(rankIndex); item.spanX = Math.max(1, c.getInt(spanXIndex)); item.spanY = Math.max(1, c.getInt(spanYIndex)); item.container = c.getInt(containerIndex); @@ -1002,7 +1006,6 @@ public class LauncherModel extends BroadcastReceiver item.id = LauncherAppState.getLauncherProvider().generateNewItemId(); values.put(LauncherSettings.Favorites._ID, item.id); - item.updateValuesWithCoordinates(values, item.cellX, item.cellY); final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); Runnable r = new Runnable() { @@ -1917,6 +1920,8 @@ public class LauncherModel extends BroadcastReceiver (LauncherSettings.Favorites.SPANX); final int spanYIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.SPANY); + final int rankIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.RANK); final int restoredIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.RESTORED); final int profileIdIndex = c.getColumnIndexOrThrow( @@ -2102,6 +2107,7 @@ public class LauncherModel extends BroadcastReceiver info.screenId = c.getInt(screenIndex); info.cellX = c.getInt(cellXIndex); info.cellY = c.getInt(cellYIndex); + info.rank = c.getInt(rankIndex); info.spanX = 1; info.spanY = 1; info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 9233bcb0f..a9ad59652 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -57,7 +57,7 @@ public class LauncherProvider extends ContentProvider { private static final boolean LOGD = false; private static final int MIN_DATABASE_VERSION = 12; - private static final int DATABASE_VERSION = 20; + private static final int DATABASE_VERSION = 21; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -326,6 +326,10 @@ public class LauncherProvider extends ContentProvider { Uri.parse(getContext().getString(R.string.old_launcher_provider_uri))); } + public void updateFolderItemsRank() { + mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false); + } + public void deleteDatabase() { // Are you sure? (y/n) final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -409,7 +413,8 @@ public class LauncherProvider extends ContentProvider { "appWidgetProvider TEXT," + "modified INTEGER NOT NULL DEFAULT 0," + "restored INTEGER NOT NULL DEFAULT 0," + - "profileId INTEGER DEFAULT " + userSerialNumber + + "profileId INTEGER DEFAULT " + userSerialNumber + "," + + "rank INTEGER NOT NULL DEFAULT 0" + ");"); addWorkspacesTable(db); @@ -585,6 +590,12 @@ public class LauncherProvider extends ContentProvider { // else old version remains, which means we wipe old data } + if (version < 21) { + if (updateFolderItemsRank(db, true)) { + version = 21; + } + } + if (version != DATABASE_VERSION) { Log.w(TAG, "Destroying all old data."); createEmptyDB(db); @@ -609,6 +620,37 @@ public class LauncherProvider extends ContentProvider { onCreate(db); } + private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { + db.beginTransaction(); + try { + if (addRankColumn) { + // Insert new column for holding rank + db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;"); + } + + // Get a map for folder ID to folder width + Cursor c = db.rawQuery("SELECT container, MAX(cellX) FROM favorites" + + " WHERE container IN (SELECT _id FROM favorites WHERE itemType = ?)" + + " GROUP BY container;", + new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}); + + while (c.moveToNext()) { + db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE container=?;", + new Object[] {c.getLong(1) + 1, c.getLong(0)}); + } + + c.close(); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + // Old version remains, which means we wipe old data + Log.e(TAG, ex.getMessage(), ex); + return false; + } finally { + db.endTransaction(); + } + return true; + } + private boolean addProfileColumn(SQLiteDatabase db) { db.beginTransaction(); try { @@ -1116,6 +1158,8 @@ public class LauncherProvider extends ContentProvider { } finally { db.endTransaction(); } + + updateFolderItemsRank(db, false); } } finally { c.close(); diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 65e3d2848..13fd7ee30 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -295,6 +295,7 @@ class LauncherSettings { * @see android.provider.LiveFolders#DISPLAY_MODE_GRID * @see android.provider.LiveFolders#DISPLAY_MODE_LIST */ + @Deprecated static final String DISPLAY_MODE = "displayMode"; /** @@ -302,5 +303,11 @@ class LauncherSettings { *

Type: INTEGER

*/ static final String RESTORED = "restored"; + + /** + * Indicates the position of the item inside an auto-arranged view like folder or hotseat. + *

Type: INTEGER

+ */ + static final String RANK = "rank"; } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 215d63d2e..1a9b9a16c 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -49,6 +49,7 @@ import android.view.View; import android.widget.Toast; import java.util.ArrayList; +import java.util.Comparator; /** * Various utilities shared amongst the Launcher's classes. @@ -542,4 +543,12 @@ public final class Utilities { } return defaultWidgetForSearchPackage; } + + public static final Comparator RANK_COMPARATOR = new Comparator() { + + @Override + public int compare(ItemInfo lhs, ItemInfo rhs) { + return lhs.rank - rhs.rank; + } + }; } -- cgit v1.2.3 From c0ee675f69c9c71c5cb51dd2b47bef3d709ea3cd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 16 Jan 2015 14:10:32 -0800 Subject: Avoiding writing the Metadata in the backup if nothing was changed in the backup Change-Id: I4608ab5511b01f0d0018596d68e4341635649f73 --- .../launcher3/LauncherBackupAgentHelper.java | 2 +- .../android/launcher3/LauncherBackupHelper.java | 32 ++++++++++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 3d7d3f271..ddfd70d6b 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -32,7 +32,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { private static final String LAUNCHER_DATA_PREFIX = "L"; - static final boolean VERBOSE = true; + static final boolean VERBOSE = false; static final boolean DEBUG = false; private static BackupManager sBackupManager; diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 4faa8ace8..32bea5baa 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -57,6 +57,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.zip.CRC32; @@ -140,9 +141,9 @@ public class LauncherBackupHelper implements BackupHelper { private IconCache mIconCache; private BackupManager mBackupManager; - private HashMap mWidgetMap; private byte[] mBuffer = new byte[512]; private long mLastBackupRestoreTime; + private boolean mBackupDataWasUpdated; private DeviceProfieData mCurrentProfile; boolean restoreSuccessful; @@ -201,7 +202,7 @@ public class LauncherBackupHelper implements BackupHelper { // Record the time before performing backup so that entries edited while the backup // was going on, do not get missed in next backup. long newBackupTime = System.currentTimeMillis(); - + mBackupDataWasUpdated = false; try { backupFavorites(data); backupScreens(data); @@ -219,16 +220,30 @@ public class LauncherBackupHelper implements BackupHelper { for (String deleted: mExistingKeys) { if (VERBOSE) Log.v(TAG, "dropping deleted item " + deleted); data.writeEntityHeader(deleted, -1); + mBackupDataWasUpdated = true; } mExistingKeys.clear(); - mLastBackupRestoreTime = newBackupTime; + if (!mBackupDataWasUpdated) { + // Check if any metadata has changed + mBackupDataWasUpdated = (in.profile == null) + || !Arrays.equals(DeviceProfieData.toByteArray(in.profile), + DeviceProfieData.toByteArray(getDeviceProfieData())) + || (in.backupVersion != BACKUP_VERSION) + || (in.appVersion != getAppVersion()); + } + + if (mBackupDataWasUpdated) { + mLastBackupRestoreTime = newBackupTime; - // We store the journal at two places. - // 1) Storing it in newState allows us to do partial backups by comparing old state - // 2) Storing it in backup data allows us to validate keys during restore - Journal state = getCurrentStateJournal(); - writeRowToBackup(JOURNAL_KEY, state, data); + // We store the journal at two places. + // 1) Storing it in newState allows us to do partial backups by comparing old state + // 2) Storing it in backup data allows us to validate keys during restore + Journal state = getCurrentStateJournal(); + writeRowToBackup(JOURNAL_KEY, state, data); + } else { + if (DEBUG) Log.d(TAG, "Nothing was written during backup"); + } } catch (IOException e) { Log.e(TAG, "launcher backup has failed", e); } @@ -1020,6 +1035,7 @@ public class LauncherBackupHelper implements BackupHelper { byte[] blob = writeCheckedBytes(proto); data.writeEntityHeader(backupKey, blob.length); data.writeEntityData(blob, blob.length); + mBackupDataWasUpdated = true; if (VERBOSE) Log.v(TAG, "Writing New entry " + backupKey); } -- cgit v1.2.3 From 2571c1acc2ab0d507ac2fd118271bb8a76660865 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Fri, 16 Jan 2015 16:06:23 -0800 Subject: Import translations. DO NOT MERGE Change-Id: Id2721f2c721bfcaf5f880e6675f66a02a8bca1f3 Auto-generated-cl: translation import --- WallpaperPicker/res/values-am/strings.xml | 6 ++--- WallpaperPicker/res/values-bn-rBD/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-eu-rES/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-fa/strings.xml | 2 +- WallpaperPicker/res/values-gl-rES/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-is-rIS/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-kk-rKZ/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-kn-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-ky-rKG/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-mk-rMK/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-ml-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-mr-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-my-rMM/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-ne-rNP/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-si-rLK/strings.xml | 38 +++++++++++++++++++++++++++ WallpaperPicker/res/values-ta-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-te-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-th/strings.xml | 2 +- WallpaperPicker/res/values-ur-rPK/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-uz-rUZ/strings.xml | 36 +++++++++++++++++++++++++ res/values-cs/strings.xml | 2 +- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 +-- res/values-my-rMM/strings.xml | 8 +++--- 24 files changed, 627 insertions(+), 13 deletions(-) create mode 100644 WallpaperPicker/res/values-bn-rBD/strings.xml create mode 100644 WallpaperPicker/res/values-eu-rES/strings.xml create mode 100644 WallpaperPicker/res/values-gl-rES/strings.xml create mode 100644 WallpaperPicker/res/values-is-rIS/strings.xml create mode 100644 WallpaperPicker/res/values-kk-rKZ/strings.xml create mode 100644 WallpaperPicker/res/values-kn-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-ky-rKG/strings.xml create mode 100644 WallpaperPicker/res/values-mk-rMK/strings.xml create mode 100644 WallpaperPicker/res/values-ml-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-mr-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-my-rMM/strings.xml create mode 100644 WallpaperPicker/res/values-ne-rNP/strings.xml create mode 100644 WallpaperPicker/res/values-si-rLK/strings.xml create mode 100644 WallpaperPicker/res/values-ta-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-te-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-ur-rPK/strings.xml create mode 100644 WallpaperPicker/res/values-uz-rUZ/strings.xml diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml index 394161634..59c3bf733 100644 --- a/WallpaperPicker/res/values-am/strings.xml +++ b/WallpaperPicker/res/values-am/strings.xml @@ -19,7 +19,7 @@ - "የግድግዳ ወረቀት አዘጋጅ" + "ልጣፍ አዘጋጅ" "ምስሉን መጫን አልተቻለም" "ምስሉን እንደ ግድግዳ ወረቀት መጫን አልተቻለም" @@ -27,10 +27,10 @@ "%1$d ተመርጧል" "%1$d ተመርጧል" - "የግድግዳ ወረቀት %1$d የ%2$d" + "ልጣፍ %1$d የ%2$d" "%1$s ተመርጧል" "ሰርዝ" "ምስል ይምረጡ" "የግድግዳ ወረቀቶች" - "የግድግዳ ወረቀት ይከርክሙ" + "ልጣፍ ይከርክሙ" diff --git a/WallpaperPicker/res/values-bn-rBD/strings.xml b/WallpaperPicker/res/values-bn-rBD/strings.xml new file mode 100644 index 000000000..a4fccb208 --- /dev/null +++ b/WallpaperPicker/res/values-bn-rBD/strings.xml @@ -0,0 +1,36 @@ + + + + + "ওয়ালপেপার সেট করুন" + "চিত্র লোড করা যায়নি" + "ওয়ালপেপার হিসাবে চিত্র লোড করা যায়নি" + + "%1$dটি নির্বাচন করা হয়েছে" + "%1$dটি নির্বাচন করা হয়েছে" + "%1$dটি নির্বাচন করা হয়েছে" + + "%2$dটির মধ্যে %1$dটি ওয়ালপেপার" + "%1$s নির্বাচন করা হয়েছে" + "মুছুন" + "চিত্র বাছুন" + "ওয়ালপেপারগুলি" + "ওয়ালপেপার কাটছাঁট করুন" + diff --git a/WallpaperPicker/res/values-eu-rES/strings.xml b/WallpaperPicker/res/values-eu-rES/strings.xml new file mode 100644 index 000000000..327550d52 --- /dev/null +++ b/WallpaperPicker/res/values-eu-rES/strings.xml @@ -0,0 +1,36 @@ + + + + + "Ezarri horma-papera" + "Ezin izan da irudia kargatu" + "Ezin izan da irudia horma-paper gisa kargatu" + + "%1$d hautatuta" + "%1$d hautatuta" + "%1$d hautatuta" + + "%1$d/%2$d horma-papera" + "%1$s hautatu da" + "Ezabatu" + "Hautatu irudia" + "Horma-paperak" + "Ebaki horma-papera" + diff --git a/WallpaperPicker/res/values-fa/strings.xml b/WallpaperPicker/res/values-fa/strings.xml index eb330b515..ef4bc4fa6 100644 --- a/WallpaperPicker/res/values-fa/strings.xml +++ b/WallpaperPicker/res/values-fa/strings.xml @@ -28,7 +28,7 @@ "‏%1$d انتخاب شد" "‏کاغذدیواری %1$d از %2$d" - "%1$s انتخاب شده است" + "%1$s انتخاب شد" "حذف" "انتخاب تصویر" "کاغذدیواری‌ها" diff --git a/WallpaperPicker/res/values-gl-rES/strings.xml b/WallpaperPicker/res/values-gl-rES/strings.xml new file mode 100644 index 000000000..9f1fd5014 --- /dev/null +++ b/WallpaperPicker/res/values-gl-rES/strings.xml @@ -0,0 +1,36 @@ + + + + + "Establecer fondo de pantalla" + "Non se puido cargar a imaxe" + "Non se puido cargar a imaxe como fondo de pantalla" + + "Seleccionaches %1$d" + "Seleccionaches %1$d" + "Seleccionaches %1$d" + + "Fondo de pantalla %1$d de %2$d" + "Seleccionaches %1$s" + "Eliminar" + "Escoller imaxe" + "Fondos de pantalla" + "Recortar fondo de pantalla" + diff --git a/WallpaperPicker/res/values-is-rIS/strings.xml b/WallpaperPicker/res/values-is-rIS/strings.xml new file mode 100644 index 000000000..6d5d680cf --- /dev/null +++ b/WallpaperPicker/res/values-is-rIS/strings.xml @@ -0,0 +1,36 @@ + + + + + "Velja veggfóður" + "Ekki var hægt að hlaða mynd" + "Ekki var hægt að hlaða mynd sem veggfóður" + + "%1$d valin" + "%1$d valið" + "%1$d valin" + + "Veggfóður %1$d af %2$d" + "%1$s valið" + "Eyða" + "Velja mynd" + "Veggfóður" + "Skera veggfóður" + diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml new file mode 100644 index 000000000..b75562ec9 --- /dev/null +++ b/WallpaperPicker/res/values-kk-rKZ/strings.xml @@ -0,0 +1,36 @@ + + + + + "Артқы фонды орнату" + "Суретті жүктей алмады" + "Суретті артқы фон ретінде жүктей алмады" + + "%1$d таңдалған" + "%1$d таңдалған" + "%1$d таңдалған" + + "%1$d артқы фон, барлығы %2$d" + "%1$s таңдалған" + "Жою" + "Суретті таңдау" + "Артқы фондар" + "Артқы фонды кесу" + diff --git a/WallpaperPicker/res/values-kn-rIN/strings.xml b/WallpaperPicker/res/values-kn-rIN/strings.xml new file mode 100644 index 000000000..89203bf1c --- /dev/null +++ b/WallpaperPicker/res/values-kn-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "ವಾಲ್‌ಪೇಪರ್ ಹೊಂದಿಸಿ" + "ಚಿತ್ರವನ್ನು ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ" + "ಚಿತ್ರವನ್ನು ವಾಲ್‌ಪೇಪರ್‌ ರೂಪದಲ್ಲಿ ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ" + + "%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + "%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + "%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + + "%2$d ರಲ್ಲಿ %1$d ವಾಲ್‌ಪೇಪರ್‌" + "%1$s ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + "ಅಳಿಸು" + "ಚಿತ್ರವನ್ನು ಆರಿಸಿ" + "ವಾಲ್‌ಪೇಪರ್‌ಗಳು" + "ವಾಲ್‌ಪೇಪರ್‌ ಕತ್ತರಿಸಿ" + diff --git a/WallpaperPicker/res/values-ky-rKG/strings.xml b/WallpaperPicker/res/values-ky-rKG/strings.xml new file mode 100644 index 000000000..696b3ee17 --- /dev/null +++ b/WallpaperPicker/res/values-ky-rKG/strings.xml @@ -0,0 +1,36 @@ + + + + + "Тушкагаз орнотуу" + "Сүрөт жүктөө мүмкүн болбоду" + "Сүрөттү тушкагаз катары жүктөө кыйрады" + + "%1$d тандалды" + "%1$d тандалды" + "%1$d тандалды" + + "%2$d ичинен %1$d тушкагаз" + "%1$s тандалды" + "Жок кылуу" + "Сүрөт тандоо" + "Тушкагаздар" + "Тушкагазды тегиздөө" + diff --git a/WallpaperPicker/res/values-mk-rMK/strings.xml b/WallpaperPicker/res/values-mk-rMK/strings.xml new file mode 100644 index 000000000..b1919ccd4 --- /dev/null +++ b/WallpaperPicker/res/values-mk-rMK/strings.xml @@ -0,0 +1,36 @@ + + + + + "Подеси тапет" + "Сликата не можеше да се вчита" + "Сликата не можеше да се вчита како тапет" + + "Избрано %1$d" + "Избрано %1$d" + "Избрано %1$d" + + "Тапет %1$d од %2$d" + "Избран %1$s" + "Избриши" + "Избери слика" + "Тапети" + "Исечи тапет" + diff --git a/WallpaperPicker/res/values-ml-rIN/strings.xml b/WallpaperPicker/res/values-ml-rIN/strings.xml new file mode 100644 index 000000000..0b6a4c108 --- /dev/null +++ b/WallpaperPicker/res/values-ml-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "വാൾപേപ്പർ സജ്ജീകരിക്കുക" + "ചിത്രം ലോഡുചെയ്യാനായില്ല" + "വാൾപേപ്പറായി ചിത്രം ലോഡുചെയ്യാനായില്ല" + + "%1$d തിരഞ്ഞെടുത്തു" + "%1$d തിരഞ്ഞെടുത്തു" + "%1$d തിരഞ്ഞെടുത്തു" + + "%1$d / %2$d വാൾപേപ്പർ" + "%1$s തിരഞ്ഞെടുത്തു" + "ഇല്ലാതാക്കുക" + "ചിത്രം തിരഞ്ഞെടുക്കുക" + "വാൾപേപ്പറുകൾ" + "വാൾപേപ്പറിന്റെ വലുപ്പം മാറ്റൽ" + diff --git a/WallpaperPicker/res/values-mr-rIN/strings.xml b/WallpaperPicker/res/values-mr-rIN/strings.xml new file mode 100644 index 000000000..33cfaa148 --- /dev/null +++ b/WallpaperPicker/res/values-mr-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "वॉलपेपर सेट करा" + "प्रतिमा लोड करू शकलो नाही" + "वॉलपेपर म्हणून प्रतिमा लोड करू शकलो नाही" + + "%1$d निवडले" + "%1$d निवडले" + "%1$d निवडले" + + "%2$d पैकी %1$d वॉलपेपर" + "%1$s निवडले" + "हटवा" + "प्रतिमा निवडा" + "वॉलपेपर" + "वॉलपेपर कापा" + diff --git a/WallpaperPicker/res/values-my-rMM/strings.xml b/WallpaperPicker/res/values-my-rMM/strings.xml new file mode 100644 index 000000000..4d5ac47df --- /dev/null +++ b/WallpaperPicker/res/values-my-rMM/strings.xml @@ -0,0 +1,36 @@ + + + + + "နောက်ခံအား သတ်မှတ်ရန်" + "ပုံရိပ် တင် မရပါ" + "ပုံရိပ်အား နောက်ခံအဖြစ် တင် မရပါ" + + "%1$d ရွေးချယ်ပြီး" + "%1$d ရွေးချယ်ပြီး" + "%1$d ရွေးချယ်ပြီး" + + "နောက်ခံ %1$d မှ %2$d" + "ရွေးချယ်ထားသော %1$s" + "ဖျက်ပါ" + "ပုံ ရွေးပါ" + "နောက်ခံများ" + "နောက်ခံအား ဖြတ်ခြင်း" + diff --git a/WallpaperPicker/res/values-ne-rNP/strings.xml b/WallpaperPicker/res/values-ne-rNP/strings.xml new file mode 100644 index 000000000..221fa9438 --- /dev/null +++ b/WallpaperPicker/res/values-ne-rNP/strings.xml @@ -0,0 +1,36 @@ + + + + + "वालपेपर मिलाउनुहोस्" + "तस्बिर लोड गर्न सकिएन" + "तस्बिरलाई वालपेपरका रूपमा लोड गर्न सकिएन" + + "%1$d चयन भयो" + "%1$d चयन भयो" + "%1$d चयन भयो" + + "%2$d को %1$d वालपेपर" + "चयन गरिएको %1$s" + "मेट्नुहोस्" + "तस्बिर छान्नुहोस्" + "वालपेपरहरु" + "वालपेपर काँटछाट गर्नुहोस्" + diff --git a/WallpaperPicker/res/values-si-rLK/strings.xml b/WallpaperPicker/res/values-si-rLK/strings.xml new file mode 100644 index 000000000..d175724fd --- /dev/null +++ b/WallpaperPicker/res/values-si-rLK/strings.xml @@ -0,0 +1,38 @@ + + + + + "වෝල්පේපරය සකසන්න" + "පින්තූරය පූරණය කිරීමට නොහැකි විය" + "පින්තූරය වෝල්පේපරයක් ලෙස පූරණය කිරීමට නොහැකි විය" + + "%1$d තෝරා ගන්නා ලදි" + "%1$d තෝරා ගන්නා ලදි" + "%1$d තෝරා ගන්නා ලදි" + + + + + "තෝරාගත්තේ %1$s" + "මකන්න" + "පින්තූරයක් තෝරන්න" + "වෝල්පේපර" + "වෝල්පේපරය කප්පාදු කිරීම" + diff --git a/WallpaperPicker/res/values-ta-rIN/strings.xml b/WallpaperPicker/res/values-ta-rIN/strings.xml new file mode 100644 index 000000000..2b984271a --- /dev/null +++ b/WallpaperPicker/res/values-ta-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "வால்பேப்பரை அமை" + "படத்தை ஏற்ற முடியவில்லை" + "படத்தை வால்பேப்பராக ஏற்ற முடியவில்லை" + + "%1$d தேர்ந்தெடுக்கப்பட்டன" + "%1$d தேர்ந்தெடுக்கப்பட்டது" + "%1$d தேர்ந்தெடுக்கப்பட்டன" + + "வால்பேப்பர் %1$d / %2$d" + "%1$s தேர்ந்தெடுக்கப்பட்டது" + "நீக்கு" + "படத்தைத் தேர்வுசெய்க" + "வால்பேப்பர்கள்" + "வால்பேப்பரைச் செதுக்கு" + diff --git a/WallpaperPicker/res/values-te-rIN/strings.xml b/WallpaperPicker/res/values-te-rIN/strings.xml new file mode 100644 index 000000000..c26f84e28 --- /dev/null +++ b/WallpaperPicker/res/values-te-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "వాల్‌పేపర్‌ను సెట్ చేయి" + "చిత్రాన్ని లోడ్ చేయడం సాధ్యపడలేదు" + "చిత్రాన్ని వాల్‌పేపర్‌గా లోడ్ చేయడం సాధ్యపడలేదు" + + "%1$d ఎంచుకోబడింది" + "%1$d ఎంచుకోబడింది" + "%1$d ఎంచుకోబడింది" + + "%2$dలో %1$dవ వాల్‌పేపర్" + "%1$s ఎంచుకోబడింది" + "తొలగించు" + "చిత్రాన్ని ఎంచుకోండి" + "వాల్‌పేపర్‌లు" + "వాల్‌పేపర్‌ను కత్తిరించండి" + diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml index c81720563..6b4c23536 100644 --- a/WallpaperPicker/res/values-th/strings.xml +++ b/WallpaperPicker/res/values-th/strings.xml @@ -29,7 +29,7 @@ "วอลเปเปอร์ %1$d จาก %2$d" "เลือก %1$s แล้ว" - "นำออก" + "ลบ" "เลือกรูปภาพ" "วอลเปเปอร์" "ครอบตัดวอลเปเปอร์" diff --git a/WallpaperPicker/res/values-ur-rPK/strings.xml b/WallpaperPicker/res/values-ur-rPK/strings.xml new file mode 100644 index 000000000..7b12d4239 --- /dev/null +++ b/WallpaperPicker/res/values-ur-rPK/strings.xml @@ -0,0 +1,36 @@ + + + + + "وال پیپر سیٹ کریں" + "تصویر کو لوڈ نہیں کیا جا سکا" + "تصویر کو وال پیپر کے بطور لوڈ نہیں کیا جا سکا" + + "‏%1$d کو منتخب کیا گیا" + "‏%1$d کو منتخب کیا گیا" + "‏%1$d کو منتخب کیا گیا" + + "‏وال پیپر ‎%1$d از ‎%2$d" + "%1$s کو منتخب کیا گیا" + "حذف کریں" + "تصویر منتخب کریں" + "وال پیپرز" + "وال پیپر کو تراشیں" + diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml new file mode 100644 index 000000000..73d7a0d1e --- /dev/null +++ b/WallpaperPicker/res/values-uz-rUZ/strings.xml @@ -0,0 +1,36 @@ + + + + + "Fonga rasm o‘rnatish" + "Rasm yuklanmadi" + "Fon rasmi sifatida rasm yuklanmadi" + + "%1$d ta tanlandi" + "%1$d tanlandi" + "%1$d ta tanlandi" + + "Fon rasmi %2$ddan %1$d" + "%1$s tanlandi" + "O‘chirish" + "Rasmni tanlang" + "Fon rasmlari" + "Fon rasmini kesish" + diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 69ec22c41..0288806f4 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -26,7 +26,7 @@ "Aplikace není nainstalována." "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" - "V Bezpečném režimu jsou widgety zakázány." + "V nouzovém režimu jsou widgety zakázány." "Widgety" "Widgety" "Zobrazit Mem" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 670ac70b5..3a1c82dcc 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -85,7 +85,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍​" + "សូម​ស្វាគមន៍" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1beb049e2..65f01b7f0 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ​" + "ຍົກ​ເລີກ" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -117,7 +117,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ​" + "ລຶບ" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 544ecdc7a..668803587 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -60,7 +60,7 @@ "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +76,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +102,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +111,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" -- cgit v1.2.3 From 0141792813d503c13450ebf4fe3b805a72866213 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Fri, 16 Jan 2015 16:07:34 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I49acadd887529f73104e7f612be9a54f7689dd97 Auto-generated-cl: translation import --- WallpaperPicker/res/values-am/strings.xml | 6 ++--- WallpaperPicker/res/values-bn-rBD/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-eu-rES/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-fa/strings.xml | 2 +- WallpaperPicker/res/values-gl-rES/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-is-rIS/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-kk-rKZ/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-kn-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-ky-rKG/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-mk-rMK/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-ml-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-mr-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-my-rMM/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-ne-rNP/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-si-rLK/strings.xml | 38 +++++++++++++++++++++++++++ WallpaperPicker/res/values-ta-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-te-rIN/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-th/strings.xml | 2 +- WallpaperPicker/res/values-ur-rPK/strings.xml | 36 +++++++++++++++++++++++++ WallpaperPicker/res/values-uz-rUZ/strings.xml | 36 +++++++++++++++++++++++++ res/values-cs/strings.xml | 2 +- res/values-km-rKH/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 4 +-- res/values-my-rMM/strings.xml | 8 +++--- 24 files changed, 627 insertions(+), 13 deletions(-) create mode 100644 WallpaperPicker/res/values-bn-rBD/strings.xml create mode 100644 WallpaperPicker/res/values-eu-rES/strings.xml create mode 100644 WallpaperPicker/res/values-gl-rES/strings.xml create mode 100644 WallpaperPicker/res/values-is-rIS/strings.xml create mode 100644 WallpaperPicker/res/values-kk-rKZ/strings.xml create mode 100644 WallpaperPicker/res/values-kn-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-ky-rKG/strings.xml create mode 100644 WallpaperPicker/res/values-mk-rMK/strings.xml create mode 100644 WallpaperPicker/res/values-ml-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-mr-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-my-rMM/strings.xml create mode 100644 WallpaperPicker/res/values-ne-rNP/strings.xml create mode 100644 WallpaperPicker/res/values-si-rLK/strings.xml create mode 100644 WallpaperPicker/res/values-ta-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-te-rIN/strings.xml create mode 100644 WallpaperPicker/res/values-ur-rPK/strings.xml create mode 100644 WallpaperPicker/res/values-uz-rUZ/strings.xml diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml index 394161634..59c3bf733 100644 --- a/WallpaperPicker/res/values-am/strings.xml +++ b/WallpaperPicker/res/values-am/strings.xml @@ -19,7 +19,7 @@ - "የግድግዳ ወረቀት አዘጋጅ" + "ልጣፍ አዘጋጅ" "ምስሉን መጫን አልተቻለም" "ምስሉን እንደ ግድግዳ ወረቀት መጫን አልተቻለም" @@ -27,10 +27,10 @@ "%1$d ተመርጧል" "%1$d ተመርጧል" - "የግድግዳ ወረቀት %1$d የ%2$d" + "ልጣፍ %1$d የ%2$d" "%1$s ተመርጧል" "ሰርዝ" "ምስል ይምረጡ" "የግድግዳ ወረቀቶች" - "የግድግዳ ወረቀት ይከርክሙ" + "ልጣፍ ይከርክሙ" diff --git a/WallpaperPicker/res/values-bn-rBD/strings.xml b/WallpaperPicker/res/values-bn-rBD/strings.xml new file mode 100644 index 000000000..a4fccb208 --- /dev/null +++ b/WallpaperPicker/res/values-bn-rBD/strings.xml @@ -0,0 +1,36 @@ + + + + + "ওয়ালপেপার সেট করুন" + "চিত্র লোড করা যায়নি" + "ওয়ালপেপার হিসাবে চিত্র লোড করা যায়নি" + + "%1$dটি নির্বাচন করা হয়েছে" + "%1$dটি নির্বাচন করা হয়েছে" + "%1$dটি নির্বাচন করা হয়েছে" + + "%2$dটির মধ্যে %1$dটি ওয়ালপেপার" + "%1$s নির্বাচন করা হয়েছে" + "মুছুন" + "চিত্র বাছুন" + "ওয়ালপেপারগুলি" + "ওয়ালপেপার কাটছাঁট করুন" + diff --git a/WallpaperPicker/res/values-eu-rES/strings.xml b/WallpaperPicker/res/values-eu-rES/strings.xml new file mode 100644 index 000000000..327550d52 --- /dev/null +++ b/WallpaperPicker/res/values-eu-rES/strings.xml @@ -0,0 +1,36 @@ + + + + + "Ezarri horma-papera" + "Ezin izan da irudia kargatu" + "Ezin izan da irudia horma-paper gisa kargatu" + + "%1$d hautatuta" + "%1$d hautatuta" + "%1$d hautatuta" + + "%1$d/%2$d horma-papera" + "%1$s hautatu da" + "Ezabatu" + "Hautatu irudia" + "Horma-paperak" + "Ebaki horma-papera" + diff --git a/WallpaperPicker/res/values-fa/strings.xml b/WallpaperPicker/res/values-fa/strings.xml index eb330b515..ef4bc4fa6 100644 --- a/WallpaperPicker/res/values-fa/strings.xml +++ b/WallpaperPicker/res/values-fa/strings.xml @@ -28,7 +28,7 @@ "‏%1$d انتخاب شد" "‏کاغذدیواری %1$d از %2$d" - "%1$s انتخاب شده است" + "%1$s انتخاب شد" "حذف" "انتخاب تصویر" "کاغذدیواری‌ها" diff --git a/WallpaperPicker/res/values-gl-rES/strings.xml b/WallpaperPicker/res/values-gl-rES/strings.xml new file mode 100644 index 000000000..9f1fd5014 --- /dev/null +++ b/WallpaperPicker/res/values-gl-rES/strings.xml @@ -0,0 +1,36 @@ + + + + + "Establecer fondo de pantalla" + "Non se puido cargar a imaxe" + "Non se puido cargar a imaxe como fondo de pantalla" + + "Seleccionaches %1$d" + "Seleccionaches %1$d" + "Seleccionaches %1$d" + + "Fondo de pantalla %1$d de %2$d" + "Seleccionaches %1$s" + "Eliminar" + "Escoller imaxe" + "Fondos de pantalla" + "Recortar fondo de pantalla" + diff --git a/WallpaperPicker/res/values-is-rIS/strings.xml b/WallpaperPicker/res/values-is-rIS/strings.xml new file mode 100644 index 000000000..6d5d680cf --- /dev/null +++ b/WallpaperPicker/res/values-is-rIS/strings.xml @@ -0,0 +1,36 @@ + + + + + "Velja veggfóður" + "Ekki var hægt að hlaða mynd" + "Ekki var hægt að hlaða mynd sem veggfóður" + + "%1$d valin" + "%1$d valið" + "%1$d valin" + + "Veggfóður %1$d af %2$d" + "%1$s valið" + "Eyða" + "Velja mynd" + "Veggfóður" + "Skera veggfóður" + diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml new file mode 100644 index 000000000..b75562ec9 --- /dev/null +++ b/WallpaperPicker/res/values-kk-rKZ/strings.xml @@ -0,0 +1,36 @@ + + + + + "Артқы фонды орнату" + "Суретті жүктей алмады" + "Суретті артқы фон ретінде жүктей алмады" + + "%1$d таңдалған" + "%1$d таңдалған" + "%1$d таңдалған" + + "%1$d артқы фон, барлығы %2$d" + "%1$s таңдалған" + "Жою" + "Суретті таңдау" + "Артқы фондар" + "Артқы фонды кесу" + diff --git a/WallpaperPicker/res/values-kn-rIN/strings.xml b/WallpaperPicker/res/values-kn-rIN/strings.xml new file mode 100644 index 000000000..89203bf1c --- /dev/null +++ b/WallpaperPicker/res/values-kn-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "ವಾಲ್‌ಪೇಪರ್ ಹೊಂದಿಸಿ" + "ಚಿತ್ರವನ್ನು ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ" + "ಚಿತ್ರವನ್ನು ವಾಲ್‌ಪೇಪರ್‌ ರೂಪದಲ್ಲಿ ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ" + + "%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + "%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + "%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + + "%2$d ರಲ್ಲಿ %1$d ವಾಲ್‌ಪೇಪರ್‌" + "%1$s ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + "ಅಳಿಸು" + "ಚಿತ್ರವನ್ನು ಆರಿಸಿ" + "ವಾಲ್‌ಪೇಪರ್‌ಗಳು" + "ವಾಲ್‌ಪೇಪರ್‌ ಕತ್ತರಿಸಿ" + diff --git a/WallpaperPicker/res/values-ky-rKG/strings.xml b/WallpaperPicker/res/values-ky-rKG/strings.xml new file mode 100644 index 000000000..696b3ee17 --- /dev/null +++ b/WallpaperPicker/res/values-ky-rKG/strings.xml @@ -0,0 +1,36 @@ + + + + + "Тушкагаз орнотуу" + "Сүрөт жүктөө мүмкүн болбоду" + "Сүрөттү тушкагаз катары жүктөө кыйрады" + + "%1$d тандалды" + "%1$d тандалды" + "%1$d тандалды" + + "%2$d ичинен %1$d тушкагаз" + "%1$s тандалды" + "Жок кылуу" + "Сүрөт тандоо" + "Тушкагаздар" + "Тушкагазды тегиздөө" + diff --git a/WallpaperPicker/res/values-mk-rMK/strings.xml b/WallpaperPicker/res/values-mk-rMK/strings.xml new file mode 100644 index 000000000..b1919ccd4 --- /dev/null +++ b/WallpaperPicker/res/values-mk-rMK/strings.xml @@ -0,0 +1,36 @@ + + + + + "Подеси тапет" + "Сликата не можеше да се вчита" + "Сликата не можеше да се вчита како тапет" + + "Избрано %1$d" + "Избрано %1$d" + "Избрано %1$d" + + "Тапет %1$d од %2$d" + "Избран %1$s" + "Избриши" + "Избери слика" + "Тапети" + "Исечи тапет" + diff --git a/WallpaperPicker/res/values-ml-rIN/strings.xml b/WallpaperPicker/res/values-ml-rIN/strings.xml new file mode 100644 index 000000000..0b6a4c108 --- /dev/null +++ b/WallpaperPicker/res/values-ml-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "വാൾപേപ്പർ സജ്ജീകരിക്കുക" + "ചിത്രം ലോഡുചെയ്യാനായില്ല" + "വാൾപേപ്പറായി ചിത്രം ലോഡുചെയ്യാനായില്ല" + + "%1$d തിരഞ്ഞെടുത്തു" + "%1$d തിരഞ്ഞെടുത്തു" + "%1$d തിരഞ്ഞെടുത്തു" + + "%1$d / %2$d വാൾപേപ്പർ" + "%1$s തിരഞ്ഞെടുത്തു" + "ഇല്ലാതാക്കുക" + "ചിത്രം തിരഞ്ഞെടുക്കുക" + "വാൾപേപ്പറുകൾ" + "വാൾപേപ്പറിന്റെ വലുപ്പം മാറ്റൽ" + diff --git a/WallpaperPicker/res/values-mr-rIN/strings.xml b/WallpaperPicker/res/values-mr-rIN/strings.xml new file mode 100644 index 000000000..33cfaa148 --- /dev/null +++ b/WallpaperPicker/res/values-mr-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "वॉलपेपर सेट करा" + "प्रतिमा लोड करू शकलो नाही" + "वॉलपेपर म्हणून प्रतिमा लोड करू शकलो नाही" + + "%1$d निवडले" + "%1$d निवडले" + "%1$d निवडले" + + "%2$d पैकी %1$d वॉलपेपर" + "%1$s निवडले" + "हटवा" + "प्रतिमा निवडा" + "वॉलपेपर" + "वॉलपेपर कापा" + diff --git a/WallpaperPicker/res/values-my-rMM/strings.xml b/WallpaperPicker/res/values-my-rMM/strings.xml new file mode 100644 index 000000000..4d5ac47df --- /dev/null +++ b/WallpaperPicker/res/values-my-rMM/strings.xml @@ -0,0 +1,36 @@ + + + + + "နောက်ခံအား သတ်မှတ်ရန်" + "ပုံရိပ် တင် မရပါ" + "ပုံရိပ်အား နောက်ခံအဖြစ် တင် မရပါ" + + "%1$d ရွေးချယ်ပြီး" + "%1$d ရွေးချယ်ပြီး" + "%1$d ရွေးချယ်ပြီး" + + "နောက်ခံ %1$d မှ %2$d" + "ရွေးချယ်ထားသော %1$s" + "ဖျက်ပါ" + "ပုံ ရွေးပါ" + "နောက်ခံများ" + "နောက်ခံအား ဖြတ်ခြင်း" + diff --git a/WallpaperPicker/res/values-ne-rNP/strings.xml b/WallpaperPicker/res/values-ne-rNP/strings.xml new file mode 100644 index 000000000..221fa9438 --- /dev/null +++ b/WallpaperPicker/res/values-ne-rNP/strings.xml @@ -0,0 +1,36 @@ + + + + + "वालपेपर मिलाउनुहोस्" + "तस्बिर लोड गर्न सकिएन" + "तस्बिरलाई वालपेपरका रूपमा लोड गर्न सकिएन" + + "%1$d चयन भयो" + "%1$d चयन भयो" + "%1$d चयन भयो" + + "%2$d को %1$d वालपेपर" + "चयन गरिएको %1$s" + "मेट्नुहोस्" + "तस्बिर छान्नुहोस्" + "वालपेपरहरु" + "वालपेपर काँटछाट गर्नुहोस्" + diff --git a/WallpaperPicker/res/values-si-rLK/strings.xml b/WallpaperPicker/res/values-si-rLK/strings.xml new file mode 100644 index 000000000..d175724fd --- /dev/null +++ b/WallpaperPicker/res/values-si-rLK/strings.xml @@ -0,0 +1,38 @@ + + + + + "වෝල්පේපරය සකසන්න" + "පින්තූරය පූරණය කිරීමට නොහැකි විය" + "පින්තූරය වෝල්පේපරයක් ලෙස පූරණය කිරීමට නොහැකි විය" + + "%1$d තෝරා ගන්නා ලදි" + "%1$d තෝරා ගන්නා ලදි" + "%1$d තෝරා ගන්නා ලදි" + + + + + "තෝරාගත්තේ %1$s" + "මකන්න" + "පින්තූරයක් තෝරන්න" + "වෝල්පේපර" + "වෝල්පේපරය කප්පාදු කිරීම" + diff --git a/WallpaperPicker/res/values-ta-rIN/strings.xml b/WallpaperPicker/res/values-ta-rIN/strings.xml new file mode 100644 index 000000000..2b984271a --- /dev/null +++ b/WallpaperPicker/res/values-ta-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "வால்பேப்பரை அமை" + "படத்தை ஏற்ற முடியவில்லை" + "படத்தை வால்பேப்பராக ஏற்ற முடியவில்லை" + + "%1$d தேர்ந்தெடுக்கப்பட்டன" + "%1$d தேர்ந்தெடுக்கப்பட்டது" + "%1$d தேர்ந்தெடுக்கப்பட்டன" + + "வால்பேப்பர் %1$d / %2$d" + "%1$s தேர்ந்தெடுக்கப்பட்டது" + "நீக்கு" + "படத்தைத் தேர்வுசெய்க" + "வால்பேப்பர்கள்" + "வால்பேப்பரைச் செதுக்கு" + diff --git a/WallpaperPicker/res/values-te-rIN/strings.xml b/WallpaperPicker/res/values-te-rIN/strings.xml new file mode 100644 index 000000000..c26f84e28 --- /dev/null +++ b/WallpaperPicker/res/values-te-rIN/strings.xml @@ -0,0 +1,36 @@ + + + + + "వాల్‌పేపర్‌ను సెట్ చేయి" + "చిత్రాన్ని లోడ్ చేయడం సాధ్యపడలేదు" + "చిత్రాన్ని వాల్‌పేపర్‌గా లోడ్ చేయడం సాధ్యపడలేదు" + + "%1$d ఎంచుకోబడింది" + "%1$d ఎంచుకోబడింది" + "%1$d ఎంచుకోబడింది" + + "%2$dలో %1$dవ వాల్‌పేపర్" + "%1$s ఎంచుకోబడింది" + "తొలగించు" + "చిత్రాన్ని ఎంచుకోండి" + "వాల్‌పేపర్‌లు" + "వాల్‌పేపర్‌ను కత్తిరించండి" + diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml index c81720563..6b4c23536 100644 --- a/WallpaperPicker/res/values-th/strings.xml +++ b/WallpaperPicker/res/values-th/strings.xml @@ -29,7 +29,7 @@ "วอลเปเปอร์ %1$d จาก %2$d" "เลือก %1$s แล้ว" - "นำออก" + "ลบ" "เลือกรูปภาพ" "วอลเปเปอร์" "ครอบตัดวอลเปเปอร์" diff --git a/WallpaperPicker/res/values-ur-rPK/strings.xml b/WallpaperPicker/res/values-ur-rPK/strings.xml new file mode 100644 index 000000000..7b12d4239 --- /dev/null +++ b/WallpaperPicker/res/values-ur-rPK/strings.xml @@ -0,0 +1,36 @@ + + + + + "وال پیپر سیٹ کریں" + "تصویر کو لوڈ نہیں کیا جا سکا" + "تصویر کو وال پیپر کے بطور لوڈ نہیں کیا جا سکا" + + "‏%1$d کو منتخب کیا گیا" + "‏%1$d کو منتخب کیا گیا" + "‏%1$d کو منتخب کیا گیا" + + "‏وال پیپر ‎%1$d از ‎%2$d" + "%1$s کو منتخب کیا گیا" + "حذف کریں" + "تصویر منتخب کریں" + "وال پیپرز" + "وال پیپر کو تراشیں" + diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml new file mode 100644 index 000000000..73d7a0d1e --- /dev/null +++ b/WallpaperPicker/res/values-uz-rUZ/strings.xml @@ -0,0 +1,36 @@ + + + + + "Fonga rasm o‘rnatish" + "Rasm yuklanmadi" + "Fon rasmi sifatida rasm yuklanmadi" + + "%1$d ta tanlandi" + "%1$d tanlandi" + "%1$d ta tanlandi" + + "Fon rasmi %2$ddan %1$d" + "%1$s tanlandi" + "O‘chirish" + "Rasmni tanlang" + "Fon rasmlari" + "Fon rasmini kesish" + diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 69ec22c41..0288806f4 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -26,7 +26,7 @@ "Aplikace není nainstalována." "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" - "V Bezpečném režimu jsou widgety zakázány." + "V nouzovém režimu jsou widgety zakázány." "Widgety" "Widgety" "Zobrazit Mem" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 670ac70b5..3a1c82dcc 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -85,7 +85,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍​" + "សូម​ស្វាគមន៍" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1beb049e2..65f01b7f0 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ​" + "ຍົກ​ເລີກ" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -117,7 +117,7 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ​" + "ລຶບ" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 544ecdc7a..668803587 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -60,7 +60,7 @@ "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +76,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +102,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +111,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" -- cgit v1.2.3 From e6b63a3d7335fed5b7bbcc6d2c6230ae28facd3e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 16 Jan 2015 16:14:29 -0800 Subject: Launcher crashes on widget bind permission prompt Change-Id: If09feb357e1604e5ee1a66305b022674f466833e --- src/com/android/launcher3/Launcher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0ceb862a7..f30867b9b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -770,7 +770,7 @@ public class Launcher extends Activity mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_OK) { - addAppWidgetImpl(appWidgetId, (PendingAddWidgetInfo) mPendingAddInfo, null, + addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); } return; @@ -2257,12 +2257,12 @@ public class Launcher extends Activity mPendingAddInfo.dropPos = null; } - void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, final + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) { addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0); } - void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo, int delay) { if (appWidgetInfo.configure != null) { -- cgit v1.2.3 From 71b5c0b988a64b3a0613ded5403749bc537ee8a5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 8 Jan 2015 16:59:04 -0800 Subject: Adding accessibility controls > Adding 'Remove' option to workspace items > Adding 'Add to workspace' to all apps and widget list items, which adds the item to the first available space, giving preference to the current workspace screen > Adding 'App info' and 'Uninstall' options to appropriate items Bug: 18482913 Change-Id: Ifab7423af2d9ba502b5a2771b37bb5436b3df937 --- res/layout/search_drop_target_bar.xml | 2 +- res/values-sw600dp/config.xml | 4 - res/values-sw720dp/config.xml | 3 - res/values/config.xml | 14 +- res/values/strings.xml | 7 +- src/com/android/launcher3/BubbleTextView.java | 36 +- src/com/android/launcher3/CellLayout.java | 8 +- src/com/android/launcher3/DeleteDropTarget.java | 83 +++-- src/com/android/launcher3/FolderIcon.java | 1 + src/com/android/launcher3/InfoDropTarget.java | 30 +- src/com/android/launcher3/Launcher.java | 71 ++-- .../launcher3/LauncherAccessibilityDelegate.java | 109 ++++++ src/com/android/launcher3/LauncherAppState.java | 10 + .../launcher3/LauncherAppWidgetHostView.java | 1 + src/com/android/launcher3/LauncherModel.java | 406 +++++++++++---------- src/com/android/launcher3/PagedViewWidget.java | 2 +- src/com/android/launcher3/PreloadIconDrawable.java | 5 +- src/com/android/launcher3/Workspace.java | 61 +--- 18 files changed, 494 insertions(+), 359 deletions(-) create mode 100644 src/com/android/launcher3/LauncherAccessibilityDelegate.java diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml index af2d01634..0d7167e5b 100644 --- a/res/layout/search_drop_target_bar.xml +++ b/res/layout/search_drop_target_bar.xml @@ -38,7 +38,7 @@ android:id="@+id/delete_target_text" style="@style/DropTargetButton" android:drawableStart="@drawable/remove_target_selector" - android:text="@string/delete_zone_label_workspace" /> + android:text="@string/delete_target_label" /> -1000 - - - 14000 diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml index c00b5943f..3d2ca8318 100644 --- a/res/values-sw720dp/config.xml +++ b/res/values-sw720dp/config.xml @@ -10,9 +10,6 @@ false - - 18000 - false diff --git a/res/values/config.xml b/res/values/config.xml index 96bd13bbb..cbec51216 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -36,11 +36,6 @@ 220 - 350 - 600 - 7 - 250 - 200 300 250 @@ -90,9 +85,6 @@ 80 250 - - 8000 - true @@ -106,4 +98,10 @@ + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 4af1f7347..8b7e6c199 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -118,9 +118,6 @@ s --> Home - - Remove Uninstall @@ -304,4 +301,8 @@ s --> The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually. + + + + Add To Workspace diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 07f3045a5..f9255e6bd 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -49,7 +49,15 @@ public class BubbleTextView extends TextView { private static final int SHADOW_SMALL_COLOUR = 0xCC000000; static final float PADDING_V = 3.0f; - private HolographicOutlineHelper mOutlineHelper; + + private final Drawable mBackground; + private final CheckLongPressHelper mLongPressHelper; + private final HolographicOutlineHelper mOutlineHelper; + + // TODO: Remove custom background handling code, as no instance of BubbleTextView use any + // background. + private boolean mBackgroundSizeChanged; + private Bitmap mPressedBackground; private float mSlop; @@ -58,14 +66,8 @@ public class BubbleTextView extends TextView { private final boolean mCustomShadowsEnabled; private boolean mIsTextVisible; - // TODO: Remove custom background handling code, as no instance of BubbleTextView use any - // background. - private boolean mBackgroundSizeChanged; - private final Drawable mBackground; - private boolean mStayPressed; private boolean mIgnorePressedStateChange; - private CheckLongPressHelper mLongPressHelper; public BubbleTextView(Context context) { this(context, null, 0); @@ -90,7 +92,14 @@ public class BubbleTextView extends TextView { } else { mBackground = null; } - init(); + mLongPressHelper = new CheckLongPressHelper(this); + + mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); + if (mCustomShadowsEnabled) { + setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR); + } + + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } public void onFinishInflate() { @@ -102,15 +111,6 @@ public class BubbleTextView extends TextView { setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); } - private void init() { - mLongPressHelper = new CheckLongPressHelper(this); - - mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); - if (mCustomShadowsEnabled) { - setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR); - } - } - public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, boolean setDefaultPadding) { applyFromShortcutInfo(info, iconCache, setDefaultPadding, false); @@ -328,7 +328,7 @@ public class BubbleTextView extends TextView { Drawable top = getCompoundDrawables()[1]; if (top instanceof PreloadIconDrawable) { - ((PreloadIconDrawable) top).applyTheme(getPreloaderTheme()); + ((PreloadIconDrawable) top).applyPreloaderTheme(getPreloaderTheme()); } mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 7424d61c1..e6865b2e6 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -2981,11 +2981,11 @@ public class CellLayout extends ViewGroup { static boolean findVacantCell(int[] vacant, int spanX, int spanY, int xCount, int yCount, boolean[][] occupied) { - for (int y = 0; y < yCount; y++) { - for (int x = 0; x < xCount; x++) { + for (int y = 0; (y + spanY) <= yCount; y++) { + for (int x = 0; (x + spanX) <= xCount; x++) { boolean available = !occupied[x][y]; -out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { - for (int j = y; j < y + spanY - 1 && y < yCount; j++) { +out: for (int i = x; i < x + spanX; i++) { + for (int j = y; j < y + spanY; j++) { available = available && !occupied[i][j]; if (!available) break out; } diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 5a5c0027e..ebe874f38 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.res.ColorStateList; @@ -115,15 +116,6 @@ public class DeleteDropTarget extends ButtonDropTarget { private boolean isDragSourceWorkspaceOrFolder(DragObject d) { return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder); } - private boolean isWorkspaceOrFolderApplication(DragObject d) { - return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof ShortcutInfo); - } - private boolean isWorkspaceOrFolderWidget(DragObject d) { - return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof LauncherAppWidgetInfo); - } - private boolean isWorkspaceFolder(DragObject d) { - return (d.dragSource instanceof Workspace) && (d.dragInfo instanceof FolderInfo); - } private void setHoverColor() { if (mCurrentDrawable != null) { @@ -177,6 +169,7 @@ public class DeleteDropTarget extends ButtonDropTarget { return false; } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void onDragStart(DragSource source, Object info, int dragAction) { boolean isVisible = true; @@ -284,7 +277,8 @@ public class DeleteDropTarget extends ButtonDropTarget { } private boolean isUninstallFromWorkspace(DragObject d) { - if (LauncherAppState.isDisableAllApps() && isWorkspaceOrFolderApplication(d)) { + if (LauncherAppState.isDisableAllApps() && isDragSourceWorkspaceOrFolder(d) + && (d.dragInfo instanceof ShortcutInfo)) { ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo; // Only allow manifest shortcuts to initiate an un-install. return !InstallShortcutReceiver.isValidShortcutLaunchIntent(shortcut.intent); @@ -297,10 +291,7 @@ public class DeleteDropTarget extends ButtonDropTarget { boolean wasWaitingForUninstall = mWaitingForUninstall; mWaitingForUninstall = false; if (isAllAppsApplication(d.dragSource, item)) { - // Uninstall the application if it is being dragged from AppsCustomize - AppInfo appInfo = (AppInfo) item; - mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags, - appInfo.user); + uninstallApp(mLauncher, (AppInfo) item); } else if (isUninstallFromWorkspace(d)) { ShortcutInfo shortcut = (ShortcutInfo) item; if (shortcut.intent != null && shortcut.intent.getComponent() != null) { @@ -329,40 +320,62 @@ public class DeleteDropTarget extends ButtonDropTarget { mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess); } } - } else if (isWorkspaceOrFolderApplication(d)) { - LauncherModel.deleteItemFromDatabase(mLauncher, item); - } else if (isWorkspaceFolder(d)) { - // Remove the folder from the workspace and delete the contents from launcher model - FolderInfo folderInfo = (FolderInfo) item; - mLauncher.removeFolder(folderInfo); - LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo); - } else if (isWorkspaceOrFolderWidget(d)) { + } else if (isDragSourceWorkspaceOrFolder(d)) { + removeWorkspaceOrFolderItem(mLauncher, item, null); + } + if (wasWaitingForUninstall && !mWaitingForUninstall) { + if (d.dragSource instanceof Folder) { + ((Folder) d.dragSource).onUninstallActivityReturned(false); + } else if (d.dragSource instanceof Workspace) { + ((Workspace) d.dragSource).onUninstallActivityReturned(false); + } + } + } + + public static void uninstallApp(Launcher launcher, AppInfo info) { + launcher.startApplicationUninstallActivity(info.componentName, info.flags, info.user); + } + + /** + * Removes the item from the workspace. If the view is not null, it also removes the view. + * @return true if the item was removed. + */ + public static boolean removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) { + if (item instanceof ShortcutInfo) { + LauncherModel.deleteItemFromDatabase(launcher, item); + } else if (item instanceof FolderInfo) { + FolderInfo folder = (FolderInfo) item; + launcher.removeFolder(folder); + LauncherModel.deleteFolderContentsFromDatabase(launcher, folder); + } else if (item instanceof LauncherAppWidgetInfo) { + final LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) item; + // Remove the widget from the workspace - mLauncher.removeAppWidget((LauncherAppWidgetInfo) item); - LauncherModel.deleteItemFromDatabase(mLauncher, item); + launcher.removeAppWidget(widget); + LauncherModel.deleteItemFromDatabase(launcher, widget); - final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item; - final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost(); + final LauncherAppWidgetHost appWidgetHost = launcher.getAppWidgetHost(); - if (appWidgetHost != null && !launcherAppWidgetInfo.isCustomWidget() - && launcherAppWidgetInfo.isWidgetIdValid()) { + if (appWidgetHost != null && !widget.isCustomWidget() + && widget.isWidgetIdValid()) { // Deleting an app widget ID is a void call but writes to disk before returning // to the caller... new AsyncTask() { public Void doInBackground(Void ... args) { - appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId); + appWidgetHost.deleteAppWidgetId(widget.appWidgetId); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } + } else { + return false; } - if (wasWaitingForUninstall && !mWaitingForUninstall) { - if (d.dragSource instanceof Folder) { - ((Folder) d.dragSource).onUninstallActivityReturned(false); - } else if (d.dragSource instanceof Workspace) { - ((Workspace) d.dragSource).onUninstallActivityReturned(false); - } + + if (view != null) { + launcher.getWorkspace().removeWorkspaceItem(view); + launcher.getWorkspace().stripEmptyScreens(); } + return true; } public void onDrop(DragObject d) { diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index a359f1180..a3e82959a 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -127,6 +127,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void init() { mLongPressHelper = new CheckLongPressHelper(this); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } public boolean isDropEnabled() { diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index 7e55af228..3c36361aa 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -76,28 +76,32 @@ public class InfoDropTarget extends ButtonDropTarget { // acceptDrop is called just before onDrop. We do the work here, rather than // in onDrop, because it allows us to reject the drop (by returning false) // so that the object being dragged isn't removed from the drag source. + + startDetailsActivityForInfo(d.dragInfo, mLauncher); + // There is no post-drop animation, so clean up the DragView now + d.deferDragViewCleanupPostAnimation = false; + return false; + } + + public static void startDetailsActivityForInfo(Object info, Launcher launcher) { ComponentName componentName = null; - if (d.dragInfo instanceof AppInfo) { - componentName = ((AppInfo) d.dragInfo).componentName; - } else if (d.dragInfo instanceof ShortcutInfo) { - componentName = ((ShortcutInfo) d.dragInfo).intent.getComponent(); - } else if (d.dragInfo instanceof PendingAddItemInfo) { - componentName = ((PendingAddItemInfo) d.dragInfo).componentName; + if (info instanceof AppInfo) { + componentName = ((AppInfo) info).componentName; + } else if (info instanceof ShortcutInfo) { + componentName = ((ShortcutInfo) info).intent.getComponent(); + } else if (info instanceof PendingAddItemInfo) { + componentName = ((PendingAddItemInfo) info).componentName; } final UserHandleCompat user; - if (d.dragInfo instanceof ItemInfo) { - user = ((ItemInfo) d.dragInfo).user; + if (info instanceof ItemInfo) { + user = ((ItemInfo) info).user; } else { user = UserHandleCompat.myUserHandle(); } if (componentName != null) { - mLauncher.startApplicationDetailsActivity(componentName, user); + launcher.startApplicationDetailsActivity(componentName, user); } - - // There is no post-drop animation, so clean up the DragView now - d.deferDragViewCleanupPostAnimation = false; - return false; } @Override diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0ceb862a7..5d8e136cd 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -162,7 +162,6 @@ public class Launcher extends Activity static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; static final int SCREEN_COUNT = 5; - static final int DEFAULT_SCREEN = 2; // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] @@ -232,7 +231,6 @@ public class Launcher extends Activity private static final int ACTIVITY_START_DELAY = 1000; private static final Object sLock = new Object(); - private static int sScreen = DEFAULT_SCREEN; private HashMap mItemIdToViewId = new HashMap(); private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); @@ -675,18 +673,7 @@ public class Launcher extends Activity return !mModel.isLoadingWorkspace(); } - static int getScreen() { - synchronized (sLock) { - return sScreen; - } - } - - static void setScreen(int screen) { - synchronized (sLock) { - sScreen = screen; - } - } - + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static int generateViewId() { if (Build.VERSION.SDK_INT >= 17) { return View.generateViewId(); @@ -1594,7 +1581,6 @@ public class Launcher extends Activity * Add a widget to the workspace. * * @param appWidgetId The app widget id - * @param cellInfo The position on screen where to create the widget. */ private void completeAddAppWidget(int appWidgetId, long container, long screenId, AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) { @@ -2294,20 +2280,39 @@ public class Launcher extends Activity closeFolder(); mWorkspace.moveToCustomContentScreen(animate); } + + public void addPendingItem(PendingAddItemInfo info, long container, long screenId, + int[] cell, int spanX, int spanY) { + switch (info.itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + int span[] = new int[2]; + span[0] = spanX; + span[1] = spanY; + addAppWidgetFromDrop((PendingAddWidgetInfo) info, + container, screenId, cell, span); + break; + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + processShortcutFromDrop(info.componentName, container, screenId, cell); + break; + default: + throw new IllegalStateException("Unknown item type: " + info.itemType); + } + } + /** * Process a shortcut drop. * * @param componentName The name of the component * @param screenId The ID of the screen where it should be added * @param cell The cell it should be added to, optional - * @param position The location on the screen where it was dropped, optional */ - void processShortcutFromDrop(ComponentName componentName, long container, long screenId, - int[] cell, int[] loc) { + private void processShortcutFromDrop(ComponentName componentName, long container, long screenId, + int[] cell) { resetAddInfo(); mPendingAddInfo.container = container; mPendingAddInfo.screenId = screenId; - mPendingAddInfo.dropPos = loc; + mPendingAddInfo.dropPos = null; if (cell != null) { mPendingAddInfo.cellX = cell[0]; @@ -2325,14 +2330,13 @@ public class Launcher extends Activity * @param info The PendingAppWidgetInfo of the widget being added. * @param screenId The ID of the screen where it should be added * @param cell The cell it should be added to, optional - * @param position The location on the screen where it was dropped, optional */ - void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId, - int[] cell, int[] span, int[] loc) { + private void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId, + int[] cell, int[] span) { resetAddInfo(); mPendingAddInfo.container = info.container = container; mPendingAddInfo.screenId = info.screenId = screenId; - mPendingAddInfo.dropPos = loc; + mPendingAddInfo.dropPos = null; mPendingAddInfo.minSpanX = info.minSpanX; mPendingAddInfo.minSpanY = info.minSpanY; @@ -3291,6 +3295,7 @@ public class Launcher extends Activity showAppsCustomizeHelper(animated, springLoaded, contentType); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded, final AppsCustomizePagedView.ContentType contentType) { if (mStateAnimation != null) { @@ -3303,13 +3308,10 @@ public class Launcher extends Activity final Resources res = getResources(); - final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime); - final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime); final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); final int itemsAlphaStagger = res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); - final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor); final View fromView = mWorkspace; final AppsCustomizeTabHost toView = mAppsCustomizeTabHost; @@ -3536,14 +3538,10 @@ public class Launcher extends Activity boolean material = Utilities.isLmpOrAbove(); Resources res = getResources(); - final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime); - final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime); final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime); final int itemsAlphaStagger = res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); - final float scaleFactor = (float) - res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor); final View fromView = mAppsCustomizeTabHost; final View toView = mWorkspace; Animator workspaceAnim = null; @@ -4139,6 +4137,19 @@ public class Launcher extends Activity } } + @Override + public void bindAddPendingItem(final PendingAddItemInfo info, final long container, + final long screenId, final int[] cell, final int spanX, final int spanY) { + showWorkspace(true, new Runnable() { + + @Override + public void run() { + mWorkspace.snapToPage(mWorkspace.getPageIndexForScreenId(screenId)); + addPendingItem(info, container, screenId, cell, spanX, spanY); + } + }); + } + private boolean shouldShowWeightWatcher() { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java new file mode 100644 index 000000000..c9e277e4c --- /dev/null +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -0,0 +1,109 @@ +package com.android.launcher3; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Bundle; +import android.util.SparseArray; +import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import com.android.launcher3.LauncherModel.ScreenPosProvider; + +import java.util.ArrayList; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class LauncherAccessibilityDelegate extends AccessibilityDelegate { + + public static final int REMOVE = R.id.action_remove; + public static final int INFO = R.id.action_info; + public static final int UNINSTALL = R.id.action_uninstall; + public static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; + + private final SparseArray mActions = + new SparseArray(); + private final Launcher mLauncher; + + public LauncherAccessibilityDelegate(Launcher launcher) { + mLauncher = launcher; + + mActions.put(REMOVE, new AccessibilityAction(REMOVE, + launcher.getText(R.string.delete_target_label))); + mActions.put(INFO, new AccessibilityAction(INFO, + launcher.getText(R.string.info_target_label))); + mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL, + launcher.getText(R.string.delete_target_uninstall_label))); + mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE, + launcher.getText(R.string.action_add_to_workspace))); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + if (!(host.getTag() instanceof ItemInfo)) return; + ItemInfo item = (ItemInfo) host.getTag(); + + if ((item instanceof ShortcutInfo) + || (item instanceof LauncherAppWidgetInfo) + || (item instanceof FolderInfo)) { + // Workspace shortcut / widget + info.addAction(mActions.get(REMOVE)); + } else if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { + // App or Widget from customization tray + if (item instanceof AppInfo) { + info.addAction(mActions.get(UNINSTALL)); + } + info.addAction(mActions.get(INFO)); + info.addAction(mActions.get(ADD_TO_WORKSPACE)); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if ((host.getTag() instanceof ItemInfo) + && performAction(host, (ItemInfo) host.getTag(), action)) { + return true; + } + return super.performAccessibilityAction(host, action, args); + } + + public boolean performAction(View host, ItemInfo item, int action) { + if (action == REMOVE) { + return DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host); + } else if (action == INFO) { + InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); + return true; + } else if (action == UNINSTALL) { + DeleteDropTarget.uninstallApp(mLauncher, (AppInfo) item); + return true; + } else if (action == ADD_TO_WORKSPACE) { + final int preferredPage = mLauncher.getWorkspace().getCurrentPage(); + final ScreenPosProvider screenProvider = new ScreenPosProvider() { + + @Override + public int getScreenIndex(ArrayList screenIDs) { + return preferredPage; + } + }; + if (item instanceof AppInfo) { + final ArrayList addShortcuts = new ArrayList(); + addShortcuts.add(((AppInfo) item).makeShortcut()); + mLauncher.showWorkspace(true, new Runnable() { + + @Override + public void run() { + mLauncher.getModel().addAndBindAddedWorkspaceApps( + mLauncher, addShortcuts, screenProvider, 0, true); + } + }); + return true; + } else if (item instanceof PendingAddItemInfo) { + mLauncher.getModel().addAndBindPendingItem( + mLauncher, (PendingAddItemInfo) item, screenProvider, 0); + return true; + } + } + return false; + } +} diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index b7c45a340..87e9aae15 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -32,10 +32,13 @@ import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import android.view.View.AccessibilityDelegate; import android.view.WindowManager; + import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; + import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -62,6 +65,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private static LauncherAppState INSTANCE; private DynamicGrid mDynamicGrid; + private AccessibilityDelegate mAccessibilityDelegate; public static LauncherAppState getInstance() { if (INSTANCE == null) { @@ -162,9 +166,15 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { LauncherModel setLauncher(Launcher launcher) { mModel.initialize(launcher); + mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ? + new LauncherAccessibilityDelegate(launcher) : null; return mModel; } + AccessibilityDelegate getAccessibilityDelegate() { + return mAccessibilityDelegate; + } + public IconCache getIconCache() { return mIconCache; } diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index ebe55ab78..2d04df2de 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -48,6 +48,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc mLongPressHelper = new CheckLongPressHelper(this); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mDragLayer = ((Launcher) context).getDragLayer(); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @Override diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 95ebaec87..7b5f8466a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -29,7 +29,6 @@ import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.LauncherApps.Callback; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -38,6 +37,7 @@ import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Rect; import android.net.Uri; import android.os.Environment; import android.os.Handler; @@ -49,6 +49,7 @@ import android.os.SystemClock; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; +import android.util.LongSparseArray; import android.util.Pair; import com.android.launcher3.compat.AppWidgetManagerCompat; @@ -108,7 +109,6 @@ public class LauncherModel extends BroadcastReceiver private DeferredHandler mHandler = new DeferredHandler(); private LoaderTask mLoaderTask; private boolean mIsLoaderTaskRunning; - private volatile boolean mFlushingWorkerThread; /** * Maintain a set of packages per user, for which we added a shortcut on the workspace. @@ -219,12 +219,18 @@ public class LauncherModel extends BroadcastReceiver public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); public void dumpLogsToLocalData(); + public void bindAddPendingItem(PendingAddItemInfo info, long container, long screenId, + int[] cell, int spanX, int spanY); } public interface ItemInfoFilter { public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn); } + public interface ScreenPosProvider { + int getScreenIndex(ArrayList screenIDs); + } + LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { Context context = app.getContext(); @@ -287,67 +293,6 @@ public class LauncherModel extends BroadcastReceiver return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ; } - static boolean findNextAvailableIconSpaceInScreen(ArrayList items, int[] xy, - long screen) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - final int xCount = (int) grid.numColumns; - final int yCount = (int) grid.numRows; - boolean[][] occupied = new boolean[xCount][yCount]; - - int cellX, cellY, spanX, spanY; - for (int i = 0; i < items.size(); ++i) { - final ItemInfo item = items.get(i); - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - if (item.screenId == screen) { - cellX = item.cellX; - cellY = item.cellY; - spanX = item.spanX; - spanY = item.spanY; - for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) { - for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) { - occupied[x][y] = true; - } - } - } - } - } - - return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied); - } - static Pair findNextAvailableIconSpace(Context context, String name, - Intent launchIntent, - int firstScreenIndex, - ArrayList workspaceScreens) { - // Lock on the app so that we don't try and get the items while apps are being added - LauncherAppState app = LauncherAppState.getInstance(); - LauncherModel model = app.getModel(); - boolean found = false; - synchronized (app) { - if (sWorkerThread.getThreadId() != Process.myTid()) { - // Flush the LauncherModel worker thread, so that if we just did another - // processInstallShortcut, we give it time for its shortcut to get added to the - // database (getItemsInLocalCoordinates reads the database) - model.flushWorkerThread(); - } - final ArrayList items = LauncherModel.getItemsInLocalCoordinates(context); - - // Try adding to the workspace screens incrementally, starting at the default or center - // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1)) - firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size()); - int count = workspaceScreens.size(); - for (int screen = firstScreenIndex; screen < count && !found; screen++) { - int[] tmpCoordinates = new int[2]; - if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates, - workspaceScreens.get(screen))) { - // Update the Launcher db - return new Pair(workspaceScreens.get(screen), tmpCoordinates); - } - } - } - return null; - } - public void setPackageState(final ArrayList installInfo) { // Process the updated package state Runnable r = new Runnable() { @@ -402,11 +347,196 @@ public class LauncherModel extends BroadcastReceiver public void addAndBindAddedWorkspaceApps(final Context context, final ArrayList workspaceApps) { - final Callbacks callbacks = getCallback(); + addAndBindAddedWorkspaceApps(context, workspaceApps, + new ScreenPosProvider() { + + @Override + public int getScreenIndex(ArrayList screenIDs) { + return screenIDs.isEmpty() ? 0 : 1; + } + }, 1, false); + } - if (workspaceApps == null) { - throw new RuntimeException("workspaceApps and allAppsApps must not be null"); + private static boolean findNextAvailableIconSpaceInScreen(ArrayList occupiedPos, + int[] xy, int spanX, int spanY) { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + final int xCount = (int) grid.numColumns; + final int yCount = (int) grid.numRows; + boolean[][] occupied = new boolean[xCount][yCount]; + if (occupiedPos != null) { + for (Rect r : occupiedPos) { + for (int x = r.left; 0 <= x && x < r.right && x < xCount; x++) { + for (int y = r.top; 0 <= y && y < r.bottom && y < yCount; y++) { + occupied[x][y] = true; + } + } + } + } + return CellLayout.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied); + } + + /** + * Find a position on the screen for the given size or adds a new screen. + * @return screenId and the coordinates for the item. + */ + private static Pair findSpaceForItem( + Context context, + ScreenPosProvider preferredScreen, + int fallbackStartScreen, + ArrayList workspaceScreens, + ArrayList addedWorkspaceScreensFinal, + int spanX, int spanY) { + // Load position of items which are on the desktop. We can't use sBgItemsIdMap because + // loadWorkspace() may not have been called. + final ContentResolver cr = context.getContentResolver(); + Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, + new String[] { + LauncherSettings.Favorites.SCREEN, + LauncherSettings.Favorites.CELLX, + LauncherSettings.Favorites.CELLY, + LauncherSettings.Favorites.SPANX, + LauncherSettings.Favorites.SPANY, + LauncherSettings.Favorites.CONTAINER + }, + "container=?", + new String[] { Integer.toString(LauncherSettings.Favorites.CONTAINER_DESKTOP) }, + null); + + final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); + final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); + final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); + final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); + final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); + LongSparseArray> screenItems = new LongSparseArray>(); + try { + while (c.moveToNext()) { + Rect rect = new Rect(); + rect.left = c.getInt(cellXIndex); + rect.top = c.getInt(cellYIndex); + rect.right = rect.left + Math.max(1, c.getInt(spanXIndex)); + rect.bottom = rect.top + Math.max(1, c.getInt(spanYIndex)); + + long screenId = c.getInt(screenIndex); + ArrayList items = screenItems.get(screenId); + if (items == null) { + items = new ArrayList(); + screenItems.put(screenId, items); + } + items.add(rect); + } + } catch (Exception e) { + screenItems.clear(); + } finally { + c.close(); + } + + // Find appropriate space for the item. + long screenId = 0; + int[] cordinates = new int[2]; + boolean found = false; + + int screenCount = workspaceScreens.size(); + // First check the preferred screen. + int preferredScreenIndex = preferredScreen.getScreenIndex(workspaceScreens); + if (preferredScreenIndex < screenCount) { + screenId = workspaceScreens.get(preferredScreenIndex); + found = findNextAvailableIconSpaceInScreen( + screenItems.get(screenId), cordinates, spanX, spanY); + } + + if (!found) { + // Search on any of the screens. + for (int screen = fallbackStartScreen; screen < screenCount; screen++) { + screenId = workspaceScreens.get(screen); + if (findNextAvailableIconSpaceInScreen( + screenItems.get(screenId), cordinates, spanX, spanY)) { + // We found a space for it + found = true; + break; + } + } } + + if (!found) { + // Still no position found. Add a new screen to the end. + screenId = LauncherAppState.getLauncherProvider().generateNewScreenId(); + + // Save the screen id for binding in the workspace + workspaceScreens.add(screenId); + addedWorkspaceScreensFinal.add(screenId); + + // If we still can't find an empty space, then God help us all!!! + if (!findNextAvailableIconSpaceInScreen( + screenItems.get(screenId), cordinates, spanX, spanY)) { + throw new RuntimeException("Can't find space to add the item"); + } + } + return Pair.create(screenId, cordinates); + } + + /** + * Adds the provided items to the workspace. + * @param preferredScreen the screen where we should try to add the app first + * @param fallbackStartScreen the screen to start search for empty space if + * preferredScreen is not available. + */ + public void addAndBindPendingItem( + final Context context, + final PendingAddItemInfo addInfo, + final ScreenPosProvider preferredScreen, + final int fallbackStartScreen) { + final Callbacks callbacks = getCallback(); + // Process the newly added applications and add them to the database first + Runnable r = new Runnable() { + public void run() { + final ArrayList addedWorkspaceScreensFinal = new ArrayList(); + + ArrayList workspaceScreens = new ArrayList(); + TreeMap orderedScreens = loadWorkspaceScreensDb(context); + for (Integer i : orderedScreens.keySet()) { + long screenId = orderedScreens.get(i); + workspaceScreens.add(screenId); + } + + // Find appropriate space for the item. + Pair coords = findSpaceForItem(context, preferredScreen, + fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal, + addInfo.spanX, + addInfo.spanY); + final long screenId = coords.first; + final int[] cordinates = coords.second; + + // Update the workspace screens + updateWorkspaceScreenOrder(context, workspaceScreens); + runOnMainThread(new Runnable() { + public void run() { + Callbacks cb = getCallback(); + if (callbacks == cb && cb != null) { + cb.bindAddScreens(addedWorkspaceScreensFinal); + cb.bindAddPendingItem(addInfo, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, cordinates, addInfo.spanX, addInfo.spanY); + } + } + }); + } + }; + runOnWorkerThread(r); + } + + /** + * Adds the provided items to the workspace. + * @param preferredScreen the screen where we should try to add the app first + * @param fallbackStartScreen the screen to start search for empty space if + * preferredScreen is not available. + */ + public void addAndBindAddedWorkspaceApps(final Context context, + final ArrayList workspaceApps, + final ScreenPosProvider preferredScreen, + final int fallbackStartScreen, + final boolean allowDuplicate) { + final Callbacks callbacks = getCallback(); if (workspaceApps.isEmpty()) { return; } @@ -427,53 +557,27 @@ public class LauncherModel extends BroadcastReceiver } synchronized(sBgLock) { - Iterator iter = workspaceApps.iterator(); - while (iter.hasNext()) { - ItemInfo a = iter.next(); - final String name = a.title.toString(); - final Intent launchIntent = a.getIntent(); - - // Short-circuit this logic if the icon exists somewhere on the workspace - if (shortcutExists(context, name, launchIntent, a.user)) { - continue; - } - - // Add this icon to the db, creating a new page if necessary. If there - // is only the empty page then we just add items to the first page. - // Otherwise, we add them to the next pages. - int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1; - Pair coords = LauncherModel.findNextAvailableIconSpace(context, - name, launchIntent, startSearchPageIndex, workspaceScreens); - if (coords == null) { - LauncherProvider lp = LauncherAppState.getLauncherProvider(); - - // If we can't find a valid position, then just add a new screen. - // This takes time so we need to re-queue the add until the new - // page is added. Create as many screens as necessary to satisfy - // the startSearchPageIndex. - int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 - - workspaceScreens.size()); - while (numPagesToAdd > 0) { - long screenId = lp.generateNewScreenId(); - // Save the screen id for binding in the workspace - workspaceScreens.add(screenId); - addedWorkspaceScreensFinal.add(screenId); - numPagesToAdd--; + for (ItemInfo item : workspaceApps) { + if (!allowDuplicate) { + // Short-circuit this logic if the icon exists somewhere on the workspace + if (shortcutExists(context, item.title.toString(), + item.getIntent(), item.user)) { + continue; } - - // Find the coordinate again - coords = LauncherModel.findNextAvailableIconSpace(context, - name, launchIntent, startSearchPageIndex, workspaceScreens); - } - if (coords == null) { - throw new RuntimeException("Coordinates should not be null"); } + // Find appropriate space for the item. + Pair coords = findSpaceForItem(context, preferredScreen, + fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal, + 1, 1); + long screenId = coords.first; + int[] cordinates = coords.second; + ShortcutInfo shortcutInfo; - if (a instanceof ShortcutInfo) { - shortcutInfo = (ShortcutInfo) a; - } else if (a instanceof AppInfo) { - shortcutInfo = ((AppInfo) a).makeShortcut(); + if (item instanceof ShortcutInfo) { + shortcutInfo = (ShortcutInfo) item; + } else if (item instanceof AppInfo) { + shortcutInfo = ((AppInfo) item).makeShortcut(); } else { throw new RuntimeException("Unexpected info type"); } @@ -481,7 +585,7 @@ public class LauncherModel extends BroadcastReceiver // Add the shortcut to the db addItemToDatabase(context, shortcutInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, - coords.first, coords.second[0], coords.second[1], false); + screenId, cordinates[0], cordinates[1], false); // Save the ShortcutInfo for binding in the workspace addedShortcutsFinal.add(shortcutInfo); } @@ -717,35 +821,6 @@ public class LauncherModel extends BroadcastReceiver } } - public void flushWorkerThread() { - mFlushingWorkerThread = true; - Runnable waiter = new Runnable() { - public void run() { - synchronized (this) { - notifyAll(); - mFlushingWorkerThread = false; - } - } - }; - - synchronized(waiter) { - runOnWorkerThread(waiter); - if (mLoaderTask != null) { - synchronized(mLoaderTask) { - mLoaderTask.notify(); - } - } - boolean success = false; - while (!success) { - try { - waiter.wait(); - success = true; - } catch (InterruptedException e) { - } - } - } - } - /** * Move an item in the DB to a new */ @@ -889,57 +964,6 @@ public class LauncherModel extends BroadcastReceiver } } - /** - * Returns an ItemInfo array containing all the items in the LauncherModel. - * The ItemInfo.id is not set through this function. - */ - static ArrayList getItemsInLocalCoordinates(Context context) { - ArrayList items = new ArrayList(); - final ContentResolver cr = context.getContentResolver(); - Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] { - LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER, - LauncherSettings.Favorites.SCREEN, - LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY, - LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY, - LauncherSettings.Favorites.PROFILE_ID }, null, null, null); - - final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); - final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); - final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); - final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - final int rankIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.RANK); - final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); - final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID); - UserManagerCompat userManager = UserManagerCompat.getInstance(context); - try { - while (c.moveToNext()) { - ItemInfo item = new ItemInfo(); - item.cellX = c.getInt(cellXIndex); - item.cellY = c.getInt(cellYIndex); - item.rank = c.getInt(rankIndex); - item.spanX = Math.max(1, c.getInt(spanXIndex)); - item.spanY = Math.max(1, c.getInt(spanYIndex)); - item.container = c.getInt(containerIndex); - item.itemType = c.getInt(itemTypeIndex); - item.screenId = c.getInt(screenIndex); - long serialNumber = c.getInt(profileIdIndex); - item.user = userManager.getUserForSerialNumber(serialNumber); - // Skip if user has been deleted. - if (item.user != null) { - items.add(item); - } - } - } catch (Exception e) { - items.clear(); - } finally { - c.close(); - } - - return items; - } - /** * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList. */ @@ -1543,7 +1567,7 @@ public class LauncherModel extends BroadcastReceiver } }); - while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) { + while (!mStopped && !mLoadAndBindStepFinished) { try { // Just in case mFlushingWorkerThread changes but we aren't woken up, // wait no longer than 1sec at a time diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java index 41270e3be..107069b78 100644 --- a/src/com/android/launcher3/PagedViewWidget.java +++ b/src/com/android/launcher3/PagedViewWidget.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -67,6 +66,7 @@ public class PagedViewWidget extends LinearLayout { setWillNotDraw(false); setClipToPadding(false); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @Override diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java index 2972c4f9b..bcb59c448 100644 --- a/src/com/android/launcher3/PreloadIconDrawable.java +++ b/src/com/android/launcher3/PreloadIconDrawable.java @@ -54,12 +54,11 @@ class PreloadIconDrawable extends Drawable { mPaint.setStrokeCap(Paint.Cap.ROUND); setBounds(icon.getBounds()); - applyTheme(theme); + applyPreloaderTheme(theme); onLevelChange(0); } - @Override - public void applyTheme(Theme t) { + public void applyPreloaderTheme(Theme t) { TypedArray ta = t.obtainStyledAttributes(R.styleable.PreloadIconDrawable); mBgDrawable = ta.getDrawable(R.styleable.PreloadIconDrawable_background); mBgDrawable.setFilterBitmap(true); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b6e85f304..44d77571b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -31,10 +31,7 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -77,8 +74,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** @@ -92,9 +87,6 @@ public class Workspace extends SmoothPagedView Insettable { private static final String TAG = "Launcher.Workspace"; - // Y rotation to apply to the workspace screens - private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f; - private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0; private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; @@ -228,7 +220,6 @@ public class Workspace extends SmoothPagedView private Runnable mDelayedResizeRunnable; private Runnable mDelayedSnapToPageRunnable; private Point mDisplaySize = new Point(); - private int mCameraDistance; // Variables relating to the creation of user folders by hovering shortcuts over shortcuts private static final int FOLDER_CREATION_TIMEOUT = 0; @@ -346,7 +337,6 @@ public class Workspace extends SmoothPagedView mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; mOverviewModeShrinkFactor = grid.getOverviewModeScale(); - mCameraDistance = res.getInteger(R.integer.config_cameraDistance); mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); a.recycle(); @@ -450,7 +440,6 @@ public class Workspace extends SmoothPagedView */ protected void initWorkspace() { mCurrentPage = mDefaultPage; - Launcher.setScreen(mCurrentPage); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); mIconCache = app.getIconCache(); @@ -1323,7 +1312,6 @@ public class Workspace extends SmoothPagedView @Override protected void notifyPageSwitchListener() { super.notifyPageSwitchListener(); - Launcher.setScreen(getNextPage()); if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) { mCustomContentShowing = true; @@ -3952,23 +3940,8 @@ public class Workspace extends SmoothPagedView // When dragging and dropping from customization tray, we deal with creating // widgets/shortcuts/folders in a slightly different way - switch (pendingInfo.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - int span[] = new int[2]; - span[0] = item.spanX; - span[1] = item.spanY; - mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo, - container, screenId, mTargetCell, span, null); - break; - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - mLauncher.processShortcutFromDrop(pendingInfo.componentName, - container, screenId, mTargetCell, null); - break; - default: - throw new IllegalStateException("Unknown item type: " + - pendingInfo.itemType); - } + mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell, + item.spanX, item.spanY); } }; boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET @@ -3998,7 +3971,7 @@ public class Workspace extends SmoothPagedView case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: if (info.container == NO_ID && info instanceof AppInfo) { // Came from all apps -- make a copy - info = new ShortcutInfo((AppInfo) info); + info = ((AppInfo) info).makeShortcut(); } view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); @@ -4250,15 +4223,7 @@ public class Workspace extends SmoothPagedView if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) { if (target != this && mDragInfo != null) { - CellLayout parentCell = getParentCellLayoutForView(mDragInfo.cell); - if (parentCell != null) { - parentCell.removeView(mDragInfo.cell); - } else if (LauncherAppState.isDogfoodBuild()) { - throw new NullPointerException("mDragInfo.cell has null parent"); - } - if (mDragInfo.cell instanceof DropTarget) { - mDragController.removeDropTarget((DropTarget) mDragInfo.cell); - } + removeWorkspaceItem(mDragInfo.cell); } } else if (mDragInfo != null) { CellLayout cellLayout; @@ -4283,6 +4248,18 @@ public class Workspace extends SmoothPagedView mDragInfo = null; } + public void removeWorkspaceItem(View v) { + CellLayout parentCell = getParentCellLayoutForView(v); + if (parentCell != null) { + parentCell.removeView(v); + } else if (LauncherAppState.isDogfoodBuild()) { + throw new NullPointerException("mDragInfo.cell has null parent"); + } + if (v instanceof DropTarget) { + mDragController.removeDropTarget((DropTarget) v); + } + } + public void deferCompleteDropAfterUninstallActivity() { mDeferDropAfterUninstall = true; } @@ -4477,12 +4454,6 @@ public class Workspace extends SmoothPagedView return true; } - @Override - protected void onRestoreInstanceState(Parcelable state) { - super.onRestoreInstanceState(state); - Launcher.setScreen(mCurrentPage); - } - @Override protected void dispatchRestoreInstanceState(SparseArray container) { // We don't dispatch restoreInstanceState to our children using this code path. -- cgit v1.2.3 From 60197ff979e45e65e6a44cec595dbb22a978ada4 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 19 Jan 2015 10:34:06 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I5435a134c4c9ebd14acf9cca343e71b06fd72758 Auto-generated-cl: translation import --- res/values-af/strings.xml | 3 ++- res/values-am/strings.xml | 3 ++- res/values-ar/strings.xml | 3 ++- res/values-bg/strings.xml | 3 ++- res/values-bn-rBD/strings.xml | 3 ++- res/values-ca/strings.xml | 3 ++- res/values-cs/strings.xml | 3 ++- res/values-da/strings.xml | 3 ++- res/values-de/strings.xml | 3 ++- res/values-el/strings.xml | 3 ++- res/values-en-rGB/strings.xml | 3 ++- res/values-en-rIN/strings.xml | 3 ++- res/values-es-rUS/strings.xml | 3 ++- res/values-es/strings.xml | 3 ++- res/values-et-rEE/strings.xml | 3 ++- res/values-eu-rES/strings.xml | 3 ++- res/values-fa/strings.xml | 3 ++- res/values-fi/strings.xml | 3 ++- res/values-fr-rCA/strings.xml | 3 ++- res/values-fr/strings.xml | 3 ++- res/values-gl-rES/strings.xml | 3 ++- res/values-hi/strings.xml | 3 ++- res/values-hr/strings.xml | 3 ++- res/values-hu/strings.xml | 3 ++- res/values-hy-rAM/strings.xml | 3 ++- res/values-in/strings.xml | 3 ++- res/values-is-rIS/strings.xml | 3 ++- res/values-it/strings.xml | 3 ++- res/values-iw/strings.xml | 3 ++- res/values-ja/strings.xml | 3 ++- res/values-ka-rGE/strings.xml | 3 ++- res/values-kk-rKZ/strings.xml | 3 ++- res/values-km-rKH/strings.xml | 5 +++-- res/values-kn-rIN/strings.xml | 3 ++- res/values-ko/strings.xml | 3 ++- res/values-ky-rKG/strings.xml | 3 ++- res/values-lo-rLA/strings.xml | 7 ++++--- res/values-lt/strings.xml | 3 ++- res/values-lv/strings.xml | 3 ++- res/values-mk-rMK/strings.xml | 3 ++- res/values-ml-rIN/strings.xml | 3 ++- res/values-mn-rMN/strings.xml | 3 ++- res/values-mr-rIN/strings.xml | 3 ++- res/values-ms-rMY/strings.xml | 3 ++- res/values-my-rMM/strings.xml | 11 ++++++----- res/values-nb/strings.xml | 3 ++- res/values-ne-rNP/strings.xml | 3 ++- res/values-nl/strings.xml | 3 ++- res/values-pl/strings.xml | 3 ++- res/values-pt-rPT/strings.xml | 3 ++- res/values-pt/strings.xml | 3 ++- res/values-ro/strings.xml | 3 ++- res/values-ru/strings.xml | 3 ++- res/values-si-rLK/strings.xml | 3 ++- res/values-sk/strings.xml | 3 ++- res/values-sl/strings.xml | 3 ++- res/values-sr/strings.xml | 3 ++- res/values-sv/strings.xml | 3 ++- res/values-sw/strings.xml | 3 ++- res/values-ta-rIN/strings.xml | 3 ++- res/values-te-rIN/strings.xml | 3 ++- res/values-th/strings.xml | 3 ++- res/values-tl/strings.xml | 3 ++- res/values-tr/strings.xml | 3 ++- res/values-uk/strings.xml | 3 ++- res/values-ur-rPK/strings.xml | 3 ++- res/values-uz-rUZ/strings.xml | 3 ++- res/values-vi/strings.xml | 3 ++- res/values-zh-rCN/strings.xml | 3 ++- res/values-zh-rHK/strings.xml | 3 ++- res/values-zh-rTW/strings.xml | 3 ++- res/values-zu/strings.xml | 3 ++- 72 files changed, 151 insertions(+), 79 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index f37f08f3b..f5fb42ff5 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -53,7 +53,6 @@ "Kies program" "Programme" "Tuis" - "Verwyder" "Deïnstalleer" "Verwyder" "Deïnstalleer" @@ -121,4 +120,6 @@ "Soek" "Hierdie program is nie geïnstalleer nie" "Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer." + + diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 1a25f66cd..cc8193aae 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -53,7 +53,6 @@ "መተግበሪያ ይምረጡ" "መተግበሪያዎች" "መነሻ" - "አስወግድ" "አራግፍ" "አስወግድ" "አራግፍ" @@ -121,4 +120,6 @@ "ፈልግ" "ይህ መተግበሪያ አልተጫነም" "የዚህ አዶ መተግበሪያ አልተጫነም። ማስወገድ ወይም መተግበሪያውን መፈለግና ራስዎ መጫን ይችላሉ።" + + diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 592be5841..a72f53593 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -53,7 +53,6 @@ "اختيار تطبيق" "التطبيقات" "الرئيسية" - "إزالة" "إزالة" "إزالة" "إزالة" @@ -121,4 +120,6 @@ "بحث" "لم يتم تثبيت هذا التطبيق" "لم يتم تثبيت تطبيق لهذا الرمز. يمكنك إزالته أو البحث عن التطبيق وتثبيته يدويًا." + + diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 3f710db30..bd911a07a 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -53,7 +53,6 @@ "Избор на приложение" "Приложения" "Начало" - "Премахване" "Деинсталиране" "Премахване" "Деинсталиране" @@ -121,4 +120,6 @@ "Търсене" "Това приложение не е инсталирано" "Приложението за тази икона не е инсталирано. Можете да я премахнете или да потърсите приложението и да го инсталирате ръчно." + + diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index 143420103..bead26cbc 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -53,7 +53,6 @@ "অ্যাপ্লিকেশান চয়ন করুন" "অ্যাপ্লিকেশানগুলি" "হোম" - "সরান" "আনইনস্টল করুন" "সরান" "আনইনস্টল করুন" @@ -121,4 +120,6 @@ "অনুসন্ধান" "এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই" "এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।" + + diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 89e582a81..c6a28046a 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -53,7 +53,6 @@ "Tria d\'una aplicació" "Aplicacions" "Inici" - "Suprimeix" "Desinstal·la" "Suprimeix" "Desinstal·la" @@ -121,4 +120,6 @@ "Cerca" "Aquesta aplicació no està instal·lada" "L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment." + + diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 0288806f4..723c5d801 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -53,7 +53,6 @@ "Výběr aplikace" "Aplikace" "Plocha" - "Odstranit" "Odinstalovat" "Odstranit" "Odinstalovat" @@ -121,4 +120,6 @@ "Hledat" "Tato aplikace není nainstalována" "Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně." + + diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 5fa6aa6b6..4914030ea 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -53,7 +53,6 @@ "Vælg app" "Apps" "Startskærm" - "Fjern" "Afinstaller" "Fjern" "Afinstaller" @@ -121,4 +120,6 @@ "Søg" "Denne app er ikke installeret" "Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt." + + diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 059983ecf..078d83b2f 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -53,7 +53,6 @@ "App auswählen" "Apps" "Startseite" - "Entfernen" "Deinstallieren" "Entfernen" "Deinstallieren" @@ -121,4 +120,6 @@ "Suchen" "Diese App ist nicht installiert" "Die App für dieses Symbol ist nicht installiert. Sie können das Symbol entfernen oder nach der App suchen und sie manuell installieren." + + diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 12f0dfef1..58e1f8ad9 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -53,7 +53,6 @@ "Επιλογή εφαρμογής" "Εφαρμογές" "Αρχική σελίδα" - "Κατάργηση" "Κατάργηση εγκατάστασης" "Κατάργηση" "Κατάργηση εγκατάστασης" @@ -121,4 +120,6 @@ "Αναζήτηση" "Αυτή η εφαρμογή δεν είναι εγκατεστημένη" "Η εφαρμογή γι\' αυτό το εικονίδιο δεν είναι εγκατεστημένη. Μπορείτε να το καταργήσετε ή να αναζητήσετε την εφαρμογή και να την εγκαταστήσετε με μη αυτόματο τρόπο." + + diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 0d004fef5..88987887f 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -53,7 +53,6 @@ "Choose app" "Apps" "Home" - "Remove" "Uninstall" "Remove" "Uninstall" @@ -121,4 +120,6 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." + + diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 0d004fef5..88987887f 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -53,7 +53,6 @@ "Choose app" "Apps" "Home" - "Remove" "Uninstall" "Remove" "Uninstall" @@ -121,4 +120,6 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." + + diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 4408080e2..c047a7381 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -53,7 +53,6 @@ "Elegir aplicación" "Aplicaciones" "Pantalla principal" - "Eliminar" "Desinstalar" "Eliminar" "Desinstalar" @@ -121,4 +120,6 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente." + + diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 898d0eaa5..5f3a0771c 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -53,7 +53,6 @@ "Selecciona una aplicación" "Aplicaciones" "Inicio" - "Eliminar" "Desinstalar" "Eliminar" "Desinstalar" @@ -121,4 +120,6 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente." + + diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 76b20aba7..d9e9fd02d 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -53,7 +53,6 @@ "Rakenduse valimine" "Rakendused" "Avaekraan" - "Eemalda" "Desinstalli" "Eemalda" "Desinstalli" @@ -121,4 +120,6 @@ "Otsing" "See rakendus ei ole installitud" "Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida." + + diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index e02479c0e..38006a826 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -53,7 +53,6 @@ "Aukeratu aplikazioa" "Aplikazioak" "Hasiera" - "Kendu" "Desinstalatu" "Kendu" "Desinstalatu" @@ -121,4 +120,6 @@ "Bilatu" "Aplikazio hau ez dago instalatuta" "Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu." + + diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 924cccf75..76fe58c82 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -53,7 +53,6 @@ "انتخاب برنامه" "برنامه‌ها" "صفحه اصلی" - "حذف" "حذف نصب" "حذف" "حذف نصب" @@ -121,4 +120,6 @@ "جستجو" "این برنامه نصب نشده است." "برنامه برای این نماد نصب نشده است. می‌توانید آن را حذف کنید یا سعی کنید برنامه را جستجو کنید و آن را به صورت دستی نصب کنید." + + diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index a5da03f43..74871db2c 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -53,7 +53,6 @@ "Valitse sovellus" "Sovellukset" "Aloitusruutu" - "Poista" "Poista" "Poista" "Poista" @@ -121,4 +120,6 @@ "Haku" "Sovellusta ei ole asennettu" "Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti." + + diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 62a2b894e..49027e71f 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -53,7 +53,6 @@ "Sélectionner une application" "Applications" "Accueil" - "Supprimer" "Désinstaller" "Supprimer" "Désinstaller" @@ -121,4 +120,6 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement." + + diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index d23b6a492..85ff3d2cc 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -53,7 +53,6 @@ "Sélectionner une application" "Applications" "Accueil" - "Supprimer" "Désinstaller" "Supprimer" "Désinstaller" @@ -121,4 +120,6 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement." + + diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index 96a53dc09..af169564d 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -53,7 +53,6 @@ "Escoller unha aplicación" "Aplicacións" "Inicio" - "Eliminar" "Desinstalar" "Eliminar" "Desinstalar" @@ -121,4 +120,6 @@ "Buscar" "Esta aplicación non está instalada" "A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente." + + diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 937248921..cdc38d662 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -53,7 +53,6 @@ "एप्‍लिकेशन चुनें" "ऐप्लिकेशन" "होम" - "निकालें" "अनइंस्टॉल करें" "निकालें" "अनइंस्टॉल करें" @@ -121,4 +120,6 @@ "खोजें" "यह ऐप्स इंस्टॉल नहीं है" "इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." + + diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 702b474ff..96db975db 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -53,7 +53,6 @@ "Odabir aplikacije" "Aplikacije" "Početna" - "Ukloni" "Deinstaliraj" "Ukloni" "Deinstaliraj" @@ -121,4 +120,6 @@ "Traži" "Ta aplikacija nije instalirana" "Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno." + + diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 513548a2a..e44acbfed 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -53,7 +53,6 @@ "Válasszon alkalmazást" "Alkalmazások" "Főoldal" - "Eltávolítás" "Eltávolítás" "Eltávolítás" "Eltávolítás" @@ -121,4 +120,6 @@ "Keresés" "Az alkalmazás nincs telepítve" "Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt." + + diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 940c14bf2..a8536f5c4 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -53,7 +53,6 @@ "Ընտրել ծրագիրը" "Ծրագրեր" "Հիմնական" - "Հեռացնել" "Ապատեղադրել" "Հեռացնել" "Ապատեղադրել" @@ -121,4 +120,6 @@ "Գտնել" "Այս ծրագիրը տեղադրված չէ:" "Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:" + + diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index b0c5d139d..24286b28e 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -53,7 +53,6 @@ "Pilih aplikasi" "Aplikasi" "Layar Utama" - "Hapus" "Copot pemasangan" "Hapus" "Copot pemasangan" @@ -121,4 +120,6 @@ "Telusuri" "Aplikasi ini belum terpasang" "Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual." + + diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index a3fa0bc52..faca7828e 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -53,7 +53,6 @@ "Veldu forrit" "Forrit" "Heim" - "Fjarlægja" "Eyða" "Fjarlægja" "Eyða" @@ -121,4 +120,6 @@ "Leita" "Þetta forrit er ekki uppsett" "Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt." + + diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index d78d0326d..351ce06be 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -53,7 +53,6 @@ "Scegli app" "App" "Home page" - "Rimuovi" "Disinstalla" "Rimuovi" "Disinstalla" @@ -121,4 +120,6 @@ "Cerca" "L\'app non è installata" "L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente." + + diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 61fb53efa..c0cb737a8 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -53,7 +53,6 @@ "בחר אפליקציה" "אפליקציות" "דף הבית" - "הסר" "הסר התקנה" "הסר" "הסר התקנה" @@ -121,4 +120,6 @@ "חפש" "אפליקציה זו אינה מותקנת" "האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית." + + diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index ec872b8a9..d9537b407 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -53,7 +53,6 @@ "アプリを選択" "アプリ" "ホーム" - "削除" "アンインストール" "削除" "アンインストール" @@ -121,4 +120,6 @@ "検索" "このアプリはインストールされていません" "このアイコンのアプリはインストールされていません。このアイコンは削除できます。または、手動でアプリを検索してインストールしください。" + + diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index b96d3d267..d91bff564 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -53,7 +53,6 @@ "აირჩიეთ აპი" "აპები" "მთავარი" - "წაშლა" "დეინსტალაცია" "წაშლა" "დეინსტალაცია" @@ -121,4 +120,6 @@ "ძიება" "ეს აპი დაყენებული არ არის" "ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია." + + diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index c5b5a62f6..555131d79 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -53,7 +53,6 @@ "Қолданба таңдау" "Қолданбалар" "Негізгі" - "Алып тастау" "Алмау" "Алып тастау" "Алмау" @@ -121,4 +120,6 @@ "Іздеу" "Бұл қолданба орнатылмаған" "Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады." + + diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 3a1c82dcc..18d04efe6 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -53,7 +53,6 @@ "ជ្រើស​កម្មវិធី" "កម្មវិធី" "ដើម" - "លុប​ចេញ" "លុប" "លុប​ចេញ" "លុប" @@ -85,7 +84,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍" + "សូម​ស្វាគមន៍​" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" @@ -121,4 +120,6 @@ "ស្វែងរក" "មិន​បាន​ដំឡើង​កម្មវិធី​នេះ" "មិន​បាន​ដំឡើង​កម្មវិធី​សម្រាប់​រូបតំណាង​នេះ។ អ្នក​អាច​លុប​វា ឬ​ស្វែងរក​កម្មវិធី និង​ដំឡើង​វា​ដោយ​ដៃ។" + + diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index 7a070511d..adc348dff 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -53,7 +53,6 @@ "ಅಪ್ಲಿಕೇಶನ್ ಆಯ್ಕೆಮಾಡಿ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" "ಮುಖಪುಟ" - "ತೆಗೆದುಹಾಕು" "ಅಸ್ಥಾಪಿಸು" "ತೆಗೆದುಹಾಕು" "ಅಸ್ಥಾಪಿಸು" @@ -121,4 +120,6 @@ "ಹುಡುಕು" "ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ" "ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು." + + diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index c73b431bd..84130deb6 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -53,7 +53,6 @@ "앱 선택" "앱" "홈" - "삭제" "제거" "삭제" "제거" @@ -121,4 +120,6 @@ "검색" "이 앱이 설치되어 있지 않음" "이 아이콘의 앱이 설치되어 있지 않습니다. 아이콘을 삭제하거나 앱을 검색하여 수동으로 설치하세요." + + diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index a8a605b26..72f7068e4 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -53,7 +53,6 @@ "Колдонмо тандоо" "Колдонмолор" "Үйгө" - "Алып салуу" "Чечип салуу" "Алып салуу" "Чечип салуу" @@ -121,4 +120,6 @@ "Издөө" "Бул колдонмо орнотулган эмес" "Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот." + + diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 65f01b7f0..1382ff694 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ" + "ຍົກ​ເລີກ​" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -53,7 +53,6 @@ "ເລືອກແອັບຯ" "ແອັບຯ" "ໜ້າຫຼັກ" - "ລຶບ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ລຶບ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" @@ -117,8 +116,10 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ" + "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." + + diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 897c2fd9f..e7b5d4bf7 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -53,7 +53,6 @@ "Pasirinkite programą" "Programos" "Pagrindinis" - "Pašalinti" "Pašalinti" "Pašalinti" "Pašalinti" @@ -121,4 +120,6 @@ "Ieškoti" "Ši programa neįdiegta" "Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys." + + diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 13a47606d..47a60c157 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -53,7 +53,6 @@ "Lietotnes izvēle" "Lietotnes" "Sākums" - "Noņemt" "Atinstalēt" "Noņemt" "Atinstalēt" @@ -121,4 +120,6 @@ "Meklēt" "Šī lietotne nav instalēta" "Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli." + + diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index dcc55d7b7..c9e14d32d 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -53,7 +53,6 @@ "Избери апликација" "Апликации" "Почетна страница" - "Отстрани" "Деинсталирај" "Отстрани" "Деинсталирај" @@ -121,4 +120,6 @@ "Барај" "Апликацијата не е инсталирана" "Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно." + + diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index cf5efaae2..4ad562e48 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -53,7 +53,6 @@ "അപ്ലിക്കേഷൻ തിരഞ്ഞെടുക്കുക" "അപ്ലിക്കേഷനുകൾ" "ഹോം" - "നീക്കംചെയ്യുക" "അണ്‍ഇസ്റ്റാളുചെയ്യുക" "നീക്കംചെയ്യുക" "അണ്‍ഇസ്റ്റാളുചെയ്യുക" @@ -121,4 +120,6 @@ "തിരയുക" "ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല" "ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക." + + diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index f258b4c8d..5e9fc4822 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -53,7 +53,6 @@ "Апп сонгох" "Апп" "Нүүр" - "Устгах" "Устгах" "Устгах" "Устгах" @@ -121,4 +120,6 @@ "Хайх" "Энэ апп-г суулгаагүй байна" "Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой." + + diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index 098e83023..f863bcda9 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -53,7 +53,6 @@ "अॅप निवडा" "अॅप्स" "मुख्‍यपृष्‍ठ" - "काढा" "विस्थापित करा" "काढा" "विस्थापित करा" @@ -121,4 +120,6 @@ "शोधा" "हा अॅप स्थापित केलेला नाही" "या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता." + + diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 532b6ad8b..541e0e149 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -53,7 +53,6 @@ "Pilih apl" "Apl" "Laman Utama" - "Alih keluar" "Nyahpasang" "Alih keluar" "Nyahpasang" @@ -121,4 +120,6 @@ "Carian" "Apl ini tidak dipasang" "Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual." + + diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 668803587..3ea53d72d 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -53,14 +53,13 @@ "အပလီကေးရှင်း တစ်ခုခုကို ရွေးချယ်ပါ" "အပ်ပလီကေးရှင်းများ" "ပင်မစာမျက်နှာ" - "ဖယ်ရှာခြင်း" "ဖယ်ရှားခြင်း" "ဖယ်ရှာခြင်း" "ဖယ်ရှားခြင်း" "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +75,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +101,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +110,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" @@ -121,4 +120,6 @@ "ရှာဖွေရန်" "App မတပ်ဆင်ရသေးပါ" "ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually." + + diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index a9bbe08ca..7a6066da2 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -53,7 +53,6 @@ "Velg app" "Apper" "Startside" - "Fjern" "Avinstaller" "Fjern" "Avinstaller" @@ -121,4 +120,6 @@ "Søk" "Denne appen er ikke installert" "Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt." + + diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 815873de9..4486f1ec5 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -53,7 +53,6 @@ "अनुप्रयोग छनौट गर्नुहोस्" "अनुप्रयोगहरू" "गृह" - "हटाउनुहोस्" "हटाउनुहोस्" "हटाउनुहोस्" "हटाउनुहोस्" @@ -123,4 +122,6 @@ "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" "यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।" + + diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 8a32fd1d9..662cae7ad 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -53,7 +53,6 @@ "App selecteren" "Apps" "Startpagina" - "Verwijderen" "Verwijderen" "Verwijderen" "Verwijderen" @@ -121,4 +120,6 @@ "Zoeken" "Deze app is niet geïnstalleerd" "De app voor dit pictogram is niet geïnstalleerd. U kunt het pictogram verwijderen of de app zoeken en handmatig installeren." + + diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 4eabae96e..fd3abd952 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -53,7 +53,6 @@ "Wybierz aplikację" "Aplikacje" "Ekran główny" - "Usuń" "Odinstaluj" "Usuń" "Odinstaluj" @@ -121,4 +120,6 @@ "Szukaj" "Ta aplikacja nie jest zainstalowana" "Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie." + + diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 4397ebf81..83ad2d745 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -53,7 +53,6 @@ "Escolher aplicação" "Aplicações" "Ecrã principal" - "Remover" "Desinstalar" "Remover" "Desinstalar" @@ -121,4 +120,6 @@ "Pesquisar" "Esta aplicação não está instalada" "A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente." + + diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 07c82143e..5a5253cec 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -53,7 +53,6 @@ "Selecione um app" "Apps" "Início" - "Remover" "Desinstalar" "Remover" "Desinstalar" @@ -121,4 +120,6 @@ "Pesquisar" "Este app não está instalado" "O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente." + + diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 54cb46c3f..2e30eb72b 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -53,7 +53,6 @@ "Alegeți aplicația" "Aplicații" "Ecran de pornire" - "Eliminați" "Dezinstalați" "Eliminați" "Dezinstalați" @@ -121,4 +120,6 @@ "Căutați" "Aplicația nu este instalată" "Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual." + + diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 70ef0f591..5993db313 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -53,7 +53,6 @@ "Выбор приложения" "Приложения" "Главный экран" - "Удалить" "Удалить" "Удалить" "Удалить" @@ -121,4 +120,6 @@ "Найти" "Приложение не установлено" "Приложение не установлено. Вы можете удалить значок или найти приложение и установить его вручную." + + diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index ab3abb83b..b51a9c155 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -53,7 +53,6 @@ "යෙදුම තේරීම" "යෙදුම්" "මුල් පිටුව" - "ඉවත් කරන්න" "අස්ථාපනය කරන්න" "ඉවත් කරන්න" "අස්ථාපනය කරන්න" @@ -121,4 +120,6 @@ "සොයන්න" "මෙම යෙදුම ස්ථාපනය කර නොමැත" "මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න." + + diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 3f41f2d7e..626b0ee43 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -53,7 +53,6 @@ "Vybrať aplikáciu" "Aplikácie" "Domovská stránka" - "Odstrániť" "Odinštalovať" "Odstrániť" "Odinštalovať" @@ -121,4 +120,6 @@ "Vyhľadať" "Táto aplikácia nie je nainštalovaná" "Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte." + + diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index d5a34d58f..e4aba274b 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -53,7 +53,6 @@ "Izberite aplikacijo" "Aplikacije" "Začetni zaslon" - "Odstrani" "Odstrani" "Odstrani" "Odstrani" @@ -121,4 +120,6 @@ "Iskanje" "Ta aplikacija ni nameščena." "Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno." + + diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 9f40890c4..033d129bc 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -53,7 +53,6 @@ "Избор апликације" "Апликације" "Почетна" - "Уклони" "Деинсталирај" "Уклони" "Деинсталирај" @@ -121,4 +120,6 @@ "Претражи" "Ова апликација није инсталирана" "Апликација за ову икону није инсталирана. Можете да је уклоните или да потражите апликацију и инсталирате је ручно." + + diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 25f4d2623..eb298b12b 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -53,7 +53,6 @@ "Välj app" "Appar" "Startskärm" - "Ta bort" "Avinstallera" "Ta bort" "Avinstallera" @@ -121,4 +120,6 @@ "Sök" "Appen är inte installerad" "Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt." + + diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index fa810618b..e811727ae 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -53,7 +53,6 @@ "Chagua programu" "Programu" "Mwanzo" - "Ondoa" "Ondoa" "Ondoa" "Ondoa" @@ -123,4 +122,6 @@ "Tafuta" "Programu hii haijasakinishwa" "Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." + + diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index 9dba6f669..60af81fff 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -53,7 +53,6 @@ "பயன்பாட்டைத் தேர்வுசெய்யவும்" "பயன்பாடுகள்" "முகப்பு" - "அகற்று" "நிறுவல் நீக்கு" "அகற்று" "நிறுவல் நீக்கு" @@ -121,4 +120,6 @@ "தேடு" "பயன்பாடு நிறுவப்படவில்லை" "ஐகானுக்கான பயன்பாடு நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்." + + diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index bfbc7920f..9b8484c3e 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -53,7 +53,6 @@ "అనువర్తనాన్ని ఎంచుకోండి" "అనువర్తనాలు" "హోమ్" - "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" @@ -121,4 +120,6 @@ "శోధించు" "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" "ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." + + diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 0fc20d26f..4d2d02c8f 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -53,7 +53,6 @@ "เลือกแอป" "แอป" "หน้าแรก" - "ลบ" "ถอนการติดตั้ง" "ลบ" "ถอนการติดตั้ง" @@ -121,4 +120,6 @@ "ค้นหา" "ไม่ได้ติดตั้งแอปนี้" "ยังไม่ได้ติดตั้งแอปสำหรับไอคอนนี้ คุณสามารถนำไอคอนออก หรือค้นหาแอปดังกล่าวแล้วติดตั้งด้วยตนเอง" + + diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 4d2f1d478..f74c0058a 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -53,7 +53,6 @@ "Pumili ng app" "Apps" "Home" - "Alisin" "I-uninstall" "Alisin" "I-uninstall" @@ -121,4 +120,6 @@ "Maghanap" "Hindi naka-install ang app na ito" "Hindi naka-install ang app para sa icon na ito. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano." + + diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 6c78f6f63..046304c78 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -53,7 +53,6 @@ "Uygulama seçin" "Uygulamalar" "Ana ekran" - "Kaldır" "Yüklemeyi kaldır" "Kaldır" "Yüklemeyi kaldır" @@ -121,4 +120,6 @@ "Ara" "Bu uygulama yüklü değil" "Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz." + + diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 71e1816a3..01d8390e4 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -53,7 +53,6 @@ "Вибрати програму" "Додатки" "Головний екран" - "Вилучити" "Видалити" "Вилучити" "Видалити" @@ -121,4 +120,6 @@ "Шукати" "Цей додаток не встановлено" "Додаток для цього значка не встановлено. Можна видалити значок або знайти додаток і встановити його вручну." + + diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index ba1d3fa9a..5ccb2f2c7 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -53,7 +53,6 @@ "ایپ منتخب کریں" "ایپس" "ہوم" - "ہٹائیں" "اَن انسٹال کریں" "ہٹائیں" "اَن انسٹال کریں" @@ -121,4 +120,6 @@ "تلاش کریں" "یہ ایپ انسٹال کردہ نہیں ہے" "اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔" + + diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index a3c5670a3..c10f757b9 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -53,7 +53,6 @@ "Ilovani tanlash" "Ilovalar" "Uy" - "O‘chirish" "O‘chirish" "O‘chirish" "O‘chirish" @@ -121,4 +120,6 @@ "Qidirish" "Ushbu ilova o‘rnatilmagan" "Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin." + + diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 5bc16275e..c887624f3 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -53,7 +53,6 @@ "Chọn ứng dụng" "Ứng dụng" "Màn hình chính" - "Xóa" "Gỡ cài đặt" "Xóa" "Gỡ cài đặt" @@ -121,4 +120,6 @@ "Tìm kiếm" "Ứng dụng này chưa được cài đặt" "Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công." + + diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index b7c42fe5c..12229c56f 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -53,7 +53,6 @@ "选择应用" "应用" "主屏幕" - "删除" "卸载" "删除" "卸载" @@ -121,4 +120,6 @@ "搜索" "未安装此应用" "未安装此图标对应的应用。您可以移除此图标,也可以尝试搜索相应的应用并手动安装。" + + diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 2eb700890..322d703a0 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -53,7 +53,6 @@ "選擇應用程式" "應用程式" "主畫面" - "移除" "解除安裝" "移除" "解除安裝" @@ -121,4 +120,6 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" + + diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 52e758396..5abb913a7 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -53,7 +53,6 @@ "選擇應用程式" "應用程式" "主螢幕" - "移除" "解除安裝" "移除" "解除安裝" @@ -121,4 +120,6 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" + + diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index aea09e845..68481cb3a 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -53,7 +53,6 @@ "Khetha uhlelo lokusebenza" "Izinhlelo zokusebenza" "Ikhaya" - "Susa" "Khipha" "Susa" "Khipha" @@ -121,4 +120,6 @@ "Sesha" "Lolu hlelo lokusebenza alifakiwe" "Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela." + + -- cgit v1.2.3 From c1b7c2ecbdcf6b16e36fd8ad843d017f0eaac594 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 16 Jan 2015 16:19:04 -0800 Subject: Simplifying loadScreenDB to load the screen IDs in sorted order Change-Id: I5551b5c12cdc2b0e304a06d2e8edbe81e124a55c --- src/com/android/launcher3/LauncherModel.java | 57 ++++++++-------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7b5f8466a..2e879bcec 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -75,7 +75,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; -import java.util.TreeMap; /** * Maintains in-memory state of the Launcher. It is expected that there should be only one @@ -491,13 +490,7 @@ public class LauncherModel extends BroadcastReceiver Runnable r = new Runnable() { public void run() { final ArrayList addedWorkspaceScreensFinal = new ArrayList(); - - ArrayList workspaceScreens = new ArrayList(); - TreeMap orderedScreens = loadWorkspaceScreensDb(context); - for (Integer i : orderedScreens.keySet()) { - long screenId = orderedScreens.get(i); - workspaceScreens.add(screenId); - } + ArrayList workspaceScreens = loadWorkspaceScreensDb(context); // Find appropriate space for the item. Pair coords = findSpaceForItem(context, preferredScreen, @@ -549,13 +542,7 @@ public class LauncherModel extends BroadcastReceiver // Get the list of workspace screens. We need to append to this list and // can not use sBgWorkspaceScreens because loadWorkspace() may not have been // called. - ArrayList workspaceScreens = new ArrayList(); - TreeMap orderedScreens = loadWorkspaceScreensDb(context); - for (Integer i : orderedScreens.keySet()) { - long screenId = orderedScreens.get(i); - workspaceScreens.add(screenId); - } - + ArrayList workspaceScreens = loadWorkspaceScreensDb(context); synchronized(sBgLock) { for (ItemInfo item : workspaceApps) { if (!allowDuplicate) { @@ -1443,40 +1430,31 @@ public class LauncherModel extends BroadcastReceiver } } - /** Loads the workspace screens db into a map of Rank -> ScreenId */ - private static TreeMap loadWorkspaceScreensDb(Context context) { + /** + * Loads the workspace screen ids in an ordered list. + */ + private static ArrayList loadWorkspaceScreensDb(Context context) { final ContentResolver contentResolver = context.getContentResolver(); final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; - final Cursor sc = contentResolver.query(screensUri, null, null, null, null); - TreeMap orderedScreens = new TreeMap(); + // Get screens ordered by rank. + final Cursor sc = contentResolver.query(screensUri, null, null, null, + LauncherSettings.WorkspaceScreens.SCREEN_RANK); + ArrayList screenIds = new ArrayList(); try { - final int idIndex = sc.getColumnIndexOrThrow( - LauncherSettings.WorkspaceScreens._ID); - final int rankIndex = sc.getColumnIndexOrThrow( - LauncherSettings.WorkspaceScreens.SCREEN_RANK); + final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID); while (sc.moveToNext()) { try { - long screenId = sc.getLong(idIndex); - int rank = sc.getInt(rankIndex); - orderedScreens.put(rank, screenId); + screenIds.add(sc.getLong(idIndex)); } catch (Exception e) { - Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e, true); + Launcher.addDumpLog(TAG, "Desktop items loading interrupted" + + " - invalid screens: " + e, true); } } } finally { sc.close(); } - - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true); - ArrayList orderedScreensPairs= new ArrayList(); - for (Integer i : orderedScreens.keySet()) { - orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }"); - } - Launcher.addDumpLog(TAG, "11683562 - screens: " + - TextUtils.join(", ", orderedScreensPairs), true); - return orderedScreens; + return screenIds; } public boolean isAllAppsLoaded() { @@ -2422,10 +2400,7 @@ public class LauncherModel extends BroadcastReceiver } LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId); } else { - TreeMap orderedScreens = loadWorkspaceScreensDb(mContext); - for (Integer i : orderedScreens.keySet()) { - sBgWorkspaceScreens.add(orderedScreens.get(i)); - } + sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext)); // Log to disk Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + TextUtils.join(", ", sBgWorkspaceScreens), true); -- cgit v1.2.3 From c46bfef5dfd8c6867641bdc32c27b03e2f183726 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 5 Jan 2015 12:40:08 -0800 Subject: Removing support library dependency > Removed FolderAutoScrollHelper as folders are currently not scrollable Change-Id: I261f43a665b742697e3224e1e9852ccc526badcd --- Android.mk | 2 - WallpaperPicker/AndroidManifest.xml | 4 +- .../android/photos/views/TiledImageRenderer.java | 2 +- src/com/android/launcher3/Folder.java | 41 +++++---------- .../android/launcher3/FolderAutoScrollHelper.java | 59 ---------------------- src/com/android/launcher3/Workspace.java | 7 ++- 6 files changed, 20 insertions(+), 95 deletions(-) delete mode 100644 src/com/android/launcher3/FolderAutoScrollHelper.java diff --git a/Android.mk b/Android.mk index 110117be4..632dd0961 100644 --- a/Android.mk +++ b/Android.mk @@ -23,8 +23,6 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13 - LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, WallpaperPicker/src) \ $(call all-renderscript-files-under, src) \ diff --git a/WallpaperPicker/AndroidManifest.xml b/WallpaperPicker/AndroidManifest.xml index 5b6a0077d..cb1457bdc 100644 --- a/WallpaperPicker/AndroidManifest.xml +++ b/WallpaperPicker/AndroidManifest.xml @@ -4,7 +4,7 @@ android:versionCode="1" android:versionName="1.0" > - - + + diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java index c4e493b34..b0292e660 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java @@ -20,9 +20,9 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.RectF; -import android.support.v4.util.LongSparseArray; import android.util.DisplayMetrics; import android.util.Log; +import android.util.LongSparseArray; import android.util.Pools.Pool; import android.util.Pools.SynchronizedPool; import android.view.View; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 66b656882..69254776c 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -21,12 +21,13 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; +import android.os.Build; import android.os.SystemClock; -import android.support.v4.widget.AutoScrollHelper; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -123,8 +124,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private boolean mDestroyed; - private AutoScrollHelper mAutoScrollHelper; - private Runnable mDeferredAction; private boolean mDeferDropAfterUninstall; private boolean mUninstallSuccessful; @@ -208,7 +207,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setSelectAllOnFocus(true); mFolderName.setInputType(mFolderName.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); - mAutoScrollHelper = new FolderAutoScrollHelper(mScrollView); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -700,6 +698,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } @@ -715,29 +714,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList final MotionEvent translatedEv = MotionEvent.obtain( downTime, downTime, MotionEvent.ACTION_MOVE, d.x, d.y, 0); - if (!mAutoScrollHelper.isEnabled()) { - mAutoScrollHelper.setEnabled(true); - } - - final boolean handled = mAutoScrollHelper.onTouch(this, translatedEv); translatedEv.recycle(); - - if (handled) { + mTargetCell = mContent.findNearestArea( + (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); + if (isLayoutRtl()) { + mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; + } + if (mTargetCell[0] != mPreviousTargetCell[0] + || mTargetCell[1] != mPreviousTargetCell[1]) { mReorderAlarm.cancelAlarm(); - } else { - mTargetCell = mContent.findNearestArea( - (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); - if (isLayoutRtl()) { - mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; - } - if (mTargetCell[0] != mPreviousTargetCell[0] - || mTargetCell[1] != mPreviousTargetCell[1]) { - mReorderAlarm.cancelAlarm(); - mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); - mReorderAlarm.setAlarm(REORDER_DELAY); - mPreviousTargetCell[0] = mTargetCell[0]; - mPreviousTargetCell[1] = mTargetCell[1]; - } + mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); + mReorderAlarm.setAlarm(REORDER_DELAY); + mPreviousTargetCell[0] = mTargetCell[0]; + mPreviousTargetCell[1] = mTargetCell[1]; } } @@ -783,8 +772,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void onDragExit(DragObject d) { - // Exiting folder; stop the auto scroller. - mAutoScrollHelper.setEnabled(false); // We only close the folder if this is a true drag exit, ie. not because // a drop has occurred above the folder. if (!d.dragComplete) { diff --git a/src/com/android/launcher3/FolderAutoScrollHelper.java b/src/com/android/launcher3/FolderAutoScrollHelper.java deleted file mode 100644 index 40e888464..000000000 --- a/src/com/android/launcher3/FolderAutoScrollHelper.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2013 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.support.v4.widget.AutoScrollHelper; -import android.widget.ScrollView; - -/** - * An implementation of {@link AutoScrollHelper} that knows how to scroll - * through a {@link Folder}. - */ -public class FolderAutoScrollHelper extends AutoScrollHelper { - private static final float MAX_SCROLL_VELOCITY = 1500f; - - private final ScrollView mTarget; - - public FolderAutoScrollHelper(ScrollView target) { - super(target); - - mTarget = target; - - setActivationDelay(0); - setEdgeType(EDGE_TYPE_INSIDE_EXTEND); - setExclusive(true); - setMaximumVelocity(MAX_SCROLL_VELOCITY, MAX_SCROLL_VELOCITY); - setRampDownDuration(0); - setRampUpDuration(0); - } - - @Override - public void scrollTargetBy(int deltaX, int deltaY) { - mTarget.scrollBy(deltaX, deltaY); - } - - @Override - public boolean canTargetScrollHorizontally(int direction) { - // List do not scroll horizontally. - return false; - } - - @Override - public boolean canTargetScrollVertically(int direction) { - return mTarget.canScrollVertically(direction); - } -} \ No newline at end of file diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 44d77571b..66e370b08 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -48,7 +48,6 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; -import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -492,7 +491,7 @@ public class Workspace extends SmoothPagedView CellLayout cl = ((CellLayout) child); cl.setOnInterceptTouchListener(this); cl.setClickable(true); - cl.setImportantForAccessibility(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO); + cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); super.onChildViewAdded(parent, child); } @@ -2210,8 +2209,8 @@ public class Workspace extends SmoothPagedView private void updateAccessibilityFlags() { int accessible = mState == State.NORMAL ? - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES : - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; + IMPORTANT_FOR_ACCESSIBILITY_YES : + IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; setImportantForAccessibility(accessible); } -- cgit v1.2.3 From d0091012322d5d3a37b6605cdf61ab85431e56a8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 20 Jan 2015 15:55:34 -0800 Subject: Removing usage of reflection. > Updated launcher.java with the new APIs > Removed unused method in BitmapUtils Change-Id: I24804d2cd7d9fc45040532e8324672f5ea6fc110 --- .../com/android/gallery3d/common/BitmapUtils.java | 52 ---------------------- src/com/android/launcher3/Launcher.java | 46 +++++-------------- 2 files changed, 11 insertions(+), 87 deletions(-) diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java index a671ed2b9..6a816d990 100644 --- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java +++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java @@ -18,17 +18,13 @@ package com.android.gallery3d.common; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; -import android.os.Build; import android.util.FloatMath; import android.util.Log; import java.io.ByteArrayOutputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; public class BitmapUtils { private static final String TAG = "BitmapUtils"; @@ -187,54 +183,6 @@ public class BitmapUtils { return bitmap; } - public static Bitmap createVideoThumbnail(String filePath) { - // MediaMetadataRetriever is available on API Level 8 - // but is hidden until API Level 10 - Class clazz = null; - Object instance = null; - try { - clazz = Class.forName("android.media.MediaMetadataRetriever"); - instance = clazz.newInstance(); - - Method method = clazz.getMethod("setDataSource", String.class); - method.invoke(instance, filePath); - - // The method name changes between API Level 9 and 10. - if (Build.VERSION.SDK_INT <= 9) { - return (Bitmap) clazz.getMethod("captureFrame").invoke(instance); - } else { - byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance); - if (data != null) { - Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap != null) return bitmap; - } - return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance); - } - } catch (IllegalArgumentException ex) { - // Assume this is a corrupt video file - } catch (RuntimeException ex) { - // Assume this is a corrupt video file. - } catch (InstantiationException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (InvocationTargetException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (ClassNotFoundException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (NoSuchMethodException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (IllegalAccessException e) { - Log.e(TAG, "createVideoThumbnail", e); - } finally { - try { - if (instance != null) { - clazz.getMethod("release").invoke(instance); - } - } catch (Exception ignored) { - } - } - return null; - } - public static byte[] compressToBytes(Bitmap bitmap) { return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 84476b7cc..61915b755 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -115,9 +115,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; @@ -1686,40 +1683,19 @@ public class Launcher extends Activity * Sets up transparent navigation and status bars in LMP. * This method is a no-op for other platform versions. */ - @TargetApi(19) + @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setupTransparentSystemBarsForLmp() { - // TODO(sansid): use the APIs directly when compiling against L sdk. - // Currently we use reflection to access the flags and the API to set the transparency - // on the System bars. if (Utilities.isLmpOrAbove()) { - try { - getWindow().getAttributes().systemUiVisibility |= - (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - Field drawsSysBackgroundsField = WindowManager.LayoutParams.class.getField( - "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS"); - getWindow().addFlags(drawsSysBackgroundsField.getInt(null)); - - Method setStatusBarColorMethod = - Window.class.getDeclaredMethod("setStatusBarColor", int.class); - Method setNavigationBarColorMethod = - Window.class.getDeclaredMethod("setNavigationBarColor", int.class); - setStatusBarColorMethod.invoke(getWindow(), Color.TRANSPARENT); - setNavigationBarColorMethod.invoke(getWindow(), Color.TRANSPARENT); - } catch (NoSuchFieldException e) { - Log.w(TAG, "NoSuchFieldException while setting up transparent bars"); - } catch (NoSuchMethodException ex) { - Log.w(TAG, "NoSuchMethodException while setting up transparent bars"); - } catch (IllegalAccessException e) { - Log.w(TAG, "IllegalAccessException while setting up transparent bars"); - } catch (IllegalArgumentException e) { - Log.w(TAG, "IllegalArgumentException while setting up transparent bars"); - } catch (InvocationTargetException e) { - Log.w(TAG, "InvocationTargetException while setting up transparent bars"); - } finally {} + Window window = getWindow(); + window.getAttributes().systemUiVisibility |= + (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT); + window.setNavigationBarColor(Color.TRANSPARENT); } } -- cgit v1.2.3 From 00b8a2bcdfc8054df5f029f421ac485163c67e1f Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 21 Jan 2015 08:01:40 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I5064a01cd7cc763df0146ad40b4fc43a44f4346e Auto-generated-cl: translation import --- res/values-af/strings.xml | 2 +- res/values-am/strings.xml | 2 +- res/values-ar/strings.xml | 2 +- res/values-bg/strings.xml | 2 +- res/values-bn-rBD/strings.xml | 2 +- res/values-ca/strings.xml | 2 +- res/values-cs/strings.xml | 2 +- res/values-da/strings.xml | 2 +- res/values-de/strings.xml | 2 +- res/values-el/strings.xml | 2 +- res/values-en-rGB/strings.xml | 2 +- res/values-en-rIN/strings.xml | 2 +- res/values-es-rUS/strings.xml | 2 +- res/values-es/strings.xml | 2 +- res/values-et-rEE/strings.xml | 2 +- res/values-eu-rES/strings.xml | 2 +- res/values-fa/strings.xml | 2 +- res/values-fi/strings.xml | 2 +- res/values-fr-rCA/strings.xml | 2 +- res/values-fr/strings.xml | 2 +- res/values-gl-rES/strings.xml | 2 +- res/values-hi/strings.xml | 2 +- res/values-hr/strings.xml | 2 +- res/values-hu/strings.xml | 2 +- res/values-hy-rAM/strings.xml | 2 +- res/values-in/strings.xml | 2 +- res/values-is-rIS/strings.xml | 2 +- res/values-it/strings.xml | 2 +- res/values-iw/strings.xml | 2 +- res/values-ja/strings.xml | 2 +- res/values-ka-rGE/strings.xml | 2 +- res/values-kk-rKZ/strings.xml | 2 +- res/values-km-rKH/strings.xml | 4 ++-- res/values-kn-rIN/strings.xml | 2 +- res/values-ko/strings.xml | 2 +- res/values-ky-rKG/strings.xml | 2 +- res/values-lo-rLA/strings.xml | 6 +++--- res/values-lt/strings.xml | 2 +- res/values-lv/strings.xml | 2 +- res/values-mk-rMK/strings.xml | 2 +- res/values-ml-rIN/strings.xml | 2 +- res/values-mn-rMN/strings.xml | 2 +- res/values-mr-rIN/strings.xml | 2 +- res/values-ms-rMY/strings.xml | 2 +- res/values-my-rMM/strings.xml | 10 +++++----- res/values-nb/strings.xml | 2 +- res/values-ne-rNP/strings.xml | 2 +- res/values-nl/strings.xml | 2 +- res/values-pl/strings.xml | 2 +- res/values-pt-rPT/strings.xml | 2 +- res/values-pt/strings.xml | 2 +- res/values-ro/strings.xml | 2 +- res/values-ru/strings.xml | 2 +- res/values-si-rLK/strings.xml | 2 +- res/values-sk/strings.xml | 2 +- res/values-sl/strings.xml | 2 +- res/values-sr/strings.xml | 2 +- res/values-sv/strings.xml | 2 +- res/values-sw/strings.xml | 2 +- res/values-ta-rIN/strings.xml | 2 +- res/values-te-rIN/strings.xml | 2 +- res/values-th/strings.xml | 2 +- res/values-tl/strings.xml | 2 +- res/values-tr/strings.xml | 2 +- res/values-uk/strings.xml | 2 +- res/values-ur-rPK/strings.xml | 2 +- res/values-uz-rUZ/strings.xml | 2 +- res/values-vi/strings.xml | 2 +- res/values-zh-rCN/strings.xml | 2 +- res/values-zh-rHK/strings.xml | 2 +- res/values-zh-rTW/strings.xml | 2 +- res/values-zu/strings.xml | 2 +- 72 files changed, 79 insertions(+), 79 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index f37f08f3b..ca860cfcc 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -53,7 +53,6 @@ "Kies program" "Programme" "Tuis" - "Verwyder" "Deïnstalleer" "Verwyder" "Deïnstalleer" @@ -121,4 +120,5 @@ "Soek" "Hierdie program is nie geïnstalleer nie" "Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer." + "Voeg by werkspasie" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 1a25f66cd..4adf81187 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -53,7 +53,6 @@ "መተግበሪያ ይምረጡ" "መተግበሪያዎች" "መነሻ" - "አስወግድ" "አራግፍ" "አስወግድ" "አራግፍ" @@ -121,4 +120,5 @@ "ፈልግ" "ይህ መተግበሪያ አልተጫነም" "የዚህ አዶ መተግበሪያ አልተጫነም። ማስወገድ ወይም መተግበሪያውን መፈለግና ራስዎ መጫን ይችላሉ።" + "ወደ ስራ ቦታ ያክሉ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 592be5841..b5ffcf218 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -53,7 +53,6 @@ "اختيار تطبيق" "التطبيقات" "الرئيسية" - "إزالة" "إزالة" "إزالة" "إزالة" @@ -121,4 +120,5 @@ "بحث" "لم يتم تثبيت هذا التطبيق" "لم يتم تثبيت تطبيق لهذا الرمز. يمكنك إزالته أو البحث عن التطبيق وتثبيته يدويًا." + "إضافة إلى مساحة العمل" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 3f710db30..cb8f9ae96 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -53,7 +53,6 @@ "Избор на приложение" "Приложения" "Начало" - "Премахване" "Деинсталиране" "Премахване" "Деинсталиране" @@ -121,4 +120,5 @@ "Търсене" "Това приложение не е инсталирано" "Приложението за тази икона не е инсталирано. Можете да я премахнете или да потърсите приложението и да го инсталирате ръчно." + "Добавяне към раб. пространство" diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index 143420103..31dd461d2 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -53,7 +53,6 @@ "অ্যাপ্লিকেশান চয়ন করুন" "অ্যাপ্লিকেশানগুলি" "হোম" - "সরান" "আনইনস্টল করুন" "সরান" "আনইনস্টল করুন" @@ -121,4 +120,5 @@ "অনুসন্ধান" "এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই" "এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।" + "ওয়ার্কস্পেস যোগ করুন" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 89e582a81..d466b5376 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -53,7 +53,6 @@ "Tria d\'una aplicació" "Aplicacions" "Inici" - "Suprimeix" "Desinstal·la" "Suprimeix" "Desinstal·la" @@ -121,4 +120,5 @@ "Cerca" "Aquesta aplicació no està instal·lada" "L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment." + "Afegeix a l\'espai de treball" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 0288806f4..228c8fdb2 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -53,7 +53,6 @@ "Výběr aplikace" "Aplikace" "Plocha" - "Odstranit" "Odinstalovat" "Odstranit" "Odinstalovat" @@ -121,4 +120,5 @@ "Hledat" "Tato aplikace není nainstalována" "Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně." + "Přidat do pracovního prostoru" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 5fa6aa6b6..25f22f7b5 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -53,7 +53,6 @@ "Vælg app" "Apps" "Startskærm" - "Fjern" "Afinstaller" "Fjern" "Afinstaller" @@ -121,4 +120,5 @@ "Søg" "Denne app er ikke installeret" "Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt." + "Føj til arbejdsområdet" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 059983ecf..af55808f9 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -53,7 +53,6 @@ "App auswählen" "Apps" "Startseite" - "Entfernen" "Deinstallieren" "Entfernen" "Deinstallieren" @@ -121,4 +120,5 @@ "Suchen" "Diese App ist nicht installiert" "Die App für dieses Symbol ist nicht installiert. Sie können das Symbol entfernen oder nach der App suchen und sie manuell installieren." + "Zum Arbeitsbereich hinzufügen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 12f0dfef1..969904503 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -53,7 +53,6 @@ "Επιλογή εφαρμογής" "Εφαρμογές" "Αρχική σελίδα" - "Κατάργηση" "Κατάργηση εγκατάστασης" "Κατάργηση" "Κατάργηση εγκατάστασης" @@ -121,4 +120,5 @@ "Αναζήτηση" "Αυτή η εφαρμογή δεν είναι εγκατεστημένη" "Η εφαρμογή γι\' αυτό το εικονίδιο δεν είναι εγκατεστημένη. Μπορείτε να το καταργήσετε ή να αναζητήσετε την εφαρμογή και να την εγκαταστήσετε με μη αυτόματο τρόπο." + "Προσθήκη στο χώρο εργασίας" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 0d004fef5..2af4666ca 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -53,7 +53,6 @@ "Choose app" "Apps" "Home" - "Remove" "Uninstall" "Remove" "Uninstall" @@ -121,4 +120,5 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." + "Add To Workspace" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 0d004fef5..2af4666ca 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -53,7 +53,6 @@ "Choose app" "Apps" "Home" - "Remove" "Uninstall" "Remove" "Uninstall" @@ -121,4 +120,5 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." + "Add To Workspace" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 4408080e2..ab2e79f0a 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -53,7 +53,6 @@ "Elegir aplicación" "Aplicaciones" "Pantalla principal" - "Eliminar" "Desinstalar" "Eliminar" "Desinstalar" @@ -121,4 +120,5 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente." + "Agregar al espacio de trabajo" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 898d0eaa5..196db8a80 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -53,7 +53,6 @@ "Selecciona una aplicación" "Aplicaciones" "Inicio" - "Eliminar" "Desinstalar" "Eliminar" "Desinstalar" @@ -121,4 +120,5 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente." + "Añadir a espacio de trabajo" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 76b20aba7..044f10abd 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -53,7 +53,6 @@ "Rakenduse valimine" "Rakendused" "Avaekraan" - "Eemalda" "Desinstalli" "Eemalda" "Desinstalli" @@ -121,4 +120,5 @@ "Otsing" "See rakendus ei ole installitud" "Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida." + "Tööruumi lisamine" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index e02479c0e..8cd151855 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -53,7 +53,6 @@ "Aukeratu aplikazioa" "Aplikazioak" "Hasiera" - "Kendu" "Desinstalatu" "Kendu" "Desinstalatu" @@ -121,4 +120,5 @@ "Bilatu" "Aplikazio hau ez dago instalatuta" "Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu." + "Gehitu lan-eremuan" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 924cccf75..894b9f594 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -53,7 +53,6 @@ "انتخاب برنامه" "برنامه‌ها" "صفحه اصلی" - "حذف" "حذف نصب" "حذف" "حذف نصب" @@ -121,4 +120,5 @@ "جستجو" "این برنامه نصب نشده است." "برنامه برای این نماد نصب نشده است. می‌توانید آن را حذف کنید یا سعی کنید برنامه را جستجو کنید و آن را به صورت دستی نصب کنید." + "افزودن به فضای کاری" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index a5da03f43..455436b3f 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -53,7 +53,6 @@ "Valitse sovellus" "Sovellukset" "Aloitusruutu" - "Poista" "Poista" "Poista" "Poista" @@ -121,4 +120,5 @@ "Haku" "Sovellusta ei ole asennettu" "Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti." + "Lisää työtilaan" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 62a2b894e..cee8a6aca 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -53,7 +53,6 @@ "Sélectionner une application" "Applications" "Accueil" - "Supprimer" "Désinstaller" "Supprimer" "Désinstaller" @@ -121,4 +120,5 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement." + "Ajouter à l\'espace de travail" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index d23b6a492..adcc1e8b6 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -53,7 +53,6 @@ "Sélectionner une application" "Applications" "Accueil" - "Supprimer" "Désinstaller" "Supprimer" "Désinstaller" @@ -121,4 +120,5 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement." + "Ajouter à l\'espace de travail" diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index 96a53dc09..ed0eeb04a 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -53,7 +53,6 @@ "Escoller unha aplicación" "Aplicacións" "Inicio" - "Eliminar" "Desinstalar" "Eliminar" "Desinstalar" @@ -121,4 +120,5 @@ "Buscar" "Esta aplicación non está instalada" "A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente." + "Engadir a espazo de traballo" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 937248921..995de4000 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -53,7 +53,6 @@ "एप्‍लिकेशन चुनें" "ऐप्लिकेशन" "होम" - "निकालें" "अनइंस्टॉल करें" "निकालें" "अनइंस्टॉल करें" @@ -121,4 +120,5 @@ "खोजें" "यह ऐप्स इंस्टॉल नहीं है" "इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." + "कार्यस्‍थल में जोड़ें" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 702b474ff..5c3e86fde 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -53,7 +53,6 @@ "Odabir aplikacije" "Aplikacije" "Početna" - "Ukloni" "Deinstaliraj" "Ukloni" "Deinstaliraj" @@ -121,4 +120,5 @@ "Traži" "Ta aplikacija nije instalirana" "Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno." + "Dodavanje u radni prostor" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 513548a2a..0ee2748e2 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -53,7 +53,6 @@ "Válasszon alkalmazást" "Alkalmazások" "Főoldal" - "Eltávolítás" "Eltávolítás" "Eltávolítás" "Eltávolítás" @@ -121,4 +120,5 @@ "Keresés" "Az alkalmazás nincs telepítve" "Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt." + "Hozzáadás a munkaterülethez" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 940c14bf2..73c066837 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -53,7 +53,6 @@ "Ընտրել ծրագիրը" "Ծրագրեր" "Հիմնական" - "Հեռացնել" "Ապատեղադրել" "Հեռացնել" "Ապատեղադրել" @@ -121,4 +120,5 @@ "Գտնել" "Այս ծրագիրը տեղադրված չէ:" "Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:" + "Ավելացնել աշխատատարածքին" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index b0c5d139d..8af1d2344 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -53,7 +53,6 @@ "Pilih aplikasi" "Aplikasi" "Layar Utama" - "Hapus" "Copot pemasangan" "Hapus" "Copot pemasangan" @@ -121,4 +120,5 @@ "Telusuri" "Aplikasi ini belum terpasang" "Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual." + "Tambahkan Ke Ruang Kerja" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index a3fa0bc52..bf0b0ca25 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -53,7 +53,6 @@ "Veldu forrit" "Forrit" "Heim" - "Fjarlægja" "Eyða" "Fjarlægja" "Eyða" @@ -121,4 +120,5 @@ "Leita" "Þetta forrit er ekki uppsett" "Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt." + "Bæta við vinnusvæði" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index d78d0326d..c08ab6b75 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -53,7 +53,6 @@ "Scegli app" "App" "Home page" - "Rimuovi" "Disinstalla" "Rimuovi" "Disinstalla" @@ -121,4 +120,5 @@ "Cerca" "L\'app non è installata" "L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente." + "Aggiunta all\'area di lavoro" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 61fb53efa..effebd627 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -53,7 +53,6 @@ "בחר אפליקציה" "אפליקציות" "דף הבית" - "הסר" "הסר התקנה" "הסר" "הסר התקנה" @@ -121,4 +120,5 @@ "חפש" "אפליקציה זו אינה מותקנת" "האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית." + "הוסף לסביבת העבודה" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index ec872b8a9..555f29089 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -53,7 +53,6 @@ "アプリを選択" "アプリ" "ホーム" - "削除" "アンインストール" "削除" "アンインストール" @@ -121,4 +120,5 @@ "検索" "このアプリはインストールされていません" "このアイコンのアプリはインストールされていません。このアイコンは削除できます。または、手動でアプリを検索してインストールしください。" + "作業スペースに追加" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index b96d3d267..d4c2582d6 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -53,7 +53,6 @@ "აირჩიეთ აპი" "აპები" "მთავარი" - "წაშლა" "დეინსტალაცია" "წაშლა" "დეინსტალაცია" @@ -121,4 +120,5 @@ "ძიება" "ეს აპი დაყენებული არ არის" "ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია." + "სამუშაო სივრცეში დამატება" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index c5b5a62f6..3adf2d9e6 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -53,7 +53,6 @@ "Қолданба таңдау" "Қолданбалар" "Негізгі" - "Алып тастау" "Алмау" "Алып тастау" "Алмау" @@ -121,4 +120,5 @@ "Іздеу" "Бұл қолданба орнатылмаған" "Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады." + "Жұмыс кеңістігіне қосу" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 3a1c82dcc..c7e683000 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -53,7 +53,6 @@ "ជ្រើស​កម្មវិធី" "កម្មវិធី" "ដើម" - "លុប​ចេញ" "លុប" "លុប​ចេញ" "លុប" @@ -85,7 +84,7 @@ "អេក្រង់​ដើម %1$d នៃ %2$d" "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" - "សូម​ស្វាគមន៍" + "សូម​ស្វាគមន៍​" "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" @@ -121,4 +120,5 @@ "ស្វែងរក" "មិន​បាន​ដំឡើង​កម្មវិធី​នេះ" "មិន​បាន​ដំឡើង​កម្មវិធី​សម្រាប់​រូបតំណាង​នេះ។ អ្នក​អាច​លុប​វា ឬ​ស្វែងរក​កម្មវិធី និង​ដំឡើង​វា​ដោយ​ដៃ។" + "បន្ថែមទៅចន្លោះបណ្តោះអាសន្ន" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index 7a070511d..b327ebf0a 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -53,7 +53,6 @@ "ಅಪ್ಲಿಕೇಶನ್ ಆಯ್ಕೆಮಾಡಿ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" "ಮುಖಪುಟ" - "ತೆಗೆದುಹಾಕು" "ಅಸ್ಥಾಪಿಸು" "ತೆಗೆದುಹಾಕು" "ಅಸ್ಥಾಪಿಸು" @@ -121,4 +120,5 @@ "ಹುಡುಕು" "ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ" "ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು." + "ಕಾರ್ಯಕ್ಷೇತ್ರಕ್ಕೆ ಸೇರಿಸಿ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index c73b431bd..a9aeedc71 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -53,7 +53,6 @@ "앱 선택" "앱" "홈" - "삭제" "제거" "삭제" "제거" @@ -121,4 +120,5 @@ "검색" "이 앱이 설치되어 있지 않음" "이 아이콘의 앱이 설치되어 있지 않습니다. 아이콘을 삭제하거나 앱을 검색하여 수동으로 설치하세요." + "작업공간에 추가" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index a8a605b26..7dd1206b4 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -53,7 +53,6 @@ "Колдонмо тандоо" "Колдонмолор" "Үйгө" - "Алып салуу" "Чечип салуу" "Алып салуу" "Чечип салуу" @@ -121,4 +120,5 @@ "Издөө" "Бул колдонмо орнотулган эмес" "Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот." + "Жумуш ордуна кошуу" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 65f01b7f0..8832408cd 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -37,7 +37,7 @@ "ຊື່ໂຟນເດີ" "ປ່ຽນຊື່ໂຟນເດີ" "ຕົກລົງ" - "ຍົກ​ເລີກ" + "ຍົກ​ເລີກ​" "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" "ແອັບຯ" "ທາງລັດ" @@ -53,7 +53,6 @@ "ເລືອກແອັບຯ" "ແອັບຯ" "ໜ້າຫຼັກ" - "ລຶບ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ລຶບ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" @@ -117,8 +116,9 @@ "​ບໍ່​ຮູ້​ຈັກ" "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" "ລຶບ​ທັງ​ໝົດ" - "ລຶບ" + "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." + "ເພີ່ມ​ໃສ່​ບ່ອນ​ເຮັດ​ວຽກ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 897c2fd9f..6bb39650e 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -53,7 +53,6 @@ "Pasirinkite programą" "Programos" "Pagrindinis" - "Pašalinti" "Pašalinti" "Pašalinti" "Pašalinti" @@ -121,4 +120,5 @@ "Ieškoti" "Ši programa neįdiegta" "Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys." + "Pridėti prie darbo srities" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 13a47606d..347cd3eb1 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -53,7 +53,6 @@ "Lietotnes izvēle" "Lietotnes" "Sākums" - "Noņemt" "Atinstalēt" "Noņemt" "Atinstalēt" @@ -121,4 +120,5 @@ "Meklēt" "Šī lietotne nav instalēta" "Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli." + "Pievienot darbvietai" diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index dcc55d7b7..129f52c64 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -53,7 +53,6 @@ "Избери апликација" "Апликации" "Почетна страница" - "Отстрани" "Деинсталирај" "Отстрани" "Деинсталирај" @@ -121,4 +120,5 @@ "Барај" "Апликацијата не е инсталирана" "Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно." + "Додај на работна површина" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index cf5efaae2..210b78138 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -53,7 +53,6 @@ "അപ്ലിക്കേഷൻ തിരഞ്ഞെടുക്കുക" "അപ്ലിക്കേഷനുകൾ" "ഹോം" - "നീക്കംചെയ്യുക" "അണ്‍ഇസ്റ്റാളുചെയ്യുക" "നീക്കംചെയ്യുക" "അണ്‍ഇസ്റ്റാളുചെയ്യുക" @@ -121,4 +120,5 @@ "തിരയുക" "ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല" "ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക." + "വർക്ക്‌സ്‌പെയ്‌സിൽ ചേർക്കുക" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index f258b4c8d..da9662b9c 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -53,7 +53,6 @@ "Апп сонгох" "Апп" "Нүүр" - "Устгах" "Устгах" "Устгах" "Устгах" @@ -121,4 +120,5 @@ "Хайх" "Энэ апп-г суулгаагүй байна" "Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой." + "Ажлын талбар нэмэх" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index 098e83023..db27b21d1 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -53,7 +53,6 @@ "अॅप निवडा" "अॅप्स" "मुख्‍यपृष्‍ठ" - "काढा" "विस्थापित करा" "काढा" "विस्थापित करा" @@ -121,4 +120,5 @@ "शोधा" "हा अॅप स्थापित केलेला नाही" "या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता." + "Workspace वर जोडा" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 532b6ad8b..fb28bc358 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -53,7 +53,6 @@ "Pilih apl" "Apl" "Laman Utama" - "Alih keluar" "Nyahpasang" "Alih keluar" "Nyahpasang" @@ -121,4 +120,5 @@ "Carian" "Apl ini tidak dipasang" "Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual." + "Tambahkan Pada Ruang Kerja" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 668803587..c6bbc1ab1 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -53,14 +53,13 @@ "အပလီကေးရှင်း တစ်ခုခုကို ရွေးချယ်ပါ" "အပ်ပလီကေးရှင်းများ" "ပင်မစာမျက်နှာ" - "ဖယ်ရှာခြင်း" "ဖယ်ရှားခြင်း" "ဖယ်ရှာခြင်း" "ဖယ်ရှားခြင်း" "အပ်ပလီကေးရှင်း အချက်အလက်များ" "အပ်ပလီကေးရှင်းများ" "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" + "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" @@ -76,7 +75,7 @@ "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" "ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း" "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" - "စဖွင့်သတ်မှတ်ရန်" + "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" @@ -102,7 +101,7 @@ "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" - "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" + "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" "ပိတ်ထားသောအကန့်" @@ -111,7 +110,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" + "စောင့်နေ" "ဒေါင်းလုဒ် လုပ်နေ" "တပ်ဆင်နေ" "မသိရ" @@ -121,4 +120,5 @@ "ရှာဖွေရန်" "App မတပ်ဆင်ရသေးပါ" "ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually." + "အလုပ်နေရာသို့ ပေါင်းထည့်မည်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index a9bbe08ca..fbed8c6e6 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -53,7 +53,6 @@ "Velg app" "Apper" "Startside" - "Fjern" "Avinstaller" "Fjern" "Avinstaller" @@ -121,4 +120,5 @@ "Søk" "Denne appen er ikke installert" "Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt." + "Legg til i arbeidsområdet" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 815873de9..5e5d25dd6 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -53,7 +53,6 @@ "अनुप्रयोग छनौट गर्नुहोस्" "अनुप्रयोगहरू" "गृह" - "हटाउनुहोस्" "हटाउनुहोस्" "हटाउनुहोस्" "हटाउनुहोस्" @@ -123,4 +122,5 @@ "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" "यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।" + "कार्यक्षेत्रमा जोड्नुहोस्" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 8a32fd1d9..a3289dae7 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -53,7 +53,6 @@ "App selecteren" "Apps" "Startpagina" - "Verwijderen" "Verwijderen" "Verwijderen" "Verwijderen" @@ -121,4 +120,5 @@ "Zoeken" "Deze app is niet geïnstalleerd" "De app voor dit pictogram is niet geïnstalleerd. U kunt het pictogram verwijderen of de app zoeken en handmatig installeren." + "Toevoegen aan werkruimte" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 4eabae96e..aad731611 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -53,7 +53,6 @@ "Wybierz aplikację" "Aplikacje" "Ekran główny" - "Usuń" "Odinstaluj" "Usuń" "Odinstaluj" @@ -121,4 +120,5 @@ "Szukaj" "Ta aplikacja nie jest zainstalowana" "Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie." + "Dodaj do obszaru roboczego" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 4397ebf81..a08bfc7c5 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -53,7 +53,6 @@ "Escolher aplicação" "Aplicações" "Ecrã principal" - "Remover" "Desinstalar" "Remover" "Desinstalar" @@ -121,4 +120,5 @@ "Pesquisar" "Esta aplicação não está instalada" "A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente." + "Adic. ao espaço de trabalho" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 07c82143e..d92f8e95d 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -53,7 +53,6 @@ "Selecione um app" "Apps" "Início" - "Remover" "Desinstalar" "Remover" "Desinstalar" @@ -121,4 +120,5 @@ "Pesquisar" "Este app não está instalado" "O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente." + "Adicionar a espaço de trabalho" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 54cb46c3f..832e4a5f8 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -53,7 +53,6 @@ "Alegeți aplicația" "Aplicații" "Ecran de pornire" - "Eliminați" "Dezinstalați" "Eliminați" "Dezinstalați" @@ -121,4 +120,5 @@ "Căutați" "Aplicația nu este instalată" "Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual." + "Adăugați în spațiul de lucru" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 70ef0f591..0341317c0 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -53,7 +53,6 @@ "Выбор приложения" "Приложения" "Главный экран" - "Удалить" "Удалить" "Удалить" "Удалить" @@ -121,4 +120,5 @@ "Найти" "Приложение не установлено" "Приложение не установлено. Вы можете удалить значок или найти приложение и установить его вручную." + "Добавить в рабочую область" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index ab3abb83b..486baf8a1 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -53,7 +53,6 @@ "යෙදුම තේරීම" "යෙදුම්" "මුල් පිටුව" - "ඉවත් කරන්න" "අස්ථාපනය කරන්න" "ඉවත් කරන්න" "අස්ථාපනය කරන්න" @@ -121,4 +120,5 @@ "සොයන්න" "මෙම යෙදුම ස්ථාපනය කර නොමැත" "මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න." + "කාර්ය ඉඩ වෙත එකතු කරන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 3f41f2d7e..45e0d4b45 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -53,7 +53,6 @@ "Vybrať aplikáciu" "Aplikácie" "Domovská stránka" - "Odstrániť" "Odinštalovať" "Odstrániť" "Odinštalovať" @@ -121,4 +120,5 @@ "Vyhľadať" "Táto aplikácia nie je nainštalovaná" "Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte." + "Pridať do pracovného priestoru" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index d5a34d58f..6ab289b75 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -53,7 +53,6 @@ "Izberite aplikacijo" "Aplikacije" "Začetni zaslon" - "Odstrani" "Odstrani" "Odstrani" "Odstrani" @@ -121,4 +120,5 @@ "Iskanje" "Ta aplikacija ni nameščena." "Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno." + "Dodajanje v delovni prostor" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 9f40890c4..66ff8923d 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -53,7 +53,6 @@ "Избор апликације" "Апликације" "Почетна" - "Уклони" "Деинсталирај" "Уклони" "Деинсталирај" @@ -121,4 +120,5 @@ "Претражи" "Ова апликација није инсталирана" "Апликација за ову икону није инсталирана. Можете да је уклоните или да потражите апликацију и инсталирате је ручно." + "Додај у радни простор" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 25f4d2623..13256bed7 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -53,7 +53,6 @@ "Välj app" "Appar" "Startskärm" - "Ta bort" "Avinstallera" "Ta bort" "Avinstallera" @@ -121,4 +120,5 @@ "Sök" "Appen är inte installerad" "Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt." + "Lägg till på arbetsyta" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index fa810618b..9d9e4a589 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -53,7 +53,6 @@ "Chagua programu" "Programu" "Mwanzo" - "Ondoa" "Ondoa" "Ondoa" "Ondoa" @@ -123,4 +122,5 @@ "Tafuta" "Programu hii haijasakinishwa" "Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." + "Ongeza Kwenye Nafasi ya kazi" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index 9dba6f669..2eecc22b4 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -53,7 +53,6 @@ "பயன்பாட்டைத் தேர்வுசெய்யவும்" "பயன்பாடுகள்" "முகப்பு" - "அகற்று" "நிறுவல் நீக்கு" "அகற்று" "நிறுவல் நீக்கு" @@ -121,4 +120,5 @@ "தேடு" "பயன்பாடு நிறுவப்படவில்லை" "ஐகானுக்கான பயன்பாடு நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்." + "பணியிடத்தில் சேர்" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index bfbc7920f..2d2ac2247 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -53,7 +53,6 @@ "అనువర్తనాన్ని ఎంచుకోండి" "అనువర్తనాలు" "హోమ్" - "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" @@ -121,4 +120,5 @@ "శోధించు" "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" "ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." + "వర్క్‌స్పేస్‌కు జోడించు" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 0fc20d26f..89b5b9bc0 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -53,7 +53,6 @@ "เลือกแอป" "แอป" "หน้าแรก" - "ลบ" "ถอนการติดตั้ง" "ลบ" "ถอนการติดตั้ง" @@ -121,4 +120,5 @@ "ค้นหา" "ไม่ได้ติดตั้งแอปนี้" "ยังไม่ได้ติดตั้งแอปสำหรับไอคอนนี้ คุณสามารถนำไอคอนออก หรือค้นหาแอปดังกล่าวแล้วติดตั้งด้วยตนเอง" + "เพิ่มไปยังพื้นที่ทำงาน" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 4d2f1d478..958ae5053 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -53,7 +53,6 @@ "Pumili ng app" "Apps" "Home" - "Alisin" "I-uninstall" "Alisin" "I-uninstall" @@ -121,4 +120,5 @@ "Maghanap" "Hindi naka-install ang app na ito" "Hindi naka-install ang app para sa icon na ito. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano." + "Idagdag Sa Workspace" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 6c78f6f63..97f5c1eb3 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -53,7 +53,6 @@ "Uygulama seçin" "Uygulamalar" "Ana ekran" - "Kaldır" "Yüklemeyi kaldır" "Kaldır" "Yüklemeyi kaldır" @@ -121,4 +120,5 @@ "Ara" "Bu uygulama yüklü değil" "Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz." + "Çalışma Alanına Ekle" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 71e1816a3..f3fbd9ea9 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -53,7 +53,6 @@ "Вибрати програму" "Додатки" "Головний екран" - "Вилучити" "Видалити" "Вилучити" "Видалити" @@ -121,4 +120,5 @@ "Шукати" "Цей додаток не встановлено" "Додаток для цього значка не встановлено. Можна видалити значок або знайти додаток і встановити його вручну." + "Додати в робочу область" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index ba1d3fa9a..5f227db05 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -53,7 +53,6 @@ "ایپ منتخب کریں" "ایپس" "ہوم" - "ہٹائیں" "اَن انسٹال کریں" "ہٹائیں" "اَن انسٹال کریں" @@ -121,4 +120,5 @@ "تلاش کریں" "یہ ایپ انسٹال کردہ نہیں ہے" "اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔" + "ورک اسپیس میں شامل کریں" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index a3c5670a3..6f298e67a 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -53,7 +53,6 @@ "Ilovani tanlash" "Ilovalar" "Uy" - "O‘chirish" "O‘chirish" "O‘chirish" "O‘chirish" @@ -121,4 +120,5 @@ "Qidirish" "Ushbu ilova o‘rnatilmagan" "Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin." + "Ishchi maydonga qo‘shish" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 5bc16275e..bf8a202ac 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -53,7 +53,6 @@ "Chọn ứng dụng" "Ứng dụng" "Màn hình chính" - "Xóa" "Gỡ cài đặt" "Xóa" "Gỡ cài đặt" @@ -121,4 +120,5 @@ "Tìm kiếm" "Ứng dụng này chưa được cài đặt" "Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công." + "Thêm vào không gian làm việc" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index b7c42fe5c..7be54fbe8 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -53,7 +53,6 @@ "选择应用" "应用" "主屏幕" - "删除" "卸载" "删除" "卸载" @@ -121,4 +120,5 @@ "搜索" "未安装此应用" "未安装此图标对应的应用。您可以移除此图标,也可以尝试搜索相应的应用并手动安装。" + "添加至工作区" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 2eb700890..b7a92656f 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -53,7 +53,6 @@ "選擇應用程式" "應用程式" "主畫面" - "移除" "解除安裝" "移除" "解除安裝" @@ -121,4 +120,5 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" + "加入工作區" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 52e758396..ecfd4c38a 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -53,7 +53,6 @@ "選擇應用程式" "應用程式" "主螢幕" - "移除" "解除安裝" "移除" "解除安裝" @@ -121,4 +120,5 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" + "新增至工作區" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index aea09e845..3b38e6a50 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -53,7 +53,6 @@ "Khetha uhlelo lokusebenza" "Izinhlelo zokusebenza" "Ikhaya" - "Susa" "Khipha" "Susa" "Khipha" @@ -121,4 +120,5 @@ "Sesha" "Lolu hlelo lokusebenza alifakiwe" "Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela." + "Engeza kusikhala sokusebenza" -- cgit v1.2.3 From f32cd5fbbb7b7ec1753606669c8936fd956df494 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 21 Jan 2015 08:02:36 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I56a37f9442adf586d40bfbb2b41b22ccf2052f51 Auto-generated-cl: translation import --- res/values-af/strings.xml | 3 +-- res/values-am/strings.xml | 3 +-- res/values-ar/strings.xml | 3 +-- res/values-bg/strings.xml | 3 +-- res/values-bn-rBD/strings.xml | 3 +-- res/values-ca/strings.xml | 3 +-- res/values-cs/strings.xml | 3 +-- res/values-da/strings.xml | 3 +-- res/values-de/strings.xml | 3 +-- res/values-el/strings.xml | 3 +-- res/values-en-rGB/strings.xml | 3 +-- res/values-en-rIN/strings.xml | 3 +-- res/values-es-rUS/strings.xml | 3 +-- res/values-es/strings.xml | 3 +-- res/values-et-rEE/strings.xml | 3 +-- res/values-eu-rES/strings.xml | 3 +-- res/values-fa/strings.xml | 3 +-- res/values-fi/strings.xml | 3 +-- res/values-fr-rCA/strings.xml | 3 +-- res/values-fr/strings.xml | 3 +-- res/values-gl-rES/strings.xml | 3 +-- res/values-hi/strings.xml | 3 +-- res/values-hr/strings.xml | 3 +-- res/values-hu/strings.xml | 3 +-- res/values-hy-rAM/strings.xml | 3 +-- res/values-in/strings.xml | 3 +-- res/values-is-rIS/strings.xml | 3 +-- res/values-it/strings.xml | 3 +-- res/values-iw/strings.xml | 3 +-- res/values-ja/strings.xml | 3 +-- res/values-ka-rGE/strings.xml | 3 +-- res/values-kk-rKZ/strings.xml | 3 +-- res/values-km-rKH/strings.xml | 3 +-- res/values-kn-rIN/strings.xml | 3 +-- res/values-ko/strings.xml | 3 +-- res/values-ky-rKG/strings.xml | 3 +-- res/values-lo-rLA/strings.xml | 3 +-- res/values-lt/strings.xml | 3 +-- res/values-lv/strings.xml | 3 +-- res/values-mk-rMK/strings.xml | 3 +-- res/values-ml-rIN/strings.xml | 3 +-- res/values-mn-rMN/strings.xml | 3 +-- res/values-mr-rIN/strings.xml | 3 +-- res/values-ms-rMY/strings.xml | 3 +-- res/values-my-rMM/strings.xml | 3 +-- res/values-nb/strings.xml | 3 +-- res/values-ne-rNP/strings.xml | 3 +-- res/values-nl/strings.xml | 3 +-- res/values-pl/strings.xml | 3 +-- res/values-pt-rPT/strings.xml | 3 +-- res/values-pt/strings.xml | 3 +-- res/values-ro/strings.xml | 3 +-- res/values-ru/strings.xml | 3 +-- res/values-si-rLK/strings.xml | 3 +-- res/values-sk/strings.xml | 3 +-- res/values-sl/strings.xml | 3 +-- res/values-sr/strings.xml | 3 +-- res/values-sv/strings.xml | 3 +-- res/values-sw/strings.xml | 3 +-- res/values-ta-rIN/strings.xml | 3 +-- res/values-te-rIN/strings.xml | 3 +-- res/values-th/strings.xml | 3 +-- res/values-tl/strings.xml | 3 +-- res/values-tr/strings.xml | 3 +-- res/values-uk/strings.xml | 3 +-- res/values-ur-rPK/strings.xml | 3 +-- res/values-uz-rUZ/strings.xml | 3 +-- res/values-vi/strings.xml | 3 +-- res/values-zh-rCN/strings.xml | 3 +-- res/values-zh-rHK/strings.xml | 3 +-- res/values-zh-rTW/strings.xml | 3 +-- res/values-zu/strings.xml | 3 +-- 72 files changed, 72 insertions(+), 144 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index f5fb42ff5..ca860cfcc 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -120,6 +120,5 @@ "Soek" "Hierdie program is nie geïnstalleer nie" "Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer." - - + "Voeg by werkspasie" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index cc8193aae..4adf81187 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -120,6 +120,5 @@ "ፈልግ" "ይህ መተግበሪያ አልተጫነም" "የዚህ አዶ መተግበሪያ አልተጫነም። ማስወገድ ወይም መተግበሪያውን መፈለግና ራስዎ መጫን ይችላሉ።" - - + "ወደ ስራ ቦታ ያክሉ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index a72f53593..b5ffcf218 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -120,6 +120,5 @@ "بحث" "لم يتم تثبيت هذا التطبيق" "لم يتم تثبيت تطبيق لهذا الرمز. يمكنك إزالته أو البحث عن التطبيق وتثبيته يدويًا." - - + "إضافة إلى مساحة العمل" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index bd911a07a..cb8f9ae96 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -120,6 +120,5 @@ "Търсене" "Това приложение не е инсталирано" "Приложението за тази икона не е инсталирано. Можете да я премахнете или да потърсите приложението и да го инсталирате ръчно." - - + "Добавяне към раб. пространство" diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index bead26cbc..31dd461d2 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -120,6 +120,5 @@ "অনুসন্ধান" "এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই" "এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।" - - + "ওয়ার্কস্পেস যোগ করুন" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index c6a28046a..d466b5376 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -120,6 +120,5 @@ "Cerca" "Aquesta aplicació no està instal·lada" "L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment." - - + "Afegeix a l\'espai de treball" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 723c5d801..228c8fdb2 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -120,6 +120,5 @@ "Hledat" "Tato aplikace není nainstalována" "Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně." - - + "Přidat do pracovního prostoru" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 4914030ea..25f22f7b5 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -120,6 +120,5 @@ "Søg" "Denne app er ikke installeret" "Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt." - - + "Føj til arbejdsområdet" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 078d83b2f..af55808f9 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -120,6 +120,5 @@ "Suchen" "Diese App ist nicht installiert" "Die App für dieses Symbol ist nicht installiert. Sie können das Symbol entfernen oder nach der App suchen und sie manuell installieren." - - + "Zum Arbeitsbereich hinzufügen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 58e1f8ad9..969904503 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -120,6 +120,5 @@ "Αναζήτηση" "Αυτή η εφαρμογή δεν είναι εγκατεστημένη" "Η εφαρμογή γι\' αυτό το εικονίδιο δεν είναι εγκατεστημένη. Μπορείτε να το καταργήσετε ή να αναζητήσετε την εφαρμογή και να την εγκαταστήσετε με μη αυτόματο τρόπο." - - + "Προσθήκη στο χώρο εργασίας" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 88987887f..2af4666ca 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -120,6 +120,5 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." - - + "Add To Workspace" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 88987887f..2af4666ca 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -120,6 +120,5 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." - - + "Add To Workspace" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index c047a7381..ab2e79f0a 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -120,6 +120,5 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente." - - + "Agregar al espacio de trabajo" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 5f3a0771c..196db8a80 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -120,6 +120,5 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente." - - + "Añadir a espacio de trabajo" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index d9e9fd02d..044f10abd 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -120,6 +120,5 @@ "Otsing" "See rakendus ei ole installitud" "Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida." - - + "Tööruumi lisamine" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 38006a826..8cd151855 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -120,6 +120,5 @@ "Bilatu" "Aplikazio hau ez dago instalatuta" "Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu." - - + "Gehitu lan-eremuan" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 76fe58c82..894b9f594 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -120,6 +120,5 @@ "جستجو" "این برنامه نصب نشده است." "برنامه برای این نماد نصب نشده است. می‌توانید آن را حذف کنید یا سعی کنید برنامه را جستجو کنید و آن را به صورت دستی نصب کنید." - - + "افزودن به فضای کاری" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 74871db2c..455436b3f 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -120,6 +120,5 @@ "Haku" "Sovellusta ei ole asennettu" "Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti." - - + "Lisää työtilaan" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 49027e71f..cee8a6aca 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -120,6 +120,5 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement." - - + "Ajouter à l\'espace de travail" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 85ff3d2cc..adcc1e8b6 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -120,6 +120,5 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement." - - + "Ajouter à l\'espace de travail" diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index af169564d..ed0eeb04a 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -120,6 +120,5 @@ "Buscar" "Esta aplicación non está instalada" "A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente." - - + "Engadir a espazo de traballo" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index cdc38d662..995de4000 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -120,6 +120,5 @@ "खोजें" "यह ऐप्स इंस्टॉल नहीं है" "इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." - - + "कार्यस्‍थल में जोड़ें" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 96db975db..5c3e86fde 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -120,6 +120,5 @@ "Traži" "Ta aplikacija nije instalirana" "Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno." - - + "Dodavanje u radni prostor" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index e44acbfed..0ee2748e2 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -120,6 +120,5 @@ "Keresés" "Az alkalmazás nincs telepítve" "Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt." - - + "Hozzáadás a munkaterülethez" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index a8536f5c4..73c066837 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -120,6 +120,5 @@ "Գտնել" "Այս ծրագիրը տեղադրված չէ:" "Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:" - - + "Ավելացնել աշխատատարածքին" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 24286b28e..8af1d2344 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -120,6 +120,5 @@ "Telusuri" "Aplikasi ini belum terpasang" "Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual." - - + "Tambahkan Ke Ruang Kerja" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index faca7828e..bf0b0ca25 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -120,6 +120,5 @@ "Leita" "Þetta forrit er ekki uppsett" "Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt." - - + "Bæta við vinnusvæði" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 351ce06be..c08ab6b75 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -120,6 +120,5 @@ "Cerca" "L\'app non è installata" "L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente." - - + "Aggiunta all\'area di lavoro" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index c0cb737a8..effebd627 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -120,6 +120,5 @@ "חפש" "אפליקציה זו אינה מותקנת" "האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית." - - + "הוסף לסביבת העבודה" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index d9537b407..555f29089 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -120,6 +120,5 @@ "検索" "このアプリはインストールされていません" "このアイコンのアプリはインストールされていません。このアイコンは削除できます。または、手動でアプリを検索してインストールしください。" - - + "作業スペースに追加" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index d91bff564..d4c2582d6 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -120,6 +120,5 @@ "ძიება" "ეს აპი დაყენებული არ არის" "ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია." - - + "სამუშაო სივრცეში დამატება" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index 555131d79..3adf2d9e6 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -120,6 +120,5 @@ "Іздеу" "Бұл қолданба орнатылмаған" "Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады." - - + "Жұмыс кеңістігіне қосу" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 18d04efe6..c7e683000 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -120,6 +120,5 @@ "ស្វែងរក" "មិន​បាន​ដំឡើង​កម្មវិធី​នេះ" "មិន​បាន​ដំឡើង​កម្មវិធី​សម្រាប់​រូបតំណាង​នេះ។ អ្នក​អាច​លុប​វា ឬ​ស្វែងរក​កម្មវិធី និង​ដំឡើង​វា​ដោយ​ដៃ។" - - + "បន្ថែមទៅចន្លោះបណ្តោះអាសន្ន" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index adc348dff..b327ebf0a 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -120,6 +120,5 @@ "ಹುಡುಕು" "ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ" "ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು." - - + "ಕಾರ್ಯಕ್ಷೇತ್ರಕ್ಕೆ ಸೇರಿಸಿ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 84130deb6..a9aeedc71 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -120,6 +120,5 @@ "검색" "이 앱이 설치되어 있지 않음" "이 아이콘의 앱이 설치되어 있지 않습니다. 아이콘을 삭제하거나 앱을 검색하여 수동으로 설치하세요." - - + "작업공간에 추가" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index 72f7068e4..7dd1206b4 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -120,6 +120,5 @@ "Издөө" "Бул колдонмо орнотулган эмес" "Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот." - - + "Жумуш ордуна кошуу" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 1382ff694..8832408cd 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -120,6 +120,5 @@ "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." - - + "ເພີ່ມ​ໃສ່​ບ່ອນ​ເຮັດ​ວຽກ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index e7b5d4bf7..6bb39650e 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -120,6 +120,5 @@ "Ieškoti" "Ši programa neįdiegta" "Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys." - - + "Pridėti prie darbo srities" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 47a60c157..347cd3eb1 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -120,6 +120,5 @@ "Meklēt" "Šī lietotne nav instalēta" "Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli." - - + "Pievienot darbvietai" diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index c9e14d32d..129f52c64 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -120,6 +120,5 @@ "Барај" "Апликацијата не е инсталирана" "Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно." - - + "Додај на работна површина" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index 4ad562e48..210b78138 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -120,6 +120,5 @@ "തിരയുക" "ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല" "ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക." - - + "വർക്ക്‌സ്‌പെയ്‌സിൽ ചേർക്കുക" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 5e9fc4822..da9662b9c 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -120,6 +120,5 @@ "Хайх" "Энэ апп-г суулгаагүй байна" "Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой." - - + "Ажлын талбар нэмэх" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index f863bcda9..db27b21d1 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -120,6 +120,5 @@ "शोधा" "हा अॅप स्थापित केलेला नाही" "या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता." - - + "Workspace वर जोडा" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 541e0e149..fb28bc358 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -120,6 +120,5 @@ "Carian" "Apl ini tidak dipasang" "Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual." - - + "Tambahkan Pada Ruang Kerja" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 3ea53d72d..c6bbc1ab1 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -120,6 +120,5 @@ "ရှာဖွေရန်" "App မတပ်ဆင်ရသေးပါ" "ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually." - - + "အလုပ်နေရာသို့ ပေါင်းထည့်မည်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 7a6066da2..fbed8c6e6 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -120,6 +120,5 @@ "Søk" "Denne appen er ikke installert" "Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt." - - + "Legg til i arbeidsområdet" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 4486f1ec5..5e5d25dd6 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -122,6 +122,5 @@ "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" "यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।" - - + "कार्यक्षेत्रमा जोड्नुहोस्" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 662cae7ad..a3289dae7 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -120,6 +120,5 @@ "Zoeken" "Deze app is niet geïnstalleerd" "De app voor dit pictogram is niet geïnstalleerd. U kunt het pictogram verwijderen of de app zoeken en handmatig installeren." - - + "Toevoegen aan werkruimte" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index fd3abd952..aad731611 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -120,6 +120,5 @@ "Szukaj" "Ta aplikacja nie jest zainstalowana" "Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie." - - + "Dodaj do obszaru roboczego" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 83ad2d745..a08bfc7c5 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -120,6 +120,5 @@ "Pesquisar" "Esta aplicação não está instalada" "A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente." - - + "Adic. ao espaço de trabalho" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 5a5253cec..d92f8e95d 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -120,6 +120,5 @@ "Pesquisar" "Este app não está instalado" "O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente." - - + "Adicionar a espaço de trabalho" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 2e30eb72b..832e4a5f8 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -120,6 +120,5 @@ "Căutați" "Aplicația nu este instalată" "Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual." - - + "Adăugați în spațiul de lucru" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 5993db313..0341317c0 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -120,6 +120,5 @@ "Найти" "Приложение не установлено" "Приложение не установлено. Вы можете удалить значок или найти приложение и установить его вручную." - - + "Добавить в рабочую область" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index b51a9c155..486baf8a1 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -120,6 +120,5 @@ "සොයන්න" "මෙම යෙදුම ස්ථාපනය කර නොමැත" "මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න." - - + "කාර්ය ඉඩ වෙත එකතු කරන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 626b0ee43..45e0d4b45 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -120,6 +120,5 @@ "Vyhľadať" "Táto aplikácia nie je nainštalovaná" "Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte." - - + "Pridať do pracovného priestoru" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index e4aba274b..6ab289b75 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -120,6 +120,5 @@ "Iskanje" "Ta aplikacija ni nameščena." "Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno." - - + "Dodajanje v delovni prostor" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 033d129bc..66ff8923d 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -120,6 +120,5 @@ "Претражи" "Ова апликација није инсталирана" "Апликација за ову икону није инсталирана. Можете да је уклоните или да потражите апликацију и инсталирате је ручно." - - + "Додај у радни простор" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index eb298b12b..13256bed7 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -120,6 +120,5 @@ "Sök" "Appen är inte installerad" "Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt." - - + "Lägg till på arbetsyta" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index e811727ae..9d9e4a589 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -122,6 +122,5 @@ "Tafuta" "Programu hii haijasakinishwa" "Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." - - + "Ongeza Kwenye Nafasi ya kazi" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index 60af81fff..2eecc22b4 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -120,6 +120,5 @@ "தேடு" "பயன்பாடு நிறுவப்படவில்லை" "ஐகானுக்கான பயன்பாடு நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்." - - + "பணியிடத்தில் சேர்" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index 9b8484c3e..2d2ac2247 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -120,6 +120,5 @@ "శోధించు" "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" "ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." - - + "వర్క్‌స్పేస్‌కు జోడించు" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 4d2d02c8f..89b5b9bc0 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -120,6 +120,5 @@ "ค้นหา" "ไม่ได้ติดตั้งแอปนี้" "ยังไม่ได้ติดตั้งแอปสำหรับไอคอนนี้ คุณสามารถนำไอคอนออก หรือค้นหาแอปดังกล่าวแล้วติดตั้งด้วยตนเอง" - - + "เพิ่มไปยังพื้นที่ทำงาน" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index f74c0058a..958ae5053 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -120,6 +120,5 @@ "Maghanap" "Hindi naka-install ang app na ito" "Hindi naka-install ang app para sa icon na ito. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano." - - + "Idagdag Sa Workspace" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 046304c78..97f5c1eb3 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -120,6 +120,5 @@ "Ara" "Bu uygulama yüklü değil" "Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz." - - + "Çalışma Alanına Ekle" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 01d8390e4..f3fbd9ea9 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -120,6 +120,5 @@ "Шукати" "Цей додаток не встановлено" "Додаток для цього значка не встановлено. Можна видалити значок або знайти додаток і встановити його вручну." - - + "Додати в робочу область" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index 5ccb2f2c7..5f227db05 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -120,6 +120,5 @@ "تلاش کریں" "یہ ایپ انسٹال کردہ نہیں ہے" "اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔" - - + "ورک اسپیس میں شامل کریں" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index c10f757b9..6f298e67a 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -120,6 +120,5 @@ "Qidirish" "Ushbu ilova o‘rnatilmagan" "Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin." - - + "Ishchi maydonga qo‘shish" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index c887624f3..bf8a202ac 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -120,6 +120,5 @@ "Tìm kiếm" "Ứng dụng này chưa được cài đặt" "Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công." - - + "Thêm vào không gian làm việc" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 12229c56f..7be54fbe8 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -120,6 +120,5 @@ "搜索" "未安装此应用" "未安装此图标对应的应用。您可以移除此图标,也可以尝试搜索相应的应用并手动安装。" - - + "添加至工作区" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 322d703a0..b7a92656f 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -120,6 +120,5 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" - - + "加入工作區" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 5abb913a7..ecfd4c38a 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -120,6 +120,5 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" - - + "新增至工作區" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 68481cb3a..3b38e6a50 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -120,6 +120,5 @@ "Sesha" "Lolu hlelo lokusebenza alifakiwe" "Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela." - - + "Engeza kusikhala sokusebenza" -- cgit v1.2.3 From a2cc624e75e41f1439d738ea6cb69802dc9652be Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 14 Jan 2015 14:23:02 -0800 Subject: Changing DB upgrade path to use swith case statement. > This allows to cancel the whole upgrade path if any one step fails and reset the DB Change-Id: I645b74af8afcd21dda0d619691a5fb3dbf1a04c0 --- src/com/android/launcher3/LauncherProvider.java | 190 +++++++++++------------- 1 file changed, 86 insertions(+), 104 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index a9ad59652..d7058a18a 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -56,7 +56,6 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int MIN_DATABASE_VERSION = 12; private static final int DATABASE_VERSION = 21; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; @@ -489,117 +488,100 @@ public class LauncherProvider extends ContentProvider { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion); - - int version = oldVersion; - if (version < MIN_DATABASE_VERSION) { - // The version cannot be lower that this, as Launcher3 never supported a lower + switch (oldVersion) { + // The version cannot be lower that 12, as Launcher3 never supported a lower // version of the DB. - createEmptyDB(db); - version = DATABASE_VERSION; - } - - if (version < 13) { - // With the new shrink-wrapped and re-orderable workspaces, it makes sense - // to persist workspace screens and their relative order. - mMaxScreenId = 0; - - addWorkspacesTable(db); - version = 13; - } - - if (version < 14) { - db.beginTransaction(); - try { - // Insert new column for holding widget provider name - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN appWidgetProvider TEXT;"); - db.setTransactionSuccessful(); - version = 14; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 12: { + // With the new shrink-wrapped and re-orderable workspaces, it makes sense + // to persist workspace screens and their relative order. + mMaxScreenId = 0; + addWorkspacesTable(db); } - } - - if (version < 15) { - db.beginTransaction(); - try { - // Insert new column for holding update timestamp - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.execSQL("ALTER TABLE workspaceScreens " + - "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - version = 15; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 13: { + db.beginTransaction(); + try { + // Insert new column for holding widget provider name + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN appWidgetProvider TEXT;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - } - - - if (version < 16) { - db.beginTransaction(); - try { - // Insert new column for holding restore status - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - version = 16; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 14: { + db.beginTransaction(); + try { + // Insert new column for holding update timestamp + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); + db.execSQL("ALTER TABLE workspaceScreens " + + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - } - - if (version < 17) { - // We use the db version upgrade here to identify users who may not have seen - // clings yet (because they weren't available), but for whom the clings are now - // available (tablet users). Because one of the possible cling flows (migration) - // is very destructive (wipes out workspaces), we want to prevent this from showing - // until clear data. We do so by marking that the clings have been shown. - LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext); - version = 17; - } - - if (version < 18) { - // No-op - version = 18; - } - - if (version < 19) { - // Due to a data loss bug, some users may have items associated with screen ids - // which no longer exist. Since this can cause other problems, and since the user - // will never see these items anyway, we use database upgrade as an opportunity to - // clean things up. - removeOrphanedItems(db); - version = 19; - } - - if (version < 20) { - // Add userId column - if (addProfileColumn(db)) { - version = 20; + case 15: { + db.beginTransaction(); + try { + // Insert new column for holding restore status + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - // else old version remains, which means we wipe old data - } - - if (version < 21) { - if (updateFolderItemsRank(db, true)) { - version = 21; + case 16: { + // We use the db version upgrade here to identify users who may not have seen + // clings yet (because they weren't available), but for whom the clings are now + // available (tablet users). Because one of the possible cling flows (migration) + // is very destructive (wipes out workspaces), we want to prevent this from showing + // until clear data. We do so by marking that the clings have been shown. + LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext); + } + case 17: { + // No-op + } + case 18: { + // Due to a data loss bug, some users may have items associated with screen ids + // which no longer exist. Since this can cause other problems, and since the user + // will never see these items anyway, we use database upgrade as an opportunity to + // clean things up. + removeOrphanedItems(db); + } + case 19: { + // Add userId column + if (!addProfileColumn(db)) { + // Old version remains, which means we wipe old data + break; + } + } + case 20: + if (!updateFolderItemsRank(db, true)) { + break; + } + case 21: { + // DB Upgraded successfully + return; } } - if (version != DATABASE_VERSION) { - Log.w(TAG, "Destroying all old data."); - createEmptyDB(db); - } + // DB was not upgraded + Log.w(TAG, "Destroying all old data."); + createEmptyDB(db); } @Override -- cgit v1.2.3 From fde11852cf0f91c4500a93dfffb44c1b86991e6e Mon Sep 17 00:00:00 2001 From: Nilesh Agrawal Date: Wed, 21 Jan 2015 11:50:57 -0800 Subject: Add strict mode exception in LauncherProvider. Content providers are created during process startup (before Application.onCreate), and we should not be doing expensive stuff in Provider.onCreate Adding an exception for now as it looks like LauncherProvider initialization is needed before we can show meaningful UI. Bug: 19094644 Change-Id: I860c2934c110d3a43f1a3afa00729077dc64796c --- src/com/android/launcher3/LauncherProvider.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index a9ad59652..4bdbdf407 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -36,6 +36,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; +import android.os.StrictMode; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -85,7 +86,9 @@ public class LauncherProvider extends ContentProvider { @Override public boolean onCreate() { final Context context = getContext(); + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); mOpenHelper = new DatabaseHelper(context); + StrictMode.setThreadPolicy(oldPolicy); LauncherAppState.setLauncherProvider(this); return true; } -- cgit v1.2.3 From bb3b02f562bef4de063099c085d3199a77d06cfd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 15 Jan 2015 12:00:14 -0800 Subject: Replacing hotseat icon to an appropriate system app > During backupi, store the hotseat target app type, based on some predefined common system apps > During restore, save this app type in the restore flag, if it is a hotseat app > During first launcher load, if an app is not being restored, try to replace it with an appropriate replacement for that type, otherwise delete it. Bug: 18764649 Change-Id: Ic49e40bd707bd8d7de18bbab8b1e58a0a36426a2 --- protos/backup.proto | 12 ++ res/xml/app_target_browser.xml | 23 ++++ res/xml/app_target_camera.xml | 23 ++++ res/xml/app_target_email.xml | 23 ++++ res/xml/app_target_gallery.xml | 23 ++++ res/xml/app_target_messenger.xml | 26 ++++ res/xml/app_target_phone.xml | 24 ++++ src/com/android/launcher3/AutoInstallsLayout.java | 14 +- src/com/android/launcher3/CommonAppTypeParser.java | 152 +++++++++++++++++++++ src/com/android/launcher3/DefaultLayoutParser.java | 15 +- .../android/launcher3/LauncherBackupHelper.java | 117 +++++++++++++++- src/com/android/launcher3/LauncherModel.java | 56 ++++++-- src/com/android/launcher3/ShortcutInfo.java | 12 +- 13 files changed, 492 insertions(+), 28 deletions(-) create mode 100644 res/xml/app_target_browser.xml create mode 100644 res/xml/app_target_camera.xml create mode 100644 res/xml/app_target_email.xml create mode 100644 res/xml/app_target_gallery.xml create mode 100644 res/xml/app_target_messenger.xml create mode 100644 res/xml/app_target_phone.xml create mode 100644 src/com/android/launcher3/CommonAppTypeParser.java diff --git a/protos/backup.proto b/protos/backup.proto index 44c4b09e3..09330ee06 100644 --- a/protos/backup.proto +++ b/protos/backup.proto @@ -72,6 +72,17 @@ message Journal { } message Favorite { + // Type of the app, this target represents + enum TargetType { + TARGET_NONE = 0; + TARGET_PHONE = 1; + TARGET_MESSENGER = 2; + TARGET_EMAIL = 3; + TARGET_BROWSER = 4; + TARGET_GALLERY = 5; + TARGET_CAMERA = 6; + } + required int64 id = 1; required int32 itemType = 2; optional string title = 3; @@ -90,6 +101,7 @@ message Favorite { optional string iconPackage = 16; optional string iconResource = 17; optional bytes icon = 18; + optional TargetType targetType = 19 [default = TARGET_NONE]; } message Screen { diff --git a/res/xml/app_target_browser.xml b/res/xml/app_target_browser.xml new file mode 100644 index 000000000..d7c3ed5fd --- /dev/null +++ b/res/xml/app_target_browser.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/xml/app_target_camera.xml b/res/xml/app_target_camera.xml new file mode 100644 index 000000000..f65a2b168 --- /dev/null +++ b/res/xml/app_target_camera.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/xml/app_target_email.xml b/res/xml/app_target_email.xml new file mode 100644 index 000000000..44f0a407d --- /dev/null +++ b/res/xml/app_target_email.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/xml/app_target_gallery.xml b/res/xml/app_target_gallery.xml new file mode 100644 index 000000000..c9d34924a --- /dev/null +++ b/res/xml/app_target_gallery.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/xml/app_target_messenger.xml b/res/xml/app_target_messenger.xml new file mode 100644 index 000000000..278eb5ca8 --- /dev/null +++ b/res/xml/app_target_messenger.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/app_target_phone.xml b/res/xml/app_target_phone.xml new file mode 100644 index 000000000..5d6ca316e --- /dev/null +++ b/res/xml/app_target_phone.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index a5d22286d..0fa4cbb75 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -112,7 +112,7 @@ public class AutoInstallsLayout { private final Context mContext; private final AppWidgetHost mAppWidgetHost; - private final LayoutParserCallback mCallback; + protected final LayoutParserCallback mCallback; protected final PackageManager mPackageManager; protected final Resources mSourceRes; @@ -122,13 +122,20 @@ public class AutoInstallsLayout { private final long[] mTemp = new long[2]; private final ContentValues mValues; - private final String mRootTag; + protected final String mRootTag; protected SQLiteDatabase mDb; public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback, Resources res, int layoutId, String rootTag) { + this(context, appWidgetHost, callback, res, layoutId, rootTag, + LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().hotseatAllAppsRank); + } + + public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost, + LayoutParserCallback callback, Resources res, + int layoutId, String rootTag, int hotseatAllAppsRank) { mContext = context; mAppWidgetHost = appWidgetHost; mCallback = callback; @@ -139,8 +146,7 @@ public class AutoInstallsLayout { mSourceRes = res; mLayoutId = layoutId; - mHotseatAllAppsRank = LauncherAppState.getInstance() - .getDynamicGrid().getDeviceProfile().hotseatAllAppsRank; + mHotseatAllAppsRank = hotseatAllAppsRank; } /** diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java new file mode 100644 index 000000000..fe2fbd75f --- /dev/null +++ b/src/com/android/launcher3/CommonAppTypeParser.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2008 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.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.res.XmlResourceParser; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.backup.BackupProtos.Favorite; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * A class that parses content values corresponding to some common app types. + */ +public class CommonAppTypeParser implements LayoutParserCallback { + private static final String TAG = "CommonAppTypeParser"; + + // Including TARGET_NONE + public static final int SUPPORTED_TYPE_COUNT = 7; + + private static final int RESTORE_FLAG_BIT_SHIFT = 4; + + + private final long mItemId; + private final int mResId; + private final Context mContext; + + ContentValues parsedValues; + Intent parsedIntent; + String parsedTitle; + + public CommonAppTypeParser(long itemId, int itemType, Context context) { + mItemId = itemId; + mContext = context; + mResId = getResourceForItemType(itemType); + } + + @Override + public long generateNewItemId() { + return mItemId; + } + + @Override + public long insertAndCheck(SQLiteDatabase db, ContentValues values) { + parsedValues = values; + + // Remove unwanted values + values.put(Favorites.ICON_TYPE, (Integer) null); + values.put(Favorites.ICON_PACKAGE, (String) null); + values.put(Favorites.ICON_RESOURCE, (String) null); + values.put(Favorites.ICON, (byte[]) null); + return 1; + } + + /** + * Tries to find a suitable app to the provided app type. + */ + public boolean findDefaultApp() { + if (mResId == 0) { + return false; + } + + parsedIntent = null; + parsedValues = null; + new MyLayoutParser().parseValues(); + return (parsedValues != null) && (parsedIntent != null); + } + + private class MyLayoutParser extends DefaultLayoutParser { + + public MyLayoutParser() { + super(mContext, null, CommonAppTypeParser.this, + mContext.getResources(), mResId, TAG_RESOLVE, 0); + } + + @Override + protected long addShortcut(String title, Intent intent, int type) { + if (type == Favorites.ITEM_TYPE_APPLICATION) { + parsedIntent = intent; + parsedTitle = title; + } + return super.addShortcut(title, intent, type); + } + + public void parseValues() { + XmlResourceParser parser = mSourceRes.getXml(mLayoutId); + try { + beginDocument(parser, mRootTag); + new ResolveParser().parseAndAdd(parser); + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Unable to parse default app info", e); + } + parser.close(); + } + } + + public static int getResourceForItemType(int type) { + switch (type) { + case Favorite.TARGET_PHONE: + return R.xml.app_target_phone; + + case Favorite.TARGET_MESSENGER: + return R.xml.app_target_messenger; + + case Favorite.TARGET_EMAIL: + return R.xml.app_target_email; + + case Favorite.TARGET_BROWSER: + return R.xml.app_target_browser; + + case Favorite.TARGET_GALLERY: + return R.xml.app_target_gallery; + + case Favorite.TARGET_CAMERA: + return R.xml.app_target_camera; + + default: + return 0; + } + } + + public static int encodeItemTypeToFlag(int itemType) { + return itemType << RESTORE_FLAG_BIT_SHIFT; + } + + public static int decodeItemTypeFromFlag(int flag) { + return (flag & ShortcutInfo.FLAG_RESTORED_APP_TYPE) >> RESTORE_FLAG_BIT_SHIFT; + } + +} diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index e3ea40ebb..48372388a 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -29,16 +29,16 @@ import java.util.List; public class DefaultLayoutParser extends AutoInstallsLayout { private static final String TAG = "DefaultLayoutParser"; - private static final String TAG_RESOLVE = "resolve"; + protected static final String TAG_RESOLVE = "resolve"; private static final String TAG_FAVORITES = "favorites"; - private static final String TAG_FAVORITE = "favorite"; + protected static final String TAG_FAVORITE = "favorite"; private static final String TAG_APPWIDGET = "appwidget"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_FOLDER = "folder"; private static final String TAG_PARTNER_FOLDER = "partner-folder"; private static final String TAG_INCLUDE = "include"; - private static final String ATTR_URI = "uri"; + protected static final String ATTR_URI = "uri"; private static final String ATTR_WORKSPACE = "workspace"; private static final String ATTR_CONTAINER = "container"; private static final String ATTR_SCREEN = "screen"; @@ -47,7 +47,12 @@ public class DefaultLayoutParser extends AutoInstallsLayout { public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback, Resources sourceRes, int layoutId) { super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES); - Log.e(TAG, "Default layout parser initialized"); + } + + public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, + LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag, + int hotseatAllAppsRank) { + super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag, hotseatAllAppsRank); } @Override @@ -218,7 +223,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * Contains a list of nodes, and accepts the first successfully parsed node. */ - private class ResolveParser implements TagParser { + protected class ResolveParser implements TagParser { private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser(); diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 32bea5baa..353bf3f00 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -19,13 +19,16 @@ import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; import android.app.backup.BackupHelper; import android.app.backup.BackupManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.XmlResourceParser; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -51,6 +54,9 @@ import com.android.launcher3.compat.UserManagerCompat; import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import com.google.protobuf.nano.MessageNano; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -58,7 +64,6 @@ import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.zip.CRC32; @@ -138,6 +143,7 @@ public class LauncherBackupHelper implements BackupHelper { private final Context mContext; private final HashSet mExistingKeys; private final ArrayList mKeys; + private final ItemTypeMatcher[] mItemTypeMatchers; private IconCache mIconCache; private BackupManager mBackupManager; @@ -154,6 +160,7 @@ public class LauncherBackupHelper implements BackupHelper { mExistingKeys = new HashSet(); mKeys = new ArrayList(); restoreSuccessful = true; + mItemTypeMatchers = new ItemTypeMatcher[CommonAppTypeParser.SUPPORTED_TYPE_COUNT]; } private void dataChanged() { @@ -753,6 +760,17 @@ public class LauncherBackupHelper implements BackupHelper { return checksum.getValue(); } + /** + * @return true if its an hotseat item, that can be replaced during restore. + * TODO: Extend check for folders in hotseat. + */ + private boolean isReplaceableHotseatItem(Favorite favorite) { + return favorite.container == Favorites.CONTAINER_HOTSEAT + && favorite.intent != null + && (favorite.itemType == Favorites.ITEM_TYPE_APPLICATION + || favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT); + } + /** Serialize a Favorite for persistence, including a checksum wrapper. */ private Favorite packFavorite(Cursor c) { Favorite favorite = new Favorite(); @@ -785,9 +803,10 @@ public class LauncherBackupHelper implements BackupHelper { favorite.title = title; } String intentDescription = c.getString(INTENT_INDEX); + Intent intent = null; if (!TextUtils.isEmpty(intentDescription)) { try { - Intent intent = Intent.parseUri(intentDescription, 0); + intent = Intent.parseUri(intentDescription, 0); intent.removeExtra(ItemInfo.EXTRA_PROFILE); favorite.intent = intent.toUri(0); } catch (URISyntaxException e) { @@ -803,6 +822,31 @@ public class LauncherBackupHelper implements BackupHelper { } } + if (isReplaceableHotseatItem(favorite)) { + if (intent != null && intent.getComponent() != null) { + PackageManager pm = mContext.getPackageManager(); + ActivityInfo activity = null;; + try { + activity = pm.getActivityInfo(intent.getComponent(), 0); + } catch (NameNotFoundException e) { + Log.e(TAG, "Target not found", e); + } + if (activity == null) { + return favorite; + } + for (int i = 0; i < mItemTypeMatchers.length; i++) { + if (mItemTypeMatchers[i] == null) { + mItemTypeMatchers[i] = new ItemTypeMatcher( + CommonAppTypeParser.getResourceForItemType(i)); + } + if (mItemTypeMatchers[i].matches(activity, pm)) { + favorite.targetType = i; + break; + } + } + } + } + return favorite; } @@ -810,6 +854,7 @@ public class LauncherBackupHelper implements BackupHelper { private ContentValues unpackFavorite(byte[] buffer, int dataSize) throws IOException { Favorite favorite = unpackProto(new Favorite(), buffer, dataSize); + ContentValues values = new ContentValues(); values.put(Favorites._ID, favorite.id); values.put(Favorites.SCREEN, favorite.screen); @@ -860,8 +905,17 @@ public class LauncherBackupHelper implements BackupHelper { throw new InvalidBackupException("Widget not in screen bounds, aborting restore"); } } else { - // Let LauncherModel know we've been here. - values.put(LauncherSettings.Favorites.RESTORED, 1); + // Check if it is an hotseat item, that can be replaced. + if (isReplaceableHotseatItem(favorite) + && favorite.targetType != Favorite.TARGET_NONE + && favorite.targetType < CommonAppTypeParser.SUPPORTED_TYPE_COUNT) { + Log.e(TAG, "Added item type flag"); + values.put(LauncherSettings.Favorites.RESTORED, + 1 | CommonAppTypeParser.encodeItemTypeToFlag(favorite.targetType)); + } else { + // Let LauncherModel know we've been here. + values.put(LauncherSettings.Favorites.RESTORED, 1); + } // Verify placement if (favorite.container == Favorites.CONTAINER_HOTSEAT) { @@ -1128,6 +1182,9 @@ public class LauncherBackupHelper implements BackupHelper { } private class InvalidBackupException extends IOException { + + private static final long serialVersionUID = 8931456637211665082L; + private InvalidBackupException(Throwable cause) { super(cause); } @@ -1136,4 +1193,54 @@ public class LauncherBackupHelper implements BackupHelper { super(reason); } } + + /** + * A class to check if an activity can handle one of the intents from a list of + * predefined intents. + */ + private class ItemTypeMatcher { + + private final ArrayList mIntents; + + ItemTypeMatcher(int xml_res) { + mIntents = xml_res == 0 ? new ArrayList() : parseIntents(xml_res); + } + + private ArrayList parseIntents(int xml_res) { + ArrayList intents = new ArrayList(); + XmlResourceParser parser = mContext.getResources().getXml(xml_res); + try { + DefaultLayoutParser.beginDocument(parser, DefaultLayoutParser.TAG_RESOLVE); + final int depth = parser.getDepth(); + int type; + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if (type != XmlPullParser.START_TAG) { + continue; + } else if (DefaultLayoutParser.TAG_FAVORITE.equals(parser.getName())) { + final String uri = DefaultLayoutParser.getAttributeValue( + parser, DefaultLayoutParser.ATTR_URI); + intents.add(Intent.parseUri(uri, 0)); + } + } + } catch (URISyntaxException | XmlPullParserException | IOException e) { + Log.e(TAG, "Unable to parse " + xml_res, e); + } finally { + parser.close(); + } + return intents; + } + + public boolean matches(ActivityInfo activity, PackageManager pm) { + for (Intent intent : mIntents) { + intent.setPackage(activity.packageName); + ResolveInfo info = pm.resolveActivity(intent, 0); + if (info != null && (info.activityInfo.name.equals(activity.name) + || info.activityInfo.name.equals(activity.targetActivity))) { + return true; + } + } + return false; + } + } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 2e879bcec..78790688a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1955,6 +1955,7 @@ public class LauncherModel extends BroadcastReceiver user = mUserManager.getUserForSerialNumber(serialNumber); int promiseType = c.getInt(restoredIndex); int disabledState = 0; + boolean itemReplaced = false; if (user == null) { // User has been deleted remove the item. itemsToRemove.add(id); @@ -1986,9 +1987,7 @@ public class LauncherModel extends BroadcastReceiver ContentValues values = new ContentValues(); values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0)); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); + updateItem(id, values); } } @@ -2018,10 +2017,27 @@ public class LauncherModel extends BroadcastReceiver ContentValues values = new ContentValues(); values.put(LauncherSettings.Favorites.RESTORED, promiseType); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); - + updateItem(id, values); + } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) { + // This is a common app. Try to replace this. + int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType); + CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context); + if (parser.findDefaultApp()) { + // Default app found. Replace it. + intent = parser.parsedIntent; + cn = intent.getComponent(); + ContentValues values = parser.parsedValues; + values.put(LauncherSettings.Favorites.RESTORED, 0); + updateItem(id, values); + restored = false; + itemReplaced = true; + + } else if (REMOVE_UNRESTORED_ICONS) { + Launcher.addDumpLog(TAG, + "Unrestored package removed: " + cn, true); + itemsToRemove.add(id); + continue; + } } else if (REMOVE_UNRESTORED_ICONS) { Launcher.addDumpLog(TAG, "Unrestored package removed: " + cn, true); @@ -2067,7 +2083,16 @@ public class LauncherModel extends BroadcastReceiver continue; } - if (restored) { + if (itemReplaced) { + if (user.equals(UserHandleCompat.myUserHandle())) { + info = getShortcutInfo(manager, intent, user, context, null, + iconIndex, titleIndex, mLabelCache, false); + } else { + // Don't replace items for other profiles. + itemsToRemove.add(id); + continue; + } + } else if (restored) { if (user.equals(UserHandleCompat.myUserHandle())) { Launcher.addDumpLog(TAG, "constructing info for partially restored package", @@ -2301,9 +2326,7 @@ public class LauncherModel extends BroadcastReceiver providerName); values.put(LauncherSettings.Favorites.RESTORED, appWidgetInfo.restoreStatus); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); + updateItem(id, values); } } sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo); @@ -2455,6 +2478,17 @@ public class LauncherModel extends BroadcastReceiver return loadedOldDb; } + /** + * Partially updates the item without any notification. Must be called on the worker thread. + */ + private void updateItem(long itemId, ContentValues update) { + mContext.getContentResolver().update( + LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, + update, + BaseColumns._ID + "= ?", + new String[]{Long.toString(itemId)}); + } + /** Filters the set of items who are directly or indirectly (via another container) on the * specified screen. */ private void filterCurrentWorkspaceItems(long currentScreenId, diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 01f79314e..15d6a3e1c 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -46,18 +46,24 @@ public class ShortcutInfo extends ItemInfo { * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout * parsing. */ - public static final int FLAG_AUTOINTALL_ICON = 2; + public static final int FLAG_AUTOINTALL_ICON = 2; //0B10; /** * The icon is being installed. If {@link FLAG_RESTORED_ICON} or {@link FLAG_AUTOINTALL_ICON} * is set, then the icon is either being installed or is in a broken state. */ - public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; + public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100; /** * Indicates that the widget restore has started. */ - public static final int FLAG_RESTORE_STARTED = 8; + public static final int FLAG_RESTORE_STARTED = 8; //0B1000; + + /** + * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}. + * Upto 15 different types supported. + */ + public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000; /** * The intent used to start the application. -- cgit v1.2.3 From 71701f8145f0eb1017b2d3590fc87babad1d1bd3 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Thu, 22 Jan 2015 19:44:32 -0800 Subject: Add project / classpath to gitignore Change-Id: I157d480ebaa6d3873d03fd547fe4a8e4a4e9e015 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fc1b944b2..4d5667e54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ db_files *.iml +.project +.classpath gen/ tests/stress/gen/ WallpaperPicker/gen/ -- cgit v1.2.3 From b564efb0244adf0fb8429940f9f716938c62a7ac Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 23 Jan 2015 13:45:20 -0800 Subject: Adding support for tag in AutoInstall layout Bug: 19121893 Change-Id: I760fa76d6e63c1aa0152e8cd710aba0d6ef4462a --- src/com/android/launcher3/AutoInstallsLayout.java | 15 +++++++++++++++ src/com/android/launcher3/DefaultLayoutParser.java | 22 ---------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index a5d22286d..382066094 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -78,6 +78,7 @@ public class AutoInstallsLayout { } // Object Tags + private static final String TAG_INCLUDE = "include"; private static final String TAG_WORKSPACE = "workspace"; private static final String TAG_APP_ICON = "appicon"; private static final String TAG_AUTO_INSTALL = "autoinstall"; @@ -100,6 +101,9 @@ public class AutoInstallsLayout { private static final String ATTR_ICON = "icon"; private static final String ATTR_URL = "url"; + // Attrs for "Include" + private static final String ATTR_WORKSPACE = "workspace"; + // Style attrs -- "Extra" private static final String ATTR_KEY = "key"; private static final String ATTR_VALUE = "value"; @@ -202,6 +206,17 @@ public class AutoInstallsLayout { HashMap tagParserMap, ArrayList screenIds) throws XmlPullParserException, IOException { + + if (TAG_INCLUDE.equals(parser.getName())) { + final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0); + if (resId != 0) { + // recursively load some more favorites, why not? + return parseLayout(resId, screenIds); + } else { + return 0; + } + } + mValues.clear(); parseContainerAndScreen(parser, mTemp); final long container = mTemp[0]; diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index e3ea40ebb..986ae81f5 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -19,7 +19,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -36,10 +35,8 @@ public class DefaultLayoutParser extends AutoInstallsLayout { private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_FOLDER = "folder"; private static final String TAG_PARTNER_FOLDER = "partner-folder"; - private static final String TAG_INCLUDE = "include"; private static final String ATTR_URI = "uri"; - private static final String ATTR_WORKSPACE = "workspace"; private static final String ATTR_CONTAINER = "container"; private static final String ATTR_SCREEN = "screen"; private static final String ATTR_FOLDER_ITEMS = "folderItems"; @@ -84,25 +81,6 @@ public class DefaultLayoutParser extends AutoInstallsLayout { out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN)); } - @Override - protected int parseAndAddNode( - XmlResourceParser parser, - HashMap tagParserMap, - ArrayList screenIds) - throws XmlPullParserException, IOException { - if (TAG_INCLUDE.equals(parser.getName())) { - final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0); - if (resId != 0) { - // recursively load some more favorites, why not? - return parseLayout(resId, screenIds); - } else { - return 0; - } - } else { - return super.parseAndAddNode(parser, tagParserMap, screenIds); - } - } - /** * AppShortcutParser which also supports adding URI based intents */ -- cgit v1.2.3 From 75beb3b32782488eaf575d1906e58b46749599c3 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 26 Jan 2015 06:24:16 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I3902548749f40061c1a9a51f65c082d0bc0a873e Auto-generated-cl: translation import --- res/values-ne-rNP/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 5e5d25dd6..41ec64596 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -122,5 +122,5 @@ "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" "यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।" - "कार्यक्षेत्रमा जोड्नुहोस्" + "कार्यक्षेत्रमा थप्नुहोस्" -- cgit v1.2.3 From b3e5e4f54125df13863358463f598c224160a13a Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 26 Jan 2015 06:25:08 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I0424587be34af78b7720a9a29d09b7e0ba129790 Auto-generated-cl: translation import --- res/values-ne-rNP/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 5e5d25dd6..41ec64596 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -122,5 +122,5 @@ "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" "यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।" - "कार्यक्षेत्रमा जोड्नुहोस्" + "कार्यक्षेत्रमा थप्नुहोस्" -- cgit v1.2.3 From a258f812fac21767d1f2e61ee7a7a58bfe6da582 Mon Sep 17 00:00:00 2001 From: Nilesh Agrawal Date: Mon, 26 Jan 2015 14:07:29 -0800 Subject: Modify strict mode thread policy for LauncherProvider. Allowing thread writes too as SQLiteOpenHelper.getWritableDatabase needs it. Bug: 19094644 Change-Id: I39fe97a9e7fc07c38a4f8e5c5979196b742e36bf --- src/com/android/launcher3/LauncherProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 4bdbdf407..bb6e8c8af 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -86,7 +86,7 @@ public class LauncherProvider extends ContentProvider { @Override public boolean onCreate() { final Context context = getContext(); - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); mOpenHelper = new DatabaseHelper(context); StrictMode.setThreadPolicy(oldPolicy); LauncherAppState.setLauncherProvider(this); -- cgit v1.2.3 From b76c165aadb4deb144ec3b1267aa1faf304638e9 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 30 Jan 2015 10:28:23 -0800 Subject: Fix ClassCastException due to typo issue 19159790 Change-Id: I5f9fa0e3672c10a460606dcb13b158bd4e614813 --- src/com/android/launcher3/LauncherModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7b5f8466a..9f976318d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3859,7 +3859,7 @@ public class LauncherModel extends BroadcastReceiver labelB = mLabelCache.get(b); } else { labelB = (b instanceof LauncherAppWidgetProviderInfo) - ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a) + ? mManager.loadLabel((LauncherAppWidgetProviderInfo) b) : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim(); mLabelCache.put(b, labelB); } -- cgit v1.2.3 From 42f3b9e0283c30c40c286f9958ce0f8b02f85c52 Mon Sep 17 00:00:00 2001 From: Robert Kozikowski Date: Wed, 4 Feb 2015 14:59:39 +0000 Subject: Adding Launcher3 callbacks for trimming memory. It will help reduce memory footprint of Launcher, when in background. It is required to help deallocate images, when Google Now Launcher goes into the background. See cl/83222937/ . Change-Id: I6d3d4d1f0457c67abcad3ba4516c87abcf666b05 --- src/com/android/launcher3/Launcher.java | 3 +++ src/com/android/launcher3/LauncherCallbacks.java | 1 + src/com/android/launcher3/LauncherExtension.java | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5d8e136cd..591eb6474 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3791,6 +3791,9 @@ public class Launcher extends Activity if (mAppsCustomizeTabHost != null) { mAppsCustomizeTabHost.trimMemory(); } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onTrimMemory(level); + } } } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index a1f4e0b90..d8128d6e5 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -42,6 +42,7 @@ public interface LauncherCallbacks { public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args); public void onHomeIntent(); public boolean handleBackPressed(); + public void onTrimMemory(int level); /* * Extension points for providing custom behavior on certain user interactions. diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index b264042cf..fe9bd6c23 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -107,6 +107,10 @@ public class LauncherExtension extends Launcher { return false; } + @Override + public void onTrimMemory(int level) { + } + @Override public void onLauncherProviderChange() { } -- cgit v1.2.3 From 4802b4107a66bc86996083b4835d1094ef4f4cbb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 4 Feb 2015 10:05:05 -0800 Subject: Adding null-check before parsing an intent Bug: 18962665 Change-Id: Iae02435f019fa205f3ee3ae721a29f26b3b56dd2 --- src/com/android/launcher3/UninstallShortcutReceiver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/UninstallShortcutReceiver.java b/src/com/android/launcher3/UninstallShortcutReceiver.java index ccea4ec0c..c9d0bb5f5 100644 --- a/src/com/android/launcher3/UninstallShortcutReceiver.java +++ b/src/com/android/launcher3/UninstallShortcutReceiver.java @@ -104,7 +104,9 @@ public class UninstallShortcutReceiver extends BroadcastReceiver { try { while (c.moveToNext()) { try { - if (intent.filterEquals(Intent.parseUri(c.getString(intentIndex), 0))) { + String intentStr = c.getString(intentIndex); + if (intentStr != null + && intent.filterEquals(Intent.parseUri(intentStr, 0))) { final long id = c.getLong(idIndex); final Uri uri = LauncherSettings.Favorites.getContentUri(id, false); cr.delete(uri, null, null); -- cgit v1.2.3 From e2d4ee1b21014e0f644134bd7c56291078b1ed60 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 6 Feb 2015 23:28:49 +0000 Subject: Revert "Adding Launcher3 callbacks for trimming memory." This reverts commit 42f3b9e0283c30c40c286f9958ce0f8b02f85c52. Change-Id: I65a7c893576c0901b231bab9abe6937f99415f1e --- src/com/android/launcher3/Launcher.java | 3 --- src/com/android/launcher3/LauncherCallbacks.java | 1 - src/com/android/launcher3/LauncherExtension.java | 4 ---- 3 files changed, 8 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 591eb6474..5d8e136cd 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3791,9 +3791,6 @@ public class Launcher extends Activity if (mAppsCustomizeTabHost != null) { mAppsCustomizeTabHost.trimMemory(); } - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onTrimMemory(level); - } } } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index d8128d6e5..a1f4e0b90 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -42,7 +42,6 @@ public interface LauncherCallbacks { public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args); public void onHomeIntent(); public boolean handleBackPressed(); - public void onTrimMemory(int level); /* * Extension points for providing custom behavior on certain user interactions. diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index fe9bd6c23..b264042cf 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -107,10 +107,6 @@ public class LauncherExtension extends Launcher { return false; } - @Override - public void onTrimMemory(int level) { - } - @Override public void onLauncherProviderChange() { } -- cgit v1.2.3 From 8f9a787945c3326791c61b001e786b96e9fc4341 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 16 Jan 2015 16:14:29 -0800 Subject: Launcher crashes on widget bind permission prompt Change-Id: If09feb357e1604e5ee1a66305b022674f466833e (cherry picked from commit e6b63a3d7335fed5b7bbcc6d2c6230ae28facd3e) --- src/com/android/launcher3/Launcher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5d8e136cd..84476b7cc 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -757,7 +757,7 @@ public class Launcher extends Activity mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_OK) { - addAppWidgetImpl(appWidgetId, (PendingAddWidgetInfo) mPendingAddInfo, null, + addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); } return; @@ -2243,12 +2243,12 @@ public class Launcher extends Activity mPendingAddInfo.dropPos = null; } - void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, final + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) { addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0); } - void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo, int delay) { if (appWidgetInfo.configure != null) { -- cgit v1.2.3 From 3c1865ad5050f594d1684fe8962bfbc8ffcbe4bb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Feb 2015 14:28:44 -0800 Subject: Using SCREEN_ORIENTATION_LOCKED for locking to current orientation. Bug: 17298128 Change-Id: If64b6957a594bcc48f6454689d11cd63d31b9239 --- src/com/android/launcher3/Launcher.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 61915b755..4e3090122 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4779,8 +4779,12 @@ public class Launcher extends Activity public void lockScreenOrientation() { if (Utilities.isRotationEnabled(this)) { - setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() - .getConfiguration().orientation)); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() + .getConfiguration().orientation)); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } } } public void unlockScreenOrientation(boolean immediate) { -- cgit v1.2.3 From c87775d8e63c7b5722c23ef95c782f574b847d73 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Feb 2015 19:52:36 -0800 Subject: Fixing rank migration code to handle null values correctly. Bug: 19297508 Change-Id: I652ef30ab37f5b09204f0388de8e065d64b7724d --- src/com/android/launcher3/LauncherProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index bb6e8c8af..0088f26cf 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -638,7 +638,8 @@ public class LauncherProvider extends ContentProvider { new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}); while (c.moveToNext()) { - db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE container=?;", + db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE " + + "container=? AND cellX IS NOT NULL AND cellY IS NOT NULL;", new Object[] {c.getLong(1) + 1, c.getLong(0)}); } -- cgit v1.2.3 From 54d7d0549892e6ab2bbca6e34454a42135426a58 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 11 Feb 2015 11:53:02 -0800 Subject: Use ResolveInfo for label and icon for LauncherActivityInfo Instead of using the ActivityInfo, use the ResolveInfo so that any label or icon specified on the intent-filter is used. Bug: 18482039 Change-Id: I87c0d9dba0754e3aa2c81727a9f60d789e5e8630 --- .../compat/LauncherActivityInfoCompatV16.java | 45 +++++++++++----------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java index 1d41a6ff6..ea51aace8 100644 --- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java +++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java @@ -29,13 +29,15 @@ import android.graphics.drawable.Drawable; public class LauncherActivityInfoCompatV16 extends LauncherActivityInfoCompat { - private ActivityInfo mActivityInfo; - private ComponentName mComponentName; - private PackageManager mPm; + private final ResolveInfo mResolveInfo; + private final ActivityInfo mActivityInfo; + private final ComponentName mComponentName; + private final PackageManager mPm; LauncherActivityInfoCompatV16(Context context, ResolveInfo info) { super(); - this.mActivityInfo = info.activityInfo; + mResolveInfo = info; + mActivityInfo = info.activityInfo; mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name); mPm = context.getPackageManager(); } @@ -49,31 +51,30 @@ public class LauncherActivityInfoCompatV16 extends LauncherActivityInfoCompat { } public CharSequence getLabel() { - return mActivityInfo.loadLabel(mPm); + return mResolveInfo.loadLabel(mPm); } public Drawable getIcon(int density) { - Drawable d = null; - if (mActivityInfo.getIconResource() != 0) { - Resources resources; + int iconRes = mResolveInfo.getIconResource(); + Resources resources = null; + Drawable icon = null; + // Get the preferred density icon from the app's resources + if (density != 0 && iconRes != 0) { try { - resources = mPm.getResourcesForApplication(mActivityInfo.packageName); - } catch (PackageManager.NameNotFoundException e) { - resources = null; - } - if (resources != null) { - try { - d = resources.getDrawableForDensity(mActivityInfo.getIconResource(), density); - } catch (Resources.NotFoundException e) { - // Return default icon below. - } + resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); + icon = resources.getDrawableForDensity(iconRes, density); + } catch (NameNotFoundException | Resources.NotFoundException exc) { } } - if (d == null) { - Resources resources = Resources.getSystem(); - d = resources.getDrawableForDensity(android.R.mipmap.sym_def_app_icon, density); + // Get the default density icon + if (icon == null) { + icon = mResolveInfo.loadIcon(mPm); + } + if (icon == null) { + resources = Resources.getSystem(); + icon = resources.getDrawableForDensity(android.R.mipmap.sym_def_app_icon, density); } - return d; + return icon; } public ApplicationInfo getApplicationInfo() { -- cgit v1.2.3 From 66cfdc2549b973496f2d5555defc22b7a15b5620 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Feb 2015 13:01:51 -0800 Subject: Removing code for the unused 'upgrade path' Change-Id: Ib9eca92284b0eabbe36ffaaf26c62a6ce3cfd3e9 --- src/com/android/launcher3/Hotseat.java | 35 -------- src/com/android/launcher3/Launcher.java | 21 +---- src/com/android/launcher3/LauncherModel.java | 111 +++++++----------------- src/com/android/launcher3/LauncherProvider.java | 23 +---- src/com/android/launcher3/Workspace.java | 83 ------------------ 5 files changed, 36 insertions(+), 237 deletions(-) diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index b08272f36..289b08b14 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -16,14 +16,12 @@ package com.android.launcher3; -import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -33,7 +31,6 @@ import android.widget.TextView; import java.util.ArrayList; public class Hotseat extends FrameLayout { - private static final String TAG = "Hotseat"; private CellLayout mContent; @@ -182,38 +179,6 @@ public class Hotseat extends FrameLayout { return false; } - void addAllAppsFolder(IconCache iconCache, - ArrayList allApps, ArrayList onWorkspace, - Launcher launcher, Workspace workspace) { - if (LauncherAppState.isDisableAllApps()) { - FolderInfo fi = new FolderInfo(); - - fi.cellX = getCellXFromOrder(mAllAppsButtonRank); - fi.cellY = getCellYFromOrder(mAllAppsButtonRank); - fi.spanX = 1; - fi.spanY = 1; - fi.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT; - fi.screenId = mAllAppsButtonRank; - fi.itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; - fi.title = "More Apps"; - LauncherModel.addItemToDatabase(launcher, fi, fi.container, fi.screenId, fi.cellX, - fi.cellY, false); - FolderIcon folder = FolderIcon.fromXml(R.layout.folder_icon, launcher, - getLayout(), fi, iconCache); - workspace.addInScreen(folder, fi.container, fi.screenId, fi.cellX, fi.cellY, - fi.spanX, fi.spanY); - - for (AppInfo info: allApps) { - ComponentName cn = info.intent.getComponent(); - if (!onWorkspace.contains(cn)) { - Log.d(TAG, "Adding to 'more apps': " + info.intent); - ShortcutInfo si = info.makeShortcut(); - fi.add(si); - } - } - } - } - void addAppsToAllAppsFolder(ArrayList apps) { if (LauncherAppState.isDisableAllApps()) { View v = mContent.getChildAt(getCellXFromOrder(mAllAppsButtonRank), getCellYFromOrder(mAllAppsButtonRank)); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 61915b755..adea29e76 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -227,8 +227,6 @@ public class Launcher extends Activity private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; private static final int ACTIVITY_START_DELAY = 1000; - private static final Object sLock = new Object(); - private HashMap mItemIdToViewId = new HashMap(); private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); @@ -335,8 +333,6 @@ public class Launcher extends Activity // it from the context. private SharedPreferences mSharedPrefs; - private static ArrayList mIntentsOnWorkspaceFromUpgradePath = null; - // Holds the page that we need to animate to, and the icon views that we need to animate up // when we scroll to that page on resume. private ImageView mFolderIconImageView; @@ -4451,10 +4447,10 @@ public class Launcher extends Activity * * Implementation of the method from LauncherModel.Callbacks. */ - public void finishBindingItems(final boolean upgradePath) { + public void finishBindingItems() { Runnable r = new Runnable() { public void run() { - finishBindingItems(upgradePath); + finishBindingItems(); } }; if (waitUntilResume(r)) { @@ -4489,14 +4485,10 @@ public class Launcher extends Activity sPendingAddItem = null; } - if (upgradePath) { - mWorkspace.getUniqueComponents(true, null); - mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null); - } PackageInstallerCompat.getInstance(this).onFinishBind(); if (mLauncherCallbacks != null) { - mLauncherCallbacks.finishBindingItems(upgradePath); + mLauncherCallbacks.finishBindingItems(false); } } @@ -4563,13 +4555,6 @@ public class Launcher extends Activity */ public void bindAllApplications(final ArrayList apps) { if (LauncherAppState.isDisableAllApps()) { - if (mIntentsOnWorkspaceFromUpgradePath != null) { - if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) { - getHotseat().addAllAppsFolder(mIconCache, apps, - mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace); - } - mIntentsOnWorkspaceFromUpgradePath = null; - } if (mAppsCustomizeContent != null) { mAppsCustomizeContent.onPackagesUpdated( LauncherModel.getSortedWidgetsAndShortcuts(this)); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f3d94dbd9..3983835dc 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -90,9 +90,6 @@ public class LauncherModel extends BroadcastReceiver static final String TAG = "Launcher.Model"; - // true = use a "More Apps" folder for non-workspace apps on upgrade - // false = strew non-workspace apps across the workspace on upgrade - public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false; public static final int LOADER_FLAG_NONE = 0; public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0; public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1; @@ -198,7 +195,7 @@ public class LauncherModel extends BroadcastReceiver public void bindScreens(ArrayList orderedScreenIds); public void bindAddScreens(ArrayList orderedScreenIds); public void bindFolders(HashMap folders); - public void finishBindingItems(boolean upgradePath); + public void finishBindingItems(); public void bindAppWidget(LauncherAppWidgetInfo info); public void bindAllApplications(ArrayList apps); public void bindAppsAdded(ArrayList newScreens, @@ -1501,8 +1498,7 @@ public class LauncherModel extends BroadcastReceiver return mIsLoadingAndBindingWorkspace; } - /** Returns whether this is an upgrade path */ - private boolean loadAndBindWorkspace() { + private void loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true; // Load the workspace @@ -1510,20 +1506,18 @@ public class LauncherModel extends BroadcastReceiver Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); } - boolean isUpgradePath = false; if (!mWorkspaceLoaded) { - isUpgradePath = loadWorkspace(); + loadWorkspace(); synchronized (LoaderTask.this) { if (mStopped) { - return isUpgradePath; + return; } mWorkspaceLoaded = true; } } // Bind the workspace - bindWorkspace(-1, isUpgradePath); - return isUpgradePath; + bindWorkspace(-1); } private void waitForIdle() { @@ -1592,15 +1586,13 @@ public class LauncherModel extends BroadcastReceiver // Divide the set of loaded items into those that we are binding synchronously, and // everything else that is to be bound normally (asynchronously). - bindWorkspace(synchronousBindPage, false); + bindWorkspace(synchronousBindPage); // XXX: For now, continue posting the binding of AllApps as there are other issues that // arise from that. onlyBindAllApps(); } public void run() { - boolean isUpgrade = false; - synchronized (mLock) { mIsLoaderTaskRunning = true; } @@ -1617,7 +1609,7 @@ public class LauncherModel extends BroadcastReceiver ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); - isUpgrade = loadAndBindWorkspace(); + loadAndBindWorkspace(); if (mStopped) { break keep_running; @@ -1655,9 +1647,7 @@ public class LauncherModel extends BroadcastReceiver if (LauncherAppState.isDisableAllApps()) { // Ensure that all the applications that are in the system are // represented on the home screen. - if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) { - verifyApplications(); - } + verifyApplications(); } // Clear out this reference, otherwise we end up holding it until all of the @@ -1834,8 +1824,7 @@ public class LauncherModel extends BroadcastReceiver } } - /** Returns whether this is an upgrade path */ - private boolean loadWorkspace() { + private void loadWorkspace() { // Log to disk Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true); @@ -1869,12 +1858,6 @@ public class LauncherModel extends BroadcastReceiver LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(); } - // This code path is for our old migration code and should no longer be exercised - boolean loadedOldDb = false; - - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true); - synchronized (sBgLock) { clearSBgDataStructures(); final HashSet installingPkgs = PackageInstallerCompat @@ -2347,7 +2330,7 @@ public class LauncherModel extends BroadcastReceiver // Break early if we've stopped loading if (mStopped) { clearSBgDataStructures(); - return false; + return; } if (itemsToRemove.size() > 0) { @@ -2393,60 +2376,29 @@ public class LauncherModel extends BroadcastReceiver null, sWorker); } - if (loadedOldDb) { - long maxScreenId = 0; - // If we're importing we use the old screen order. - for (ItemInfo item: sBgItemsIdMap.values()) { - long screenId = item.screenId; - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - !sBgWorkspaceScreens.contains(screenId)) { - sBgWorkspaceScreens.add(screenId); - if (screenId > maxScreenId) { - maxScreenId = screenId; - } - } - } - Collections.sort(sBgWorkspaceScreens); - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true); - Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + - TextUtils.join(", ", sBgWorkspaceScreens), true); - - LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId); - updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); + sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext)); + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + + TextUtils.join(", ", sBgWorkspaceScreens), true); - // Update the max item id after we load an old db - long maxItemId = 0; - // If we're importing we use the old screen order. - for (ItemInfo item: sBgItemsIdMap.values()) { - maxItemId = Math.max(maxItemId, item.id); - } - LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId); - } else { - sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext)); - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + - TextUtils.join(", ", sBgWorkspaceScreens), true); - - // Remove any empty screens - ArrayList unusedScreens = new ArrayList(sBgWorkspaceScreens); - for (ItemInfo item: sBgItemsIdMap.values()) { - long screenId = item.screenId; - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - unusedScreens.contains(screenId)) { - unusedScreens.remove(screenId); - } + // Remove any empty screens + ArrayList unusedScreens = new ArrayList(sBgWorkspaceScreens); + for (ItemInfo item: sBgItemsIdMap.values()) { + long screenId = item.screenId; + if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && + unusedScreens.contains(screenId)) { + unusedScreens.remove(screenId); } + } - // If there are any empty screens remove them, and update. - if (unusedScreens.size() != 0) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " + - TextUtils.join(", ", unusedScreens), true); + // If there are any empty screens remove them, and update. + if (unusedScreens.size() != 0) { + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " + + TextUtils.join(", ", unusedScreens), true); - sBgWorkspaceScreens.removeAll(unusedScreens); - updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); - } + sBgWorkspaceScreens.removeAll(unusedScreens); + updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); } if (DEBUG_LOADERS) { @@ -2475,7 +2427,6 @@ public class LauncherModel extends BroadcastReceiver } } } - return loadedOldDb; } /** @@ -2683,7 +2634,7 @@ public class LauncherModel extends BroadcastReceiver /** * Binds all loaded data to actual views on the main thread. */ - private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) { + private void bindWorkspace(int synchronizeBindPage) { final long t = SystemClock.uptimeMillis(); Runnable r; @@ -2787,7 +2738,7 @@ public class LauncherModel extends BroadcastReceiver public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { - callbacks.finishBindingItems(isUpgradePath); + callbacks.finishBindingItems(); } // If we're profiling, ensure this is the last thing in the queue. diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 196f57c3c..ebe56011d 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -65,7 +65,6 @@ public class LauncherProvider extends ContentProvider { static final String TABLE_FAVORITES = "favorites"; static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens"; static final String PARAMETER_NOTIFY = "notify"; - static final String UPGRADED_FROM_OLD_DATABASE = "UPGRADED_FROM_OLD_DATABASE"; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; @@ -250,12 +249,6 @@ public class LauncherProvider extends ContentProvider { return mOpenHelper.generateNewScreenId(); } - // This is only required one time while loading the workspace during the - // upgrade path, and should never be called from anywhere else. - public void updateMaxScreenId(long maxScreenId) { - mOpenHelper.updateMaxScreenId(maxScreenId); - } - /** * Clears all the data for a fresh start. */ @@ -473,19 +466,13 @@ public class LauncherProvider extends ContentProvider { private void setFlagJustLoadedOldDb() { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true); - editor.putBoolean(EMPTY_DATABASE_CREATED, false); - editor.commit(); + sp.edit().putBoolean(EMPTY_DATABASE_CREATED, false).commit(); } private void setFlagEmptyDbCreated() { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(EMPTY_DATABASE_CREATED, true); - editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false); - editor.commit(); + sp.edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit(); } @Override @@ -726,12 +713,6 @@ public class LauncherProvider extends ContentProvider { return mMaxScreenId; } - public void updateMaxScreenId(long maxScreenId) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true); - mMaxScreenId = maxScreenId; - } - private long initializeMaxScreenId(SQLiteDatabase db) { Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 66e370b08..56803d960 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -43,7 +43,6 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; @@ -4295,88 +4294,6 @@ public class Workspace extends SmoothPagedView } } - ArrayList getUniqueComponents(boolean stripDuplicates, ArrayList duplicates) { - ArrayList uniqueIntents = new ArrayList(); - getUniqueIntents((CellLayout) mLauncher.getHotseat().getLayout(), uniqueIntents, duplicates, false); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - CellLayout cl = (CellLayout) getChildAt(i); - getUniqueIntents(cl, uniqueIntents, duplicates, false); - } - return uniqueIntents; - } - - void getUniqueIntents(CellLayout cl, ArrayList uniqueIntents, - ArrayList duplicates, boolean stripDuplicates) { - int count = cl.getShortcutsAndWidgets().getChildCount(); - - ArrayList children = new ArrayList(); - for (int i = 0; i < count; i++) { - View v = cl.getShortcutsAndWidgets().getChildAt(i); - children.add(v); - } - - for (int i = 0; i < count; i++) { - View v = children.get(i); - ItemInfo info = (ItemInfo) v.getTag(); - // Null check required as the AllApps button doesn't have an item info - if (info instanceof ShortcutInfo) { - ShortcutInfo si = (ShortcutInfo) info; - ComponentName cn = si.intent.getComponent(); - - Uri dataUri = si.intent.getData(); - // If dataUri is not null / empty or if this component isn't one that would - // have previously showed up in the AllApps list, then this is a widget-type - // shortcut, so ignore it. - if (dataUri != null && !dataUri.equals(Uri.EMPTY)) { - continue; - } - - if (!uniqueIntents.contains(cn)) { - uniqueIntents.add(cn); - } else { - if (stripDuplicates) { - cl.removeViewInLayout(v); - LauncherModel.deleteItemFromDatabase(mLauncher, si); - } - if (duplicates != null) { - duplicates.add(cn); - } - } - } - if (v instanceof FolderIcon) { - FolderIcon fi = (FolderIcon) v; - ArrayList items = fi.getFolder().getItemsInReadingOrder(); - for (int j = 0; j < items.size(); j++) { - if (items.get(j).getTag() instanceof ShortcutInfo) { - ShortcutInfo si = (ShortcutInfo) items.get(j).getTag(); - ComponentName cn = si.intent.getComponent(); - - Uri dataUri = si.intent.getData(); - // If dataUri is not null / empty or if this component isn't one that would - // have previously showed up in the AllApps list, then this is a widget-type - // shortcut, so ignore it. - if (dataUri != null && !dataUri.equals(Uri.EMPTY)) { - continue; - } - - if (!uniqueIntents.contains(cn)) { - uniqueIntents.add(cn); - } else { - if (stripDuplicates) { - fi.getFolderInfo().remove(si); - LauncherModel.deleteItemFromDatabase(mLauncher, si); - } - if (duplicates != null) { - duplicates.add(cn); - } - } - } - } - } - } - } - void saveWorkspaceToDb() { saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout()); int count = getChildCount(); -- cgit v1.2.3 From 09cd92221e407810c2f83ab2305f42bb47197e1b Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 11 Feb 2015 12:57:39 -0800 Subject: Remove meaningless bug id from from addDumpLog and Log.d methods b/11683562 b/6557954 Change-Id: Icd0e237ebb09ac868928328f45a3b30cf19a20df --- src/com/android/launcher3/Workspace.java | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 66e370b08..3758fe59d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -560,10 +560,6 @@ public class Workspace extends SmoothPagedView } public long insertNewWorkspaceScreen(long screenId, int insertIndex) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - insertNewWorkspaceScreen(): " + screenId + - " at index: " + insertIndex, true); - if (mWorkspaceScreens.containsKey(screenId)) { throw new RuntimeException("Screen id " + screenId + " already exists!"); } @@ -663,9 +659,6 @@ public class Workspace extends SmoothPagedView } public void addExtraEmptyScreenOnDrag() { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreenOnDrag()", true); - boolean lastChildOnScreen = false; boolean childOnFinalScreen = false; @@ -692,9 +685,6 @@ public class Workspace extends SmoothPagedView } public boolean addExtraEmptyScreen() { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreen()", true); - if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) { insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID); return true; @@ -703,9 +693,6 @@ public class Workspace extends SmoothPagedView } private void convertFinalScreenToEmptyScreenIfNecessary() { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - convertFinalScreenToEmptyScreenIfNecessary()", true); - if (mLauncher.isWorkspaceLoading()) { // Invalid and dangerous operation if workspace is loading Launcher.addDumpLog(TAG, " - workspace loading, skip", true); @@ -730,7 +717,6 @@ public class Workspace extends SmoothPagedView // Update the model if we have changed any screens mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder); - Launcher.addDumpLog(TAG, "11683562 - extra empty screen: " + finalScreenId, true); } } @@ -740,8 +726,6 @@ public class Workspace extends SmoothPagedView public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete, final int delay, final boolean stripEmptyScreens) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - removeExtraEmptyScreen()", true); if (mLauncher.isWorkspaceLoading()) { // Don't strip empty screens if the workspace is still loading Launcher.addDumpLog(TAG, " - workspace loading, skip", true); @@ -783,9 +767,7 @@ public class Workspace extends SmoothPagedView private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete, final boolean stripEmptyScreens) { - // Log to disk // XXX: Do we need to update LM workspace screens below? - Launcher.addDumpLog(TAG, "11683562 - fadeAndRemoveEmptyScreen()", true); PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f); PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f); @@ -829,8 +811,6 @@ public class Workspace extends SmoothPagedView } public long commitExtraEmptyScreen() { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - commitExtraEmptyScreen()", true); if (mLauncher.isWorkspaceLoading()) { // Invalid and dangerous operation if workspace is loading Launcher.addDumpLog(TAG, " - workspace loading, skip", true); @@ -889,9 +869,6 @@ public class Workspace extends SmoothPagedView } public void stripEmptyScreens() { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - stripEmptyScreens()", true); - if (mLauncher.isWorkspaceLoading()) { // Don't strip empty screens if the workspace is still loading. // This is dangerous and can result in data loss. @@ -919,7 +896,6 @@ public class Workspace extends SmoothPagedView int pageShift = 0; for (Long id: removeScreens) { - Launcher.addDumpLog(TAG, "11683562 - removing id: " + id, true); CellLayout cl = mWorkspaceScreens.get(id); mWorkspaceScreens.remove(id); mScreenOrder.remove(id); @@ -4104,7 +4080,6 @@ public class Workspace extends SmoothPagedView // In the case where we've prebound the widget, we remove it from the DragLayer if (finalView instanceof AppWidgetHostView && external) { - Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView"); mLauncher.getDragLayer().removeView(finalView); } -- cgit v1.2.3 From 6f553b975fba69bd9e5fd58c4db2ba72b67192ee Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 12 Feb 2015 09:46:41 -0800 Subject: Fixing last image thumb, being loaded twice Change-Id: I241e472a3f0869b82066163514fb2c29c0f18a8f --- WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 09e096396..043f6306b 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -500,10 +500,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (lastPhoto != null) { ImageView galleryThumbnailBg = (ImageView) pickImageTile.findViewById(R.id.wallpaper_image); - galleryThumbnailBg.setImageBitmap(getThumbnailOfLastPhoto()); + galleryThumbnailBg.setImageBitmap(lastPhoto); int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray); galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP); - } PickImageInfo pickImageInfo = new PickImageInfo(); -- cgit v1.2.3 From fddef4634cb3b06c85662c24ffa33026d1348d54 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 12 Feb 2015 09:51:48 -0800 Subject: Fixing logic to determine if wallpaper change. > Current logic to determine if livewallpaper changed returns true all the time > Use returnCode passed by the calling activity. Change-Id: Ida800a90451b1f7d368234e1a7e10938dcf2f6f7 --- .../launcher3/LiveWallpaperListAdapter.java | 4 ++-- .../android/launcher3/WallpaperPickerActivity.java | 28 ++-------------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java index 88f4461bf..0a9050cff 100644 --- a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java @@ -122,8 +122,8 @@ public class LiveWallpaperListAdapter extends BaseAdapter implements ListAdapter Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent()); - a.onLiveWallpaperPickerLaunch(mInfo); - a.startActivityForResultSafely(preview, WallpaperPickerActivity.PICK_LIVE_WALLPAPER); + a.startActivityForResultSafely(preview, + WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY); } } diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 09e096396..4ad7b6a6b 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -20,7 +20,6 @@ import android.animation.LayoutTransition; import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; -import android.app.WallpaperInfo; import android.app.WallpaperManager; import android.content.Context; import android.content.Intent; @@ -83,7 +82,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static final int IMAGE_PICK = 5; public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6; - public static final int PICK_LIVE_WALLPAPER = 7; private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES"; private static final String SELECTED_INDEX = "SELECTED_INDEX"; private static final int FLAG_POST_DELAY_MILLIS = 200; @@ -102,9 +100,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { ArrayList mTempWallpaperTiles = new ArrayList(); private SavedWallpaperImages mSavedImages; - private WallpaperInfo mLiveWallpaperInfoOnPickerLaunch; private int mSelectedIndex = -1; - private WallpaperInfo mLastClickedLiveWallpaperInfo; public static abstract class WallpaperTileInfo { protected View mView; @@ -885,25 +881,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { Uri uri = data.getData(); addTemporaryWallpaperTile(uri, false); } - } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) { + } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY && resultCode == RESULT_OK) { + // Something was set on the third-party activity. setResult(RESULT_OK); finish(); - } else if (requestCode == PICK_LIVE_WALLPAPER) { - WallpaperManager wm = WallpaperManager.getInstance(this); - final WallpaperInfo oldLiveWallpaper = mLiveWallpaperInfoOnPickerLaunch; - final WallpaperInfo clickedWallpaper = mLastClickedLiveWallpaperInfo; - WallpaperInfo newLiveWallpaper = wm.getWallpaperInfo(); - // Try to figure out if a live wallpaper was set; - if (newLiveWallpaper != null && - (oldLiveWallpaper == null - || !oldLiveWallpaper.getComponent() - .equals(newLiveWallpaper.getComponent()) - || clickedWallpaper.getComponent() - .equals(oldLiveWallpaper.getComponent()))) { - // Return if a live wallpaper was set - setResult(RESULT_OK); - finish(); - } } } @@ -1110,11 +1091,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return mSavedImages; } - public void onLiveWallpaperPickerLaunch(WallpaperInfo info) { - mLastClickedLiveWallpaperInfo = info; - mLiveWallpaperInfoOnPickerLaunch = WallpaperManager.getInstance(this).getWallpaperInfo(); - } - static class ZeroPaddingDrawable extends LevelListDrawable { public ZeroPaddingDrawable(Drawable d) { super(); -- cgit v1.2.3 From c7eef375121d881e5a83d6e767d68abf87668098 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 12 Feb 2015 10:12:39 -0800 Subject: Removing unused BlockingGLTextureView Change-Id: Iac5aec1193e53dd554f61d07201bac06178914f5 --- .../photos/views/BlockingGLTextureView.java | 438 --------------------- .../com/android/photos/views/TiledImageView.java | 113 +----- 2 files changed, 11 insertions(+), 540 deletions(-) delete mode 100644 WallpaperPicker/src/com/android/photos/views/BlockingGLTextureView.java diff --git a/WallpaperPicker/src/com/android/photos/views/BlockingGLTextureView.java b/WallpaperPicker/src/com/android/photos/views/BlockingGLTextureView.java deleted file mode 100644 index 8a0505185..000000000 --- a/WallpaperPicker/src/com/android/photos/views/BlockingGLTextureView.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2013 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.photos.views; - -import android.content.Context; -import android.graphics.SurfaceTexture; -import android.opengl.GLSurfaceView.Renderer; -import android.opengl.GLUtils; -import android.util.Log; -import android.view.TextureView; -import android.view.TextureView.SurfaceTextureListener; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; -import javax.microedition.khronos.opengles.GL10; - -/** - * A TextureView that supports blocking rendering for synchronous drawing - */ -public class BlockingGLTextureView extends TextureView - implements SurfaceTextureListener { - - private RenderThread mRenderThread; - - public BlockingGLTextureView(Context context) { - super(context); - setSurfaceTextureListener(this); - } - - public void setRenderer(Renderer renderer) { - if (mRenderThread != null) { - throw new IllegalArgumentException("Renderer already set"); - } - mRenderThread = new RenderThread(renderer); - } - - public void render() { - mRenderThread.render(); - } - - public void destroy() { - if (mRenderThread != null) { - mRenderThread.finish(); - mRenderThread = null; - } - } - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, - int height) { - mRenderThread.setSurface(surface); - mRenderThread.setSize(width, height); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, - int height) { - mRenderThread.setSize(width, height); - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - if (mRenderThread != null) { - mRenderThread.setSurface(null); - } - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - } - - @Override - protected void finalize() throws Throwable { - try { - destroy(); - } catch (Throwable t) { - // Ignore - } - super.finalize(); - } - - /** - * An EGL helper class. - */ - - private static class EglHelper { - private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - private static final int EGL_OPENGL_ES2_BIT = 4; - - EGL10 mEgl; - EGLDisplay mEglDisplay; - EGLSurface mEglSurface; - EGLConfig mEglConfig; - EGLContext mEglContext; - - private EGLConfig chooseEglConfig() { - int[] configsCount = new int[1]; - EGLConfig[] configs = new EGLConfig[1]; - int[] configSpec = getConfig(); - if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { - throw new IllegalArgumentException("eglChooseConfig failed " + - GLUtils.getEGLErrorString(mEgl.eglGetError())); - } else if (configsCount[0] > 0) { - return configs[0]; - } - return null; - } - - private static int[] getConfig() { - return new int[] { - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_RED_SIZE, 8, - EGL10.EGL_GREEN_SIZE, 8, - EGL10.EGL_BLUE_SIZE, 8, - EGL10.EGL_ALPHA_SIZE, 8, - EGL10.EGL_DEPTH_SIZE, 0, - EGL10.EGL_STENCIL_SIZE, 0, - EGL10.EGL_NONE - }; - } - - EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { - int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attribList); - } - - /** - * Initialize EGL for a given configuration spec. - */ - public void start() { - /* - * Get an EGL instance - */ - mEgl = (EGL10) EGLContext.getEGL(); - - /* - * Get to the default display. - */ - mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - - if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { - throw new RuntimeException("eglGetDisplay failed"); - } - - /* - * We can now initialize EGL for that display - */ - int[] version = new int[2]; - if (!mEgl.eglInitialize(mEglDisplay, version)) { - throw new RuntimeException("eglInitialize failed"); - } - mEglConfig = chooseEglConfig(); - - /* - * Create an EGL context. We want to do this as rarely as we can, because an - * EGL context is a somewhat heavy object. - */ - mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); - - if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { - mEglContext = null; - throwEglException("createContext"); - } - - mEglSurface = null; - } - - /** - * Create an egl surface for the current SurfaceTexture surface. If a surface - * already exists, destroy it before creating the new surface. - * - * @return true if the surface was created successfully. - */ - public boolean createSurface(SurfaceTexture surface) { - /* - * Check preconditions. - */ - if (mEgl == null) { - throw new RuntimeException("egl not initialized"); - } - if (mEglDisplay == null) { - throw new RuntimeException("eglDisplay not initialized"); - } - if (mEglConfig == null) { - throw new RuntimeException("mEglConfig not initialized"); - } - - /* - * The window size has changed, so we need to create a new - * surface. - */ - destroySurfaceImp(); - - /* - * Create an EGL surface we can render into. - */ - if (surface != null) { - mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, null); - } else { - mEglSurface = null; - } - - if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { - int error = mEgl.eglGetError(); - if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { - Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); - } - return false; - } - - /* - * Before we can issue GL commands, we need to make sure - * the context is current and bound to a surface. - */ - if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - /* - * Could not make the context current, probably because the underlying - * SurfaceView surface has been destroyed. - */ - logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); - return false; - } - - return true; - } - - /** - * Create a GL object for the current EGL context. - */ - public GL10 createGL() { - return (GL10) mEglContext.getGL(); - } - - /** - * Display the current render surface. - * @return the EGL error code from eglSwapBuffers. - */ - public int swap() { - if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { - return mEgl.eglGetError(); - } - return EGL10.EGL_SUCCESS; - } - - public void destroySurface() { - destroySurfaceImp(); - } - - private void destroySurfaceImp() { - if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { - mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_CONTEXT); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); - mEglSurface = null; - } - } - - public void finish() { - if (mEglContext != null) { - mEgl.eglDestroyContext(mEglDisplay, mEglContext); - mEglContext = null; - } - if (mEglDisplay != null) { - mEgl.eglTerminate(mEglDisplay); - mEglDisplay = null; - } - } - - private void throwEglException(String function) { - throwEglException(function, mEgl.eglGetError()); - } - - public static void throwEglException(String function, int error) { - String message = formatEglError(function, error); - throw new RuntimeException(message); - } - - public static void logEglErrorAsWarning(String tag, String function, int error) { - Log.w(tag, formatEglError(function, error)); - } - - public static String formatEglError(String function, int error) { - return function + " failed: " + error; - } - - } - - private static class RenderThread extends Thread { - private static final int INVALID = -1; - private static final int RENDER = 1; - private static final int CHANGE_SURFACE = 2; - private static final int RESIZE_SURFACE = 3; - private static final int FINISH = 4; - - private EglHelper mEglHelper = new EglHelper(); - - private Object mLock = new Object(); - private int mExecMsgId = INVALID; - private SurfaceTexture mSurface; - private Renderer mRenderer; - private int mWidth, mHeight; - - private boolean mFinished = false; - private GL10 mGL; - - public RenderThread(Renderer renderer) { - super("RenderThread"); - mRenderer = renderer; - start(); - } - - private void checkRenderer() { - if (mRenderer == null) { - throw new IllegalArgumentException("Renderer is null!"); - } - } - - private void checkSurface() { - if (mSurface == null) { - throw new IllegalArgumentException("surface is null!"); - } - } - - public void setSurface(SurfaceTexture surface) { - // If the surface is null we're being torn down, don't need a - // renderer then - if (surface != null) { - checkRenderer(); - } - mSurface = surface; - exec(CHANGE_SURFACE); - } - - public void setSize(int width, int height) { - checkRenderer(); - checkSurface(); - mWidth = width; - mHeight = height; - exec(RESIZE_SURFACE); - } - - public void render() { - checkRenderer(); - if (mSurface != null) { - exec(RENDER); - mSurface.updateTexImage(); - } - } - - public void finish() { - mSurface = null; - exec(FINISH); - try { - join(); - } catch (InterruptedException e) { - // Ignore - } - } - - private void exec(int msgid) { - synchronized (mLock) { - if (mExecMsgId != INVALID) { - throw new IllegalArgumentException( - "Message already set - multithreaded access?"); - } - mExecMsgId = msgid; - mLock.notify(); - try { - mLock.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - } - - private void handleMessageLocked(int what) { - switch (what) { - case CHANGE_SURFACE: - if (mEglHelper.createSurface(mSurface)) { - mGL = mEglHelper.createGL(); - mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig); - } - break; - case RESIZE_SURFACE: - mRenderer.onSurfaceChanged(mGL, mWidth, mHeight); - break; - case RENDER: - mRenderer.onDrawFrame(mGL); - mEglHelper.swap(); - break; - case FINISH: - mEglHelper.destroySurface(); - mEglHelper.finish(); - mFinished = true; - break; - } - } - - @Override - public void run() { - synchronized (mLock) { - mEglHelper.start(); - while (!mFinished) { - while (mExecMsgId == INVALID) { - try { - mLock.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - handleMessageLocked(mExecMsgId); - mExecMsgId = INVALID; - mLock.notify(); - } - mExecMsgId = FINISH; - } - } - } -} diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java index 94063b027..524fa2e47 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java @@ -16,8 +16,6 @@ package com.android.photos.views; -import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -28,11 +26,9 @@ import android.graphics.Paint.Align; import android.graphics.RectF; import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView.Renderer; -import android.os.Build; import android.util.AttributeSet; import android.view.Choreographer; import android.view.Choreographer.FrameCallback; -import android.view.View; import android.widget.FrameLayout; import com.android.gallery3d.glrenderer.BasicTexture; @@ -43,18 +39,10 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; /** - * Shows an image using {@link TiledImageRenderer} using either {@link GLSurfaceView} - * or {@link BlockingGLTextureView}. + * Shows an image using {@link TiledImageRenderer} using either {@link GLSurfaceView}. */ public class TiledImageView extends FrameLayout { - private static final boolean USE_TEXTURE_VIEW = false; - private static final boolean IS_SUPPORTED = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - private static final boolean USE_CHOREOGRAPHER = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - - private BlockingGLTextureView mTextureView; private GLSurfaceView mGLSurfaceView; private boolean mInvalPending = false; private FrameCallback mFrameCallback; @@ -79,35 +67,19 @@ public class TiledImageView extends FrameLayout { protected Object mLock = new Object(); protected ImageRendererWrapper mRenderer; - public static boolean isTilingSupported() { - return IS_SUPPORTED; - } - public TiledImageView(Context context) { this(context, null); } public TiledImageView(Context context, AttributeSet attrs) { super(context, attrs); - if (!IS_SUPPORTED) { - return; - } - mRenderer = new ImageRendererWrapper(); mRenderer.image = new TiledImageRenderer(this); - View view; - if (USE_TEXTURE_VIEW) { - mTextureView = new BlockingGLTextureView(context); - mTextureView.setRenderer(new TileRenderer()); - view = mTextureView; - } else { - mGLSurfaceView = new GLSurfaceView(context); - mGLSurfaceView.setEGLContextClientVersion(2); - mGLSurfaceView.setRenderer(new TileRenderer()); - mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - view = mGLSurfaceView; - } - addView(view, new LayoutParams( + mGLSurfaceView = new GLSurfaceView(context); + mGLSurfaceView.setEGLContextClientVersion(2); + mGLSurfaceView.setRenderer(new TileRenderer()); + mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + addView(mGLSurfaceView, new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); //setTileSource(new ColoredTiles()); } @@ -117,22 +89,11 @@ public class TiledImageView extends FrameLayout { super.setVisibility(visibility); // need to update inner view's visibility because it seems like we're causing it to draw // from {@link #dispatchDraw} or {@link #invalidate} even if we are invisible. - if (USE_TEXTURE_VIEW) { - mTextureView.setVisibility(visibility); - } else { - mGLSurfaceView.setVisibility(visibility); - } + mGLSurfaceView.setVisibility(visibility); } public void destroy() { - if (!IS_SUPPORTED) { - return; - } - if (USE_TEXTURE_VIEW) { - mTextureView.destroy(); - } else { - mGLSurfaceView.queueEvent(mFreeTextures); - } + mGLSurfaceView.queueEvent(mFreeTextures); } private Runnable mFreeTextures = new Runnable() { @@ -144,27 +105,14 @@ public class TiledImageView extends FrameLayout { }; public void onPause() { - if (!IS_SUPPORTED) { - return; - } - if (!USE_TEXTURE_VIEW) { - mGLSurfaceView.onPause(); - } + mGLSurfaceView.onPause(); } public void onResume() { - if (!IS_SUPPORTED) { - return; - } - if (!USE_TEXTURE_VIEW) { - mGLSurfaceView.onResume(); - } + mGLSurfaceView.onResume(); } public void setTileSource(TileSource source, Runnable isReadyCallback) { - if (!IS_SUPPORTED) { - return; - } synchronized (mLock) { mRenderer.source = source; mRenderer.isReadyCallback = isReadyCallback; @@ -181,9 +129,6 @@ public class TiledImageView extends FrameLayout { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (!IS_SUPPORTED) { - return; - } synchronized (mLock) { updateScaleIfNecessaryLocked(mRenderer); } @@ -199,44 +144,11 @@ public class TiledImageView extends FrameLayout { (float) getHeight() / (float) renderer.source.getImageHeight()); } - @Override - protected void dispatchDraw(Canvas canvas) { - if (!IS_SUPPORTED) { - return; - } - if (USE_TEXTURE_VIEW) { - mTextureView.render(); - } - super.dispatchDraw(canvas); - } - - @SuppressLint("NewApi") - @Override - public void setTranslationX(float translationX) { - if (!IS_SUPPORTED) { - return; - } - super.setTranslationX(translationX); - } - @Override public void invalidate() { - if (!IS_SUPPORTED) { - return; - } - if (USE_TEXTURE_VIEW) { - super.invalidate(); - mTextureView.invalidate(); - } else { - if (USE_CHOREOGRAPHER) { - invalOnVsync(); - } else { - mGLSurfaceView.requestRender(); - } - } + invalOnVsync(); } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void invalOnVsync() { if (!mInvalPending) { mInvalPending = true; @@ -255,9 +167,6 @@ public class TiledImageView extends FrameLayout { private RectF mTempRectF = new RectF(); public void positionFromMatrix(Matrix matrix) { - if (!IS_SUPPORTED) { - return; - } if (mRenderer.source != null) { final int rotation = mRenderer.source.getRotation(); final boolean swap = !(rotation % 180 == 0); -- cgit v1.2.3 From 06a4d3f1afee2d1069796d585838efbc7a1f491e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 12 Feb 2015 11:19:39 -0800 Subject: Moving some files and methods around > Removing android.util package > Moving static methods and classes out of WallpaperCropActivity > Removing some unused utility methods Change-Id: I252a0655ddce195189b6b3f0bf92970e5808c9d7 --- WallpaperPicker/src/android/util/Pools.java | 165 ------ .../android/gallery3d/common/BitmapCropTask.java | 405 +++++++++++++++ .../com/android/gallery3d/common/BitmapUtils.java | 243 ++++----- .../src/com/android/gallery3d/common/Utils.java | 271 ++-------- .../android/gallery3d/glrenderer/BasicTexture.java | 1 - .../android/gallery3d/glrenderer/GLES20Canvas.java | 2 - .../com/android/gallery3d/glrenderer/IntArray.java | 60 +++ .../src/com/android/gallery3d/util/IntArray.java | 60 --- .../android/launcher3/WallpaperCropActivity.java | 553 +-------------------- .../android/launcher3/WallpaperPickerActivity.java | 15 +- .../src/com/android/photos/views/Pools.java | 165 ++++++ .../android/photos/views/TiledImageRenderer.java | 4 +- 12 files changed, 791 insertions(+), 1153 deletions(-) delete mode 100644 WallpaperPicker/src/android/util/Pools.java create mode 100644 WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java create mode 100644 WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java delete mode 100644 WallpaperPicker/src/com/android/gallery3d/util/IntArray.java create mode 100644 WallpaperPicker/src/com/android/photos/views/Pools.java diff --git a/WallpaperPicker/src/android/util/Pools.java b/WallpaperPicker/src/android/util/Pools.java deleted file mode 100644 index 40bab1eae..000000000 --- a/WallpaperPicker/src/android/util/Pools.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2009 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 android.util; - -/** - * Helper class for crating pools of objects. An example use looks like this: - *
- * public class MyPooledClass {
- *
- *     private static final SynchronizedPool sPool =
- *             new SynchronizedPool(10);
- *
- *     public static MyPooledClass obtain() {
- *         MyPooledClass instance = sPool.acquire();
- *         return (instance != null) ? instance : new MyPooledClass();
- *     }
- *
- *     public void recycle() {
- *          // Clear state if needed.
- *          sPool.release(this);
- *     }
- *
- *     . . .
- * }
- * 
- * - * @hide - */ -public final class Pools { - - /** - * Interface for managing a pool of objects. - * - * @param The pooled type. - */ - public static interface Pool { - - /** - * @return An instance from the pool if such, null otherwise. - */ - public T acquire(); - - /** - * Release an instance to the pool. - * - * @param instance The instance to release. - * @return Whether the instance was put in the pool. - * - * @throws IllegalStateException If the instance is already in the pool. - */ - public boolean release(T instance); - } - - private Pools() { - /* do nothing - hiding constructor */ - } - - /** - * Simple (non-synchronized) pool of objects. - * - * @param The pooled type. - */ - public static class SimplePool implements Pool { - private final Object[] mPool; - - private int mPoolSize; - - /** - * Creates a new instance. - * - * @param maxPoolSize The max pool size. - * - * @throws IllegalArgumentException If the max pool size is less than zero. - */ - public SimplePool(int maxPoolSize) { - if (maxPoolSize <= 0) { - throw new IllegalArgumentException("The max pool size must be > 0"); - } - mPool = new Object[maxPoolSize]; - } - - @Override - @SuppressWarnings("unchecked") - public T acquire() { - if (mPoolSize > 0) { - final int lastPooledIndex = mPoolSize - 1; - T instance = (T) mPool[lastPooledIndex]; - mPool[lastPooledIndex] = null; - mPoolSize--; - return instance; - } - return null; - } - - @Override - public boolean release(T instance) { - if (isInPool(instance)) { - throw new IllegalStateException("Already in the pool!"); - } - if (mPoolSize < mPool.length) { - mPool[mPoolSize] = instance; - mPoolSize++; - return true; - } - return false; - } - - private boolean isInPool(T instance) { - for (int i = 0; i < mPoolSize; i++) { - if (mPool[i] == instance) { - return true; - } - } - return false; - } - } - - /** - * Synchronized) pool of objects. - * - * @param The pooled type. - */ - public static class SynchronizedPool extends SimplePool { - private final Object mLock = new Object(); - - /** - * Creates a new instance. - * - * @param maxPoolSize The max pool size. - * - * @throws IllegalArgumentException If the max pool size is less than zero. - */ - public SynchronizedPool(int maxPoolSize) { - super(maxPoolSize); - } - - @Override - public T acquire() { - synchronized (mLock) { - return super.acquire(); - } - } - - @Override - public boolean release(T element) { - synchronized (mLock) { - return super.release(element); - } - } - } -} \ No newline at end of file diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java new file mode 100644 index 000000000..45118bf45 --- /dev/null +++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java @@ -0,0 +1,405 @@ +/** + * 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.gallery3d.common; + +import android.app.WallpaperManager; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.net.Uri; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class BitmapCropTask extends AsyncTask { + + public interface OnBitmapCroppedHandler { + public void onBitmapCropped(byte[] imageBytes); + } + + private static final int DEFAULT_COMPRESS_QUALITY = 90; + private static final String LOGTAG = "BitmapCropTask"; + + Uri mInUri = null; + Context mContext; + String mInFilePath; + byte[] mInImageBytes; + int mInResId = 0; + RectF mCropBounds = null; + int mOutWidth, mOutHeight; + int mRotation; + boolean mSetWallpaper; + boolean mSaveCroppedBitmap; + Bitmap mCroppedBitmap; + Runnable mOnEndRunnable; + Resources mResources; + BitmapCropTask.OnBitmapCroppedHandler mOnBitmapCroppedHandler; + boolean mNoCrop; + + public BitmapCropTask(Context c, String filePath, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mContext = c; + mInFilePath = filePath; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + public BitmapCropTask(byte[] imageBytes, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mInImageBytes = imageBytes; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + public BitmapCropTask(Context c, Uri inUri, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mContext = c; + mInUri = inUri; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + public BitmapCropTask(Context c, Resources res, int inResId, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mContext = c; + mInResId = inResId; + mResources = res; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mCropBounds = cropBounds; + mRotation = rotation; + mOutWidth = outWidth; + mOutHeight = outHeight; + mSetWallpaper = setWallpaper; + mSaveCroppedBitmap = saveCroppedBitmap; + mOnEndRunnable = onEndRunnable; + } + + public void setOnBitmapCropped(BitmapCropTask.OnBitmapCroppedHandler handler) { + mOnBitmapCroppedHandler = handler; + } + + public void setNoCrop(boolean value) { + mNoCrop = value; + } + + public void setOnEndRunnable(Runnable onEndRunnable) { + mOnEndRunnable = onEndRunnable; + } + + // Helper to setup input stream + private InputStream regenerateInputStream() { + if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { + Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + + "image byte array given"); + } else { + try { + if (mInUri != null) { + return new BufferedInputStream( + mContext.getContentResolver().openInputStream(mInUri)); + } else if (mInFilePath != null) { + return mContext.openFileInput(mInFilePath); + } else if (mInImageBytes != null) { + return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes)); + } else { + return new BufferedInputStream(mResources.openRawResource(mInResId)); + } + } catch (FileNotFoundException e) { + Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); + } + } + return null; + } + + public Point getImageBounds() { + InputStream is = regenerateInputStream(); + if (is != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(is, null, options); + Utils.closeSilently(is); + if (options.outWidth != 0 && options.outHeight != 0) { + return new Point(options.outWidth, options.outHeight); + } + } + return null; + } + + public void setCropBounds(RectF cropBounds) { + mCropBounds = cropBounds; + } + + public Bitmap getCroppedBitmap() { + return mCroppedBitmap; + } + public boolean cropBitmap() { + boolean failure = false; + + + WallpaperManager wallpaperManager = null; + if (mSetWallpaper) { + wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); + } + + + if (mSetWallpaper && mNoCrop) { + try { + InputStream is = regenerateInputStream(); + if (is != null) { + wallpaperManager.setStream(is); + Utils.closeSilently(is); + } + } catch (IOException e) { + Log.w(LOGTAG, "cannot write stream to wallpaper", e); + failure = true; + } + return !failure; + } else { + // Find crop bounds (scaled to original image size) + Rect roundedTrueCrop = new Rect(); + Matrix rotateMatrix = new Matrix(); + Matrix inverseRotateMatrix = new Matrix(); + + Point bounds = getImageBounds(); + if (mRotation > 0) { + rotateMatrix.setRotate(mRotation); + inverseRotateMatrix.setRotate(-mRotation); + + mCropBounds.roundOut(roundedTrueCrop); + mCropBounds = new RectF(roundedTrueCrop); + + if (bounds == null) { + Log.w(LOGTAG, "cannot get bounds for image"); + failure = true; + return false; + } + + float[] rotatedBounds = new float[] { bounds.x, bounds.y }; + rotateMatrix.mapPoints(rotatedBounds); + rotatedBounds[0] = Math.abs(rotatedBounds[0]); + rotatedBounds[1] = Math.abs(rotatedBounds[1]); + + mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); + inverseRotateMatrix.mapRect(mCropBounds); + mCropBounds.offset(bounds.x/2, bounds.y/2); + + } + + mCropBounds.roundOut(roundedTrueCrop); + + if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { + Log.w(LOGTAG, "crop has bad values for full size image"); + failure = true; + return false; + } + + // See how much we're reducing the size of the image + int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth, + roundedTrueCrop.height() / mOutHeight)); + // Attempt to open a region decoder + BitmapRegionDecoder decoder = null; + InputStream is = null; + try { + is = regenerateInputStream(); + if (is == null) { + Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString()); + failure = true; + return false; + } + decoder = BitmapRegionDecoder.newInstance(is, false); + Utils.closeSilently(is); + } catch (IOException e) { + Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); + } finally { + Utils.closeSilently(is); + is = null; + } + + Bitmap crop = null; + if (decoder != null) { + // Do region decoding to get crop bitmap + BitmapFactory.Options options = new BitmapFactory.Options(); + if (scaleDownSampleSize > 1) { + options.inSampleSize = scaleDownSampleSize; + } + crop = decoder.decodeRegion(roundedTrueCrop, options); + decoder.recycle(); + } + + if (crop == null) { + // BitmapRegionDecoder has failed, try to crop in-memory + is = regenerateInputStream(); + Bitmap fullSize = null; + if (is != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + if (scaleDownSampleSize > 1) { + options.inSampleSize = scaleDownSampleSize; + } + fullSize = BitmapFactory.decodeStream(is, null, options); + Utils.closeSilently(is); + } + if (fullSize != null) { + // Find out the true sample size that was used by the decoder + scaleDownSampleSize = bounds.x / fullSize.getWidth(); + mCropBounds.left /= scaleDownSampleSize; + mCropBounds.top /= scaleDownSampleSize; + mCropBounds.bottom /= scaleDownSampleSize; + mCropBounds.right /= scaleDownSampleSize; + mCropBounds.roundOut(roundedTrueCrop); + + // Adjust values to account for issues related to rounding + if (roundedTrueCrop.width() > fullSize.getWidth()) { + // Adjust the width + roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth(); + } + if (roundedTrueCrop.right > fullSize.getWidth()) { + // Adjust the left value + int adjustment = roundedTrueCrop.left - + Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width()); + roundedTrueCrop.left -= adjustment; + roundedTrueCrop.right -= adjustment; + } + if (roundedTrueCrop.height() > fullSize.getHeight()) { + // Adjust the height + roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight(); + } + if (roundedTrueCrop.bottom > fullSize.getHeight()) { + // Adjust the top value + int adjustment = roundedTrueCrop.top - + Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height()); + roundedTrueCrop.top -= adjustment; + roundedTrueCrop.bottom -= adjustment; + } + + crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, + roundedTrueCrop.top, roundedTrueCrop.width(), + roundedTrueCrop.height()); + } + } + + if (crop == null) { + Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); + failure = true; + return false; + } + if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { + float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; + rotateMatrix.mapPoints(dimsAfter); + dimsAfter[0] = Math.abs(dimsAfter[0]); + dimsAfter[1] = Math.abs(dimsAfter[1]); + + if (!(mOutWidth > 0 && mOutHeight > 0)) { + mOutWidth = Math.round(dimsAfter[0]); + mOutHeight = Math.round(dimsAfter[1]); + } + + RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); + RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); + + Matrix m = new Matrix(); + if (mRotation == 0) { + m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); + } else { + Matrix m1 = new Matrix(); + m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); + Matrix m2 = new Matrix(); + m2.setRotate(mRotation); + Matrix m3 = new Matrix(); + m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); + Matrix m4 = new Matrix(); + m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); + + Matrix c1 = new Matrix(); + c1.setConcat(m2, m1); + Matrix c2 = new Matrix(); + c2.setConcat(m4, m3); + m.setConcat(c2, c1); + } + + Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), + (int) returnRect.height(), Bitmap.Config.ARGB_8888); + if (tmp != null) { + Canvas c = new Canvas(tmp); + Paint p = new Paint(); + p.setFilterBitmap(true); + c.drawBitmap(crop, m, p); + crop = tmp; + } + } + + if (mSaveCroppedBitmap) { + mCroppedBitmap = crop; + } + + // Compress to byte array + ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); + if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) { + // If we need to set to the wallpaper, set it + if (mSetWallpaper && wallpaperManager != null) { + try { + byte[] outByteArray = tmpOut.toByteArray(); + wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); + if (mOnBitmapCroppedHandler != null) { + mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); + } + } catch (IOException e) { + Log.w(LOGTAG, "cannot write stream to wallpaper", e); + failure = true; + } + } + } else { + Log.w(LOGTAG, "cannot compress bitmap"); + failure = true; + } + } + return !failure; // True if any of the operations failed + } + + @Override + protected Boolean doInBackground(Void... params) { + return cropBitmap(); + } + + @Override + protected void onPostExecute(Boolean result) { + if (mOnEndRunnable != null) { + mOnEndRunnable.run(); + } + } +} \ No newline at end of file diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java index 6a816d990..347001783 100644 --- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java +++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java @@ -16,83 +16,32 @@ package com.android.gallery3d.common; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; -import android.util.FloatMath; +import android.graphics.Point; +import android.net.Uri; +import android.os.Build; import android.util.Log; +import android.view.WindowManager; -import java.io.ByteArrayOutputStream; +import com.android.gallery3d.exif.ExifInterface; +import com.android.launcher3.WallpaperCropActivity; -public class BitmapUtils { - private static final String TAG = "BitmapUtils"; - private static final int DEFAULT_JPEG_QUALITY = 90; - public static final int UNCONSTRAINED = -1; - - private BitmapUtils(){} - - /* - * Compute the sample size as a function of minSideLength - * and maxNumOfPixels. - * minSideLength is used to specify that minimal width or height of a - * bitmap. - * maxNumOfPixels is used to specify the maximal size in pixels that is - * tolerable in terms of memory usage. - * - * The function returns a sample size based on the constraints. - * Both size and minSideLength can be passed in as UNCONSTRAINED, - * which indicates no care of the corresponding constraint. - * The functions prefers returning a sample size that - * generates a smaller bitmap, unless minSideLength = UNCONSTRAINED. - * - * Also, the function rounds up the sample size to a power of 2 or multiple - * of 8 because BitmapFactory only honors sample size this way. - * For example, BitmapFactory downsamples an image by 2 even though the - * request is 3. So we round up the sample size to avoid OOM. - */ - public static int computeSampleSize(int width, int height, - int minSideLength, int maxNumOfPixels) { - int initialSize = computeInitialSampleSize( - width, height, minSideLength, maxNumOfPixels); - - return initialSize <= 8 - ? Utils.nextPowerOf2(initialSize) - : (initialSize + 7) / 8 * 8; - } - - private static int computeInitialSampleSize(int w, int h, - int minSideLength, int maxNumOfPixels) { - if (maxNumOfPixels == UNCONSTRAINED - && minSideLength == UNCONSTRAINED) return 1; - - int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : - (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels)); - - if (minSideLength == UNCONSTRAINED) { - return lowerBound; - } else { - int sampleSize = Math.min(w / minSideLength, h / minSideLength); - return Math.max(sampleSize, lowerBound); - } - } +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; - // This computes a sample size which makes the longer side at least - // minSideLength long. If that's not possible, return 1. - public static int computeSampleSizeLarger(int w, int h, - int minSideLength) { - int initialSize = Math.max(w / minSideLength, h / minSideLength); - if (initialSize <= 1) return 1; +public class BitmapUtils { - return initialSize <= 8 - ? Utils.prevPowerOf2(initialSize) - : initialSize / 8 * 8; - } + private static final String TAG = "BitmapUtils"; // Find the min x that 1 / x >= scale public static int computeSampleSizeLarger(float scale) { - int initialSize = (int) FloatMath.floor(1f / scale); + int initialSize = (int) Math.floor(1f / scale); if (initialSize <= 1) return 1; return initialSize <= 8 @@ -100,15 +49,6 @@ public class BitmapUtils { : initialSize / 8 * 8; } - // Find the max x that 1 / x <= scale. - public static int computeSampleSize(float scale) { - Utils.assertTrue(scale > 0); - int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale)); - return initialSize <= 8 - ? Utils.nextPowerOf2(initialSize) - : (initialSize + 7) / 8 * 8; - } - public static Bitmap resizeBitmapByScale( Bitmap bitmap, float scale, boolean recycle) { int width = Math.round(bitmap.getWidth() * scale); @@ -132,77 +72,104 @@ public class BitmapUtils { return config; } - public static Bitmap resizeDownBySideLength( - Bitmap bitmap, int maxLength, boolean recycle) { - int srcWidth = bitmap.getWidth(); - int srcHeight = bitmap.getHeight(); - float scale = Math.min( - (float) maxLength / srcWidth, (float) maxLength / srcHeight); - if (scale >= 1.0f) return bitmap; - return resizeBitmapByScale(bitmap, scale, recycle); - } - - public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) { - int w = bitmap.getWidth(); - int h = bitmap.getHeight(); - if (w == size && h == size) return bitmap; - - // scale the image so that the shorter side equals to the target; - // the longer side will be center-cropped. - float scale = (float) size / Math.min(w, h); - - Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap)); - int width = Math.round(scale * bitmap.getWidth()); - int height = Math.round(scale * bitmap.getHeight()); - Canvas canvas = new Canvas(target); - canvas.translate((size - width) / 2f, (size - height) / 2f); - canvas.scale(scale, scale); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); - canvas.drawBitmap(bitmap, 0, 0, paint); - if (recycle) bitmap.recycle(); - return target; + /** + * As a ratio of screen height, the total distance we want the parallax effect to span + * horizontally + */ + public static float wallpaperTravelToScreenWidthRatio(int width, int height) { + float aspectRatio = width / (float) height; + + // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width + // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width + // We will use these two data points to extrapolate how much the wallpaper parallax effect + // to span (ie travel) at any aspect ratio: + + final float ASPECT_RATIO_LANDSCAPE = 16/10f; + final float ASPECT_RATIO_PORTRAIT = 10/16f; + final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; + final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; + + // To find out the desired width at different aspect ratios, we use the following two + // formulas, where the coefficient on x is the aspect ratio (width/height): + // (16/10)x + y = 1.5 + // (10/16)x + y = 1.2 + // We solve for x and y and end up with a final formula: + final float x = + (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / + (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); + final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; + return x * aspectRatio + y; } - public static void recycleSilently(Bitmap bitmap) { - if (bitmap == null) return; - try { - bitmap.recycle(); - } catch (Throwable t) { - Log.w(TAG, "unable recycle bitmap", t); + private static Point sDefaultWallpaperSize; + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { + if (sDefaultWallpaperSize == null) { + Point minDims = new Point(); + Point maxDims = new Point(); + windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); + + int maxDim = Math.max(maxDims.x, maxDims.y); + int minDim = Math.max(minDims.x, minDims.y); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + Point realSize = new Point(); + windowManager.getDefaultDisplay().getRealSize(realSize); + maxDim = Math.max(realSize.x, realSize.y); + minDim = Math.min(realSize.x, realSize.y); + } + + // We need to ensure that there is enough extra space in the wallpaper + // for the intended parallax effects + final int defaultWidth, defaultHeight; + if (res.getConfiguration().smallestScreenWidthDp >= 720) { + defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); + defaultHeight = maxDim; + } else { + defaultWidth = Math.max((int) (minDim * WallpaperCropActivity.WALLPAPER_SCREENS_SPAN), maxDim); + defaultHeight = maxDim; + } + sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight); } + return sDefaultWallpaperSize; } - public static Bitmap rotateBitmap(Bitmap source, int rotation, boolean recycle) { - if (rotation == 0) return source; - int w = source.getWidth(); - int h = source.getHeight(); - Matrix m = new Matrix(); - m.postRotate(rotation); - Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, w, h, m, true); - if (recycle) source.recycle(); - return bitmap; - } - - public static byte[] compressToBytes(Bitmap bitmap) { - return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY); + public static int getRotationFromExif(Context context, Uri uri) { + return BitmapUtils.getRotationFromExifHelper(null, 0, context, uri); } - public static byte[] compressToBytes(Bitmap bitmap, int quality) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(65536); - bitmap.compress(CompressFormat.JPEG, quality, baos); - return baos.toByteArray(); + public static int getRotationFromExif(Resources res, int resId) { + return BitmapUtils.getRotationFromExifHelper(res, resId, null, null); } - public static boolean isSupportedByRegionDecoder(String mimeType) { - if (mimeType == null) return false; - mimeType = mimeType.toLowerCase(); - return mimeType.startsWith("image/") && - (!mimeType.equals("image/gif") && !mimeType.endsWith("bmp")); - } - - public static boolean isRotationSupported(String mimeType) { - if (mimeType == null) return false; - mimeType = mimeType.toLowerCase(); - return mimeType.equals("image/jpeg"); + private static int getRotationFromExifHelper(Resources res, int resId, Context context, Uri uri) { + ExifInterface ei = new ExifInterface(); + InputStream is = null; + BufferedInputStream bis = null; + try { + if (uri != null) { + is = context.getContentResolver().openInputStream(uri); + bis = new BufferedInputStream(is); + ei.readExif(bis); + } else { + is = res.openRawResource(resId); + bis = new BufferedInputStream(is); + ei.readExif(bis); + } + Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); + if (ori != null) { + return ExifInterface.getRotationForOrientationValue(ori.shortValue()); + } + } catch (IOException e) { + Log.w(TAG, "Getting exif data failed", e); + } catch (NullPointerException e) { + // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid + Log.w(TAG, "Getting exif data failed", e); + } finally { + Utils.closeSilently(bis); + Utils.closeSilently(is); + } + return 0; } } diff --git a/WallpaperPicker/src/com/android/gallery3d/common/Utils.java b/WallpaperPicker/src/com/android/gallery3d/common/Utils.java index 614a081c8..8466c22cb 100644 --- a/WallpaperPicker/src/com/android/gallery3d/common/Utils.java +++ b/WallpaperPicker/src/com/android/gallery3d/common/Utils.java @@ -16,32 +16,16 @@ package com.android.gallery3d.common; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.database.Cursor; -import android.os.Build; +import android.graphics.RectF; import android.os.ParcelFileDescriptor; -import android.text.TextUtils; import android.util.Log; import java.io.Closeable; import java.io.IOException; -import java.io.InterruptedIOException; public class Utils { private static final String TAG = "Utils"; - private static final String DEBUG_TAG = "GalleryDebug"; - - private static final long POLY64REV = 0x95AC9329AC4BC9B5L; - private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL; - - private static long[] sCrcTable = new long[256]; - - private static final boolean IS_DEBUG_BUILD = - Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"); - - private static final String MASK_STRING = "********************************"; // Throws AssertionError if the input is false. public static void assertTrue(boolean cond) { @@ -50,28 +34,6 @@ public class Utils { } } - // Throws AssertionError with the message. We had a method having the form - // assertTrue(boolean cond, String message, Object ... args); - // However a call to that method will cause memory allocation even if the - // condition is false (due to autoboxing generated by "Object ... args"), - // so we don't use that anymore. - public static void fail(String message, Object ... args) { - throw new AssertionError( - args.length == 0 ? message : String.format(message, args)); - } - - // Throws NullPointerException if the input is null. - public static T checkNotNull(T object) { - if (object == null) throw new NullPointerException(); - return object; - } - - // Returns true if two input Object are both null or equal - // to each other. - public static boolean equals(Object a, Object b) { - return (a == b) || (a == null ? false : a.equals(b)); - } - // Returns the next power of two. // Returns the input if it is already power of 2. // Throws IllegalArgumentException if the input is <= 0 or @@ -102,87 +64,6 @@ public class Utils { return x; } - // Returns the input value x clamped to the range [min, max]. - public static float clamp(float x, float min, float max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - // Returns the input value x clamped to the range [min, max]. - public static long clamp(long x, long min, long max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - public static boolean isOpaque(int color) { - return color >>> 24 == 0xFF; - } - - public static void swap(int[] array, int i, int j) { - int temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - - /** - * A function thats returns a 64-bit crc for string - * - * @param in input string - * @return a 64-bit crc value - */ - public static final long crc64Long(String in) { - if (in == null || in.length() == 0) { - return 0; - } - return crc64Long(getBytes(in)); - } - - static { - // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c - long part; - for (int i = 0; i < 256; i++) { - part = i; - for (int j = 0; j < 8; j++) { - long x = ((int) part & 1) != 0 ? POLY64REV : 0; - part = (part >> 1) ^ x; - } - sCrcTable[i] = part; - } - } - - public static final long crc64Long(byte[] buffer) { - long crc = INITIALCRC; - for (int k = 0, n = buffer.length; k < n; ++k) { - crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8); - } - return crc; - } - - public static byte[] getBytes(String in) { - byte[] result = new byte[in.length() * 2]; - int output = 0; - for (char ch : in.toCharArray()) { - result[output++] = (byte) (ch & 0xFF); - result[output++] = (byte) (ch >> 8); - } - return result; - } - - public static void closeSilently(Closeable c) { - if (c == null) return; - try { - c.close(); - } catch (IOException t) { - Log.w(TAG, "close fail ", t); - } - } - - public static int compare(long a, long b) { - return a < b ? -1 : a == b ? 0 : 1; - } - public static int ceilLog2(float value) { int i; for (i = 0; i < 31; i++) { @@ -199,6 +80,15 @@ public class Utils { return i - 1; } + public static void closeSilently(Closeable c) { + if (c == null) return; + try { + c.close(); + } catch (IOException t) { + Log.w(TAG, "close fail ", t); + } + } + public static void closeSilently(ParcelFileDescriptor fd) { try { if (fd != null) fd.close(); @@ -215,126 +105,25 @@ public class Utils { } } - public static float interpolateAngle( - float source, float target, float progress) { - // interpolate the angle from source to target - // We make the difference in the range of [-179, 180], this is the - // shortest path to change source to target. - float diff = target - source; - if (diff < 0) diff += 360f; - if (diff > 180) diff -= 360f; - - float result = source + diff * progress; - return result < 0 ? result + 360f : result; - } - - public static float interpolateScale( - float source, float target, float progress) { - return source + progress * (target - source); - } - - public static String ensureNotNull(String value) { - return value == null ? "" : value; - } - - public static float parseFloatSafely(String content, float defaultValue) { - if (content == null) return defaultValue; - try { - return Float.parseFloat(content); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public static int parseIntSafely(String content, int defaultValue) { - if (content == null) return defaultValue; - try { - return Integer.parseInt(content); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public static boolean isNullOrEmpty(String exifMake) { - return TextUtils.isEmpty(exifMake); - } - - public static void waitWithoutInterrupt(Object object) { - try { - object.wait(); - } catch (InterruptedException e) { - Log.w(TAG, "unexpected interrupt: " + object); - } - } - - public static boolean handleInterrruptedException(Throwable e) { - // A helper to deal with the interrupt exception - // If an interrupt detected, we will setup the bit again. - if (e instanceof InterruptedIOException - || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - return true; - } - return false; - } - - /** - * @return String with special XML characters escaped. - */ - public static String escapeXml(String s) { - StringBuilder sb = new StringBuilder(); - for (int i = 0, len = s.length(); i < len; ++i) { - char c = s.charAt(i); - switch (c) { - case '<': sb.append("<"); break; - case '>': sb.append(">"); break; - case '\"': sb.append("""); break; - case '\'': sb.append("'"); break; - case '&': sb.append("&"); break; - default: sb.append(c); - } - } - return sb.toString(); - } - - public static String getUserAgent(Context context) { - PackageInfo packageInfo; - try { - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - } catch (NameNotFoundException e) { - throw new IllegalStateException("getPackageInfo failed"); - } - return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s", - packageInfo.packageName, - packageInfo.versionName, - Build.BRAND, - Build.DEVICE, - Build.MODEL, - Build.ID, - Build.VERSION.SDK_INT, - Build.VERSION.RELEASE, - Build.VERSION.INCREMENTAL); - } - - public static String[] copyOf(String[] source, int newSize) { - String[] result = new String[newSize]; - newSize = Math.min(source.length, newSize); - System.arraycopy(source, 0, result, 0, newSize); - return result; - } - - // Mask information for debugging only. It returns info.toString() directly - // for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****") - // in release build to protect the information (e.g. for privacy issue). - public static String maskDebugInfo(Object info) { - if (info == null) return null; - String s = info.toString(); - int length = Math.min(s.length(), MASK_STRING.length()); - return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length); - } - - // This method should be ONLY used for debugging. - public static void debug(String message, Object ... args) { - Log.v(DEBUG_TAG, String.format(message, args)); + public static RectF getMaxCropRect( + int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { + RectF cropRect = new RectF(); + // Get a crop rect that will fit this + if (inWidth / (float) inHeight > outWidth / (float) outHeight) { + cropRect.top = 0; + cropRect.bottom = inHeight; + cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; + cropRect.right = inWidth - cropRect.left; + if (leftAligned) { + cropRect.right -= cropRect.left; + cropRect.left = 0; + } + } else { + cropRect.left = 0; + cropRect.right = inWidth; + cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; + cropRect.bottom = inHeight - cropRect.top; + } + return cropRect; } } diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java index 2e77b903f..0f3efb727 100644 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java +++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java @@ -27,7 +27,6 @@ import java.util.WeakHashMap; // If a BasicTexture is loaded into GL memory, it has a GL texture id. public abstract class BasicTexture implements Texture { - @SuppressWarnings("unused") private static final String TAG = "BasicTexture"; protected static final int UNSPECIFIED = -1; diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java index 4ead1315e..8af1f5932 100644 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java +++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java @@ -23,8 +23,6 @@ import android.opengl.GLUtils; import android.opengl.Matrix; import android.util.Log; -import com.android.gallery3d.util.IntArray; - import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java new file mode 100644 index 000000000..f123624d6 --- /dev/null +++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 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.gallery3d.glrenderer; + +public class IntArray { + private static final int INIT_CAPACITY = 8; + + private int mData[] = new int[INIT_CAPACITY]; + private int mSize = 0; + + public void add(int value) { + if (mData.length == mSize) { + int temp[] = new int[mSize + mSize]; + System.arraycopy(mData, 0, temp, 0, mSize); + mData = temp; + } + mData[mSize++] = value; + } + + public int removeLast() { + mSize--; + return mData[mSize]; + } + + public int size() { + return mSize; + } + + // For testing only + public int[] toArray(int[] result) { + if (result == null || result.length < mSize) { + result = new int[mSize]; + } + System.arraycopy(mData, 0, result, 0, mSize); + return result; + } + + public int[] getInternalArray() { + return mData; + } + + public void clear() { + mSize = 0; + if (mData.length != INIT_CAPACITY) mData = new int[INIT_CAPACITY]; + } +} diff --git a/WallpaperPicker/src/com/android/gallery3d/util/IntArray.java b/WallpaperPicker/src/com/android/gallery3d/util/IntArray.java deleted file mode 100644 index 2c4dc2c83..000000000 --- a/WallpaperPicker/src/com/android/gallery3d/util/IntArray.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.util; - -public class IntArray { - private static final int INIT_CAPACITY = 8; - - private int mData[] = new int[INIT_CAPACITY]; - private int mSize = 0; - - public void add(int value) { - if (mData.length == mSize) { - int temp[] = new int[mSize + mSize]; - System.arraycopy(mData, 0, temp, 0, mSize); - mData = temp; - } - mData[mSize++] = value; - } - - public int removeLast() { - mSize--; - return mData[mSize]; - } - - public int size() { - return mSize; - } - - // For testing only - public int[] toArray(int[] result) { - if (result == null || result.length < mSize) { - result = new int[mSize]; - } - System.arraycopy(mData, 0, result, 0, mSize); - return result; - } - - public int[] getInternalArray() { - return mData; - } - - public void clear() { - mSize = 0; - if (mData.length != INIT_CAPACITY) mData = new int[INIT_CAPACITY]; - } -} diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index fa8ec64c2..71c7a161d 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; import android.app.WallpaperManager; @@ -24,43 +25,30 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; -import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.Point; -import android.graphics.Rect; import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.Toast; - +import com.android.gallery3d.common.BitmapCropTask; +import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; -import com.android.gallery3d.exif.ExifInterface; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - public class WallpaperCropActivity extends Activity { private static final String LOGTAG = "Launcher3.CropActivity"; protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; - private static final int DEFAULT_COMPRESS_QUALITY = 90; + /** * The maximum bitmap size we allow to be returned through the intent. * Intents have a maximum of 1MB in total size. However, the Bitmap seems to @@ -69,9 +57,7 @@ public class WallpaperCropActivity extends Activity { * array instead of a Bitmap instance to avoid overhead. */ public static final int MAX_BMAP_IN_INTENT = 750000; - private static final float WALLPAPER_SCREENS_SPAN = 2f; - - protected static Point sDefaultWallpaperSize; + public static final float WALLPAPER_SCREENS_SPAN = 2f; protected CropView mCropView; protected Uri mUri; @@ -205,111 +191,8 @@ public class WallpaperCropActivity extends Activity { return LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; } - // As a ratio of screen height, the total distance we want the parallax effect to span - // horizontally - private static float wallpaperTravelToScreenWidthRatio(int width, int height) { - float aspectRatio = width / (float) height; - - // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width - // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width - // We will use these two data points to extrapolate how much the wallpaper parallax effect - // to span (ie travel) at any aspect ratio: - - final float ASPECT_RATIO_LANDSCAPE = 16/10f; - final float ASPECT_RATIO_PORTRAIT = 10/16f; - final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; - final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; - - // To find out the desired width at different aspect ratios, we use the following two - // formulas, where the coefficient on x is the aspect ratio (width/height): - // (16/10)x + y = 1.5 - // (10/16)x + y = 1.2 - // We solve for x and y and end up with a final formula: - final float x = - (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / - (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); - final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; - return x * aspectRatio + y; - } - - static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { - if (sDefaultWallpaperSize == null) { - Point minDims = new Point(); - Point maxDims = new Point(); - windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); - - int maxDim = Math.max(maxDims.x, maxDims.y); - int minDim = Math.max(minDims.x, minDims.y); - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { - Point realSize = new Point(); - windowManager.getDefaultDisplay().getRealSize(realSize); - maxDim = Math.max(realSize.x, realSize.y); - minDim = Math.min(realSize.x, realSize.y); - } - - // We need to ensure that there is enough extra space in the wallpaper - // for the intended parallax effects - final int defaultWidth, defaultHeight; - if (isScreenLarge(res)) { - defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); - defaultHeight = maxDim; - } else { - defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); - defaultHeight = maxDim; - } - sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight); - } - return sDefaultWallpaperSize; - } - - public static int getRotationFromExif(String path) { - return getRotationFromExifHelper(path, null, 0, null, null); - } - - public static int getRotationFromExif(Context context, Uri uri) { - return getRotationFromExifHelper(null, null, 0, context, uri); - } - - public static int getRotationFromExif(Resources res, int resId) { - return getRotationFromExifHelper(null, res, resId, null, null); - } - - private static int getRotationFromExifHelper( - String path, Resources res, int resId, Context context, Uri uri) { - ExifInterface ei = new ExifInterface(); - InputStream is = null; - BufferedInputStream bis = null; - try { - if (path != null) { - ei.readExif(path); - } else if (uri != null) { - is = context.getContentResolver().openInputStream(uri); - bis = new BufferedInputStream(is); - ei.readExif(bis); - } else { - is = res.openRawResource(resId); - bis = new BufferedInputStream(is); - ei.readExif(bis); - } - Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); - if (ori != null) { - return ExifInterface.getRotationForOrientationValue(ori.shortValue()); - } - } catch (IOException e) { - Log.w(LOGTAG, "Getting exif data failed", e); - } catch (NullPointerException e) { - // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid - Log.w(LOGTAG, "Getting exif data failed", e); - } finally { - Utils.closeSilently(bis); - Utils.closeSilently(is); - } - return 0; - } - protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) { - int rotation = getRotationFromExif(this, uri); + int rotation = BitmapUtils.getRotationFromExif(this, uri); BitmapCropTask cropTask = new BitmapCropTask( this, uri, null, rotation, 0, 0, true, false, null); final Point bounds = cropTask.getImageBounds(); @@ -331,11 +214,11 @@ public class WallpaperCropActivity extends Activity { Resources res, int resId, final boolean finishActivityWhenDone) { // crop this image and scale it down to the default wallpaper size for // this device - int rotation = getRotationFromExif(res, resId); + int rotation = BitmapUtils.getRotationFromExif(res, resId); Point inSize = mCropView.getSourceDimensions(); - Point outSize = getDefaultWallpaperSize(getResources(), + Point outSize = BitmapUtils.getDefaultWallpaperSize(getResources(), getWindowManager()); - RectF crop = getMaxCropRect( + RectF crop = Utils.getMaxCropRect( inSize.x, inSize.y, outSize.x, outSize.y, false); Runnable onEndCrop = new Runnable() { public void run() { @@ -353,13 +236,9 @@ public class WallpaperCropActivity extends Activity { cropTask.execute(); } - private static boolean isScreenLarge(Resources res) { - Configuration config = res.getConfiguration(); - return config.smallestScreenWidthDp >= 720; - } - + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) protected void cropImageAndSetWallpaper(Uri uri, - OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { + BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { boolean centerCrop = getResources().getBoolean(R.bool.center_crop); // Get the crop boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; @@ -370,7 +249,7 @@ public class WallpaperCropActivity extends Activity { d.getSize(displaySize); boolean isPortrait = displaySize.x < displaySize.y; - Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(), + Point defaultWallpaperSize = BitmapUtils.getDefaultWallpaperSize(getResources(), getWindowManager()); // Get the crop RectF cropRect = mCropView.getCrop(); @@ -452,372 +331,6 @@ public class WallpaperCropActivity extends Activity { cropTask.execute(); } - public interface OnBitmapCroppedHandler { - public void onBitmapCropped(byte[] imageBytes); - } - - protected static class BitmapCropTask extends AsyncTask { - Uri mInUri = null; - Context mContext; - String mInFilePath; - byte[] mInImageBytes; - int mInResId = 0; - RectF mCropBounds = null; - int mOutWidth, mOutHeight; - int mRotation; - String mOutputFormat = "jpg"; // for now - boolean mSetWallpaper; - boolean mSaveCroppedBitmap; - Bitmap mCroppedBitmap; - Runnable mOnEndRunnable; - Resources mResources; - OnBitmapCroppedHandler mOnBitmapCroppedHandler; - boolean mNoCrop; - - public BitmapCropTask(Context c, String filePath, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { - mContext = c; - mInFilePath = filePath; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); - } - - public BitmapCropTask(byte[] imageBytes, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { - mInImageBytes = imageBytes; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); - } - - public BitmapCropTask(Context c, Uri inUri, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { - mContext = c; - mInUri = inUri; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); - } - - public BitmapCropTask(Context c, Resources res, int inResId, - RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { - mContext = c; - mInResId = inResId; - mResources = res; - init(cropBounds, rotation, - outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); - } - - private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, - boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { - mCropBounds = cropBounds; - mRotation = rotation; - mOutWidth = outWidth; - mOutHeight = outHeight; - mSetWallpaper = setWallpaper; - mSaveCroppedBitmap = saveCroppedBitmap; - mOnEndRunnable = onEndRunnable; - } - - public void setOnBitmapCropped(OnBitmapCroppedHandler handler) { - mOnBitmapCroppedHandler = handler; - } - - public void setNoCrop(boolean value) { - mNoCrop = value; - } - - public void setOnEndRunnable(Runnable onEndRunnable) { - mOnEndRunnable = onEndRunnable; - } - - // Helper to setup input stream - private InputStream regenerateInputStream() { - if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { - Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + - "image byte array given"); - } else { - try { - if (mInUri != null) { - return new BufferedInputStream( - mContext.getContentResolver().openInputStream(mInUri)); - } else if (mInFilePath != null) { - return mContext.openFileInput(mInFilePath); - } else if (mInImageBytes != null) { - return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes)); - } else { - return new BufferedInputStream(mResources.openRawResource(mInResId)); - } - } catch (FileNotFoundException e) { - Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); - } - } - return null; - } - - public Point getImageBounds() { - InputStream is = regenerateInputStream(); - if (is != null) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(is, null, options); - Utils.closeSilently(is); - if (options.outWidth != 0 && options.outHeight != 0) { - return new Point(options.outWidth, options.outHeight); - } - } - return null; - } - - public void setCropBounds(RectF cropBounds) { - mCropBounds = cropBounds; - } - - public Bitmap getCroppedBitmap() { - return mCroppedBitmap; - } - public boolean cropBitmap() { - boolean failure = false; - - - WallpaperManager wallpaperManager = null; - if (mSetWallpaper) { - wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); - } - - - if (mSetWallpaper && mNoCrop) { - try { - InputStream is = regenerateInputStream(); - if (is != null) { - wallpaperManager.setStream(is); - Utils.closeSilently(is); - } - } catch (IOException e) { - Log.w(LOGTAG, "cannot write stream to wallpaper", e); - failure = true; - } - return !failure; - } else { - // Find crop bounds (scaled to original image size) - Rect roundedTrueCrop = new Rect(); - Matrix rotateMatrix = new Matrix(); - Matrix inverseRotateMatrix = new Matrix(); - - Point bounds = getImageBounds(); - if (mRotation > 0) { - rotateMatrix.setRotate(mRotation); - inverseRotateMatrix.setRotate(-mRotation); - - mCropBounds.roundOut(roundedTrueCrop); - mCropBounds = new RectF(roundedTrueCrop); - - if (bounds == null) { - Log.w(LOGTAG, "cannot get bounds for image"); - failure = true; - return false; - } - - float[] rotatedBounds = new float[] { bounds.x, bounds.y }; - rotateMatrix.mapPoints(rotatedBounds); - rotatedBounds[0] = Math.abs(rotatedBounds[0]); - rotatedBounds[1] = Math.abs(rotatedBounds[1]); - - mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); - inverseRotateMatrix.mapRect(mCropBounds); - mCropBounds.offset(bounds.x/2, bounds.y/2); - - } - - mCropBounds.roundOut(roundedTrueCrop); - - if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { - Log.w(LOGTAG, "crop has bad values for full size image"); - failure = true; - return false; - } - - // See how much we're reducing the size of the image - int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth, - roundedTrueCrop.height() / mOutHeight)); - // Attempt to open a region decoder - BitmapRegionDecoder decoder = null; - InputStream is = null; - try { - is = regenerateInputStream(); - if (is == null) { - Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString()); - failure = true; - return false; - } - decoder = BitmapRegionDecoder.newInstance(is, false); - Utils.closeSilently(is); - } catch (IOException e) { - Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); - } finally { - Utils.closeSilently(is); - is = null; - } - - Bitmap crop = null; - if (decoder != null) { - // Do region decoding to get crop bitmap - BitmapFactory.Options options = new BitmapFactory.Options(); - if (scaleDownSampleSize > 1) { - options.inSampleSize = scaleDownSampleSize; - } - crop = decoder.decodeRegion(roundedTrueCrop, options); - decoder.recycle(); - } - - if (crop == null) { - // BitmapRegionDecoder has failed, try to crop in-memory - is = regenerateInputStream(); - Bitmap fullSize = null; - if (is != null) { - BitmapFactory.Options options = new BitmapFactory.Options(); - if (scaleDownSampleSize > 1) { - options.inSampleSize = scaleDownSampleSize; - } - fullSize = BitmapFactory.decodeStream(is, null, options); - Utils.closeSilently(is); - } - if (fullSize != null) { - // Find out the true sample size that was used by the decoder - scaleDownSampleSize = bounds.x / fullSize.getWidth(); - mCropBounds.left /= scaleDownSampleSize; - mCropBounds.top /= scaleDownSampleSize; - mCropBounds.bottom /= scaleDownSampleSize; - mCropBounds.right /= scaleDownSampleSize; - mCropBounds.roundOut(roundedTrueCrop); - - // Adjust values to account for issues related to rounding - if (roundedTrueCrop.width() > fullSize.getWidth()) { - // Adjust the width - roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth(); - } - if (roundedTrueCrop.right > fullSize.getWidth()) { - // Adjust the left value - int adjustment = roundedTrueCrop.left - - Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width()); - roundedTrueCrop.left -= adjustment; - roundedTrueCrop.right -= adjustment; - } - if (roundedTrueCrop.height() > fullSize.getHeight()) { - // Adjust the height - roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight(); - } - if (roundedTrueCrop.bottom > fullSize.getHeight()) { - // Adjust the top value - int adjustment = roundedTrueCrop.top - - Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height()); - roundedTrueCrop.top -= adjustment; - roundedTrueCrop.bottom -= adjustment; - } - - crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, - roundedTrueCrop.top, roundedTrueCrop.width(), - roundedTrueCrop.height()); - } - } - - if (crop == null) { - Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); - failure = true; - return false; - } - if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { - float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; - rotateMatrix.mapPoints(dimsAfter); - dimsAfter[0] = Math.abs(dimsAfter[0]); - dimsAfter[1] = Math.abs(dimsAfter[1]); - - if (!(mOutWidth > 0 && mOutHeight > 0)) { - mOutWidth = Math.round(dimsAfter[0]); - mOutHeight = Math.round(dimsAfter[1]); - } - - RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); - RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); - - Matrix m = new Matrix(); - if (mRotation == 0) { - m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); - } else { - Matrix m1 = new Matrix(); - m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); - Matrix m2 = new Matrix(); - m2.setRotate(mRotation); - Matrix m3 = new Matrix(); - m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); - Matrix m4 = new Matrix(); - m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); - - Matrix c1 = new Matrix(); - c1.setConcat(m2, m1); - Matrix c2 = new Matrix(); - c2.setConcat(m4, m3); - m.setConcat(c2, c1); - } - - Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), - (int) returnRect.height(), Bitmap.Config.ARGB_8888); - if (tmp != null) { - Canvas c = new Canvas(tmp); - Paint p = new Paint(); - p.setFilterBitmap(true); - c.drawBitmap(crop, m, p); - crop = tmp; - } - } - - if (mSaveCroppedBitmap) { - mCroppedBitmap = crop; - } - - // Get output compression format - CompressFormat cf = - convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); - - // Compress to byte array - ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); - if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { - // If we need to set to the wallpaper, set it - if (mSetWallpaper && wallpaperManager != null) { - try { - byte[] outByteArray = tmpOut.toByteArray(); - wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); - if (mOnBitmapCroppedHandler != null) { - mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); - } - } catch (IOException e) { - Log.w(LOGTAG, "cannot write stream to wallpaper", e); - failure = true; - } - } - } else { - Log.w(LOGTAG, "cannot compress bitmap"); - failure = true; - } - } - return !failure; // True if any of the operations failed - } - - @Override - protected Boolean doInBackground(Void... params) { - return cropBitmap(); - } - - @Override - protected void onPostExecute(Boolean result) { - if (mOnEndRunnable != null) { - mOnEndRunnable.run(); - } - } - } - protected void updateWallpaperDimensions(int width, int height) { String spKey = getSharedPreferencesKey(); SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); @@ -835,11 +348,11 @@ public class WallpaperCropActivity extends Activity { sp, getWindowManager(), WallpaperManager.getInstance(this), true); } - static public void suggestWallpaperDimension(Resources res, + public static void suggestWallpaperDimension(Resources res, final SharedPreferences sharedPrefs, WindowManager windowManager, final WallpaperManager wallpaperManager, boolean fallBackToDefaults) { - final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager); + final Point defaultWallpaperSize = BitmapUtils.getDefaultWallpaperSize(res, windowManager); // If we have saved a wallpaper width/height, use that instead int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1); @@ -859,40 +372,4 @@ public class WallpaperCropActivity extends Activity { wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); } } - - protected static RectF getMaxCropRect( - int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { - RectF cropRect = new RectF(); - // Get a crop rect that will fit this - if (inWidth / (float) inHeight > outWidth / (float) outHeight) { - cropRect.top = 0; - cropRect.bottom = inHeight; - cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; - cropRect.right = inWidth - cropRect.left; - if (leftAligned) { - cropRect.right -= cropRect.left; - cropRect.left = 0; - } - } else { - cropRect.left = 0; - cropRect.right = inWidth; - cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; - cropRect.bottom = inHeight - cropRect.top; - } - return cropRect; - } - - protected static CompressFormat convertExtensionToCompressFormat(String extension) { - return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; - } - - protected static String getFileExtension(String requestFormat) { - String outputFormat = (requestFormat == null) - ? "jpg" - : requestFormat; - outputFormat = outputFormat.toLowerCase(); - return (outputFormat.equals("png") || outputFormat.equals("gif")) - ? "png" // We don't support gif compression. - : "jpg"; - } } diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 043f6306b..53cc13dbc 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -70,6 +70,9 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; +import com.android.gallery3d.common.BitmapCropTask; +import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.common.Utils; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; @@ -174,7 +177,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onSave(final WallpaperPickerActivity a) { boolean finishActivityWhenDone = true; - OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() { + BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() { public void onBitmapCropped(byte[] imageBytes) { Point thumbSize = getDefaultThumbnailSize(a.getResources()); // rotation is set to 0 since imageBytes has already been correctly rotated @@ -240,9 +243,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource); CropView v = a.getCropView(); v.setTileSource(source, null); - Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize( + Point wallpaperSize = BitmapUtils.getDefaultWallpaperSize( a.getResources(), a.getWindowManager()); - RectF crop = WallpaperCropActivity.getMaxCropRect( + RectF crop = Utils.getMaxCropRect( source.getImageWidth(), source.getImageHeight(), wallpaperSize.x, wallpaperSize.y, false); v.setScale(wallpaperSize.x / crop.width()); @@ -811,7 +814,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { rotatedBounds[0] = Math.abs(rotatedBounds[0]); rotatedBounds[1] = Math.abs(rotatedBounds[1]); - RectF cropRect = WallpaperCropActivity.getMaxCropRect( + RectF cropRect = Utils.getMaxCropRect( (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned); cropTask.setCropBounds(cropRect); @@ -838,7 +841,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { new AsyncTask() { protected Bitmap doInBackground(Void...args) { try { - int rotation = WallpaperCropActivity.getRotationFromExif(context, uri); + int rotation = BitmapUtils.getRotationFromExif(context, uri); return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false); } catch (SecurityException securityException) { if (isDestroyed()) { @@ -1023,7 +1026,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } else { Resources res = getResources(); Point defaultThumbSize = getDefaultThumbnailSize(res); - int rotation = WallpaperCropActivity.getRotationFromExif(res, resId); + int rotation = BitmapUtils.getRotationFromExif(res, resId); thumb = createThumbnail( defaultThumbSize, this, null, null, sysRes, resId, rotation, false); if (thumb != null) { diff --git a/WallpaperPicker/src/com/android/photos/views/Pools.java b/WallpaperPicker/src/com/android/photos/views/Pools.java new file mode 100644 index 000000000..c60f2f013 --- /dev/null +++ b/WallpaperPicker/src/com/android/photos/views/Pools.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 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.photos.views; + +/** + * Helper class for crating pools of objects. An example use looks like this: + *
+ * public class MyPooledClass {
+ *
+ *     private static final SynchronizedPool sPool =
+ *             new SynchronizedPool(10);
+ *
+ *     public static MyPooledClass obtain() {
+ *         MyPooledClass instance = sPool.acquire();
+ *         return (instance != null) ? instance : new MyPooledClass();
+ *     }
+ *
+ *     public void recycle() {
+ *          // Clear state if needed.
+ *          sPool.release(this);
+ *     }
+ *
+ *     . . .
+ * }
+ * 
+ * + * @hide + */ +public final class Pools { + + /** + * Interface for managing a pool of objects. + * + * @param The pooled type. + */ + public static interface Pool { + + /** + * @return An instance from the pool if such, null otherwise. + */ + public T acquire(); + + /** + * Release an instance to the pool. + * + * @param instance The instance to release. + * @return Whether the instance was put in the pool. + * + * @throws IllegalStateException If the instance is already in the pool. + */ + public boolean release(T instance); + } + + private Pools() { + /* do nothing - hiding constructor */ + } + + /** + * Simple (non-synchronized) pool of objects. + * + * @param The pooled type. + */ + public static class SimplePool implements Pool { + private final Object[] mPool; + + private int mPoolSize; + + /** + * Creates a new instance. + * + * @param maxPoolSize The max pool size. + * + * @throws IllegalArgumentException If the max pool size is less than zero. + */ + public SimplePool(int maxPoolSize) { + if (maxPoolSize <= 0) { + throw new IllegalArgumentException("The max pool size must be > 0"); + } + mPool = new Object[maxPoolSize]; + } + + @Override + @SuppressWarnings("unchecked") + public T acquire() { + if (mPoolSize > 0) { + final int lastPooledIndex = mPoolSize - 1; + T instance = (T) mPool[lastPooledIndex]; + mPool[lastPooledIndex] = null; + mPoolSize--; + return instance; + } + return null; + } + + @Override + public boolean release(T instance) { + if (isInPool(instance)) { + throw new IllegalStateException("Already in the pool!"); + } + if (mPoolSize < mPool.length) { + mPool[mPoolSize] = instance; + mPoolSize++; + return true; + } + return false; + } + + private boolean isInPool(T instance) { + for (int i = 0; i < mPoolSize; i++) { + if (mPool[i] == instance) { + return true; + } + } + return false; + } + } + + /** + * Synchronized) pool of objects. + * + * @param The pooled type. + */ + public static class SynchronizedPool extends SimplePool { + private final Object mLock = new Object(); + + /** + * Creates a new instance. + * + * @param maxPoolSize The max pool size. + * + * @throws IllegalArgumentException If the max pool size is less than zero. + */ + public SynchronizedPool(int maxPoolSize) { + super(maxPoolSize); + } + + @Override + public T acquire() { + synchronized (mLock) { + return super.acquire(); + } + } + + @Override + public boolean release(T element) { + synchronized (mLock) { + return super.release(element); + } + } + } +} \ No newline at end of file diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java index b0292e660..f9b7ab473 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java @@ -23,8 +23,6 @@ import android.graphics.RectF; import android.util.DisplayMetrics; import android.util.Log; import android.util.LongSparseArray; -import android.util.Pools.Pool; -import android.util.Pools.SynchronizedPool; import android.view.View; import android.view.WindowManager; @@ -32,6 +30,8 @@ import com.android.gallery3d.common.Utils; import com.android.gallery3d.glrenderer.BasicTexture; import com.android.gallery3d.glrenderer.GLCanvas; import com.android.gallery3d.glrenderer.UploadedTexture; +import com.android.photos.views.Pools.Pool; +import com.android.photos.views.Pools.SynchronizedPool; /** * Handles laying out, decoding, and drawing of tiles in GL -- cgit v1.2.3 From 80e6beb48ca71a321f667e79fe35a7ab2fb4c2bf Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 13 Feb 2015 16:14:33 -0800 Subject: Ensure that layers are properly removed after animation Bug 19243980 Change-Id: Ic1d54f92051f7d937878898cae210ec18ecbaff8 --- src/com/android/launcher3/Launcher.java | 60 +++++++++++++++++++------------- src/com/android/launcher3/Workspace.java | 35 ++++++++++++------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 84476b7cc..eb2b5b803 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -309,6 +309,9 @@ public class Launcher extends Activity private View.OnTouchListener mHapticFeedbackTouchListener; + public static final int BUILD_LAYER = 0; + public static final int BUILD_AND_SET_LAYER = 1; + // Related to the auto-advancing of widgets private final int ADVANCE_MSG = 1; private final int mAdvanceInterval = 20000; @@ -3315,7 +3318,7 @@ public class Launcher extends Activity final View fromView = mWorkspace; final AppsCustomizeTabHost toView = mAppsCustomizeTabHost; - final ArrayList layerViews = new ArrayList(); + final HashMap layerViews = new HashMap(); Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ? Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN; @@ -3375,8 +3378,7 @@ public class Launcher extends Activity } final float initAlpha = alpha; - revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - layerViews.add(revealView); + layerViews.put(revealView, BUILD_AND_SET_LAYER); PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f); PropertyValuesHolder panelDriftY = PropertyValuesHolder.ofFloat("translationY", yDrift, 0); @@ -3393,8 +3395,7 @@ public class Launcher extends Activity if (page != null) { page.setVisibility(View.VISIBLE); - page.setLayerType(View.LAYER_TYPE_HARDWARE, null); - layerViews.add(page); + layerViews.put(page, BUILD_AND_SET_LAYER); ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0); page.setTranslationY(yDrift); @@ -3450,9 +3451,11 @@ public class Launcher extends Activity dispatchOnLauncherTransitionEnd(toView, animated, false); revealView.setVisibility(View.INVISIBLE); - revealView.setLayerType(View.LAYER_TYPE_NONE, null); - if (page != null) { - page.setLayerType(View.LAYER_TYPE_NONE, null); + + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } } content.setPageBackgroundsVisible(true); @@ -3484,12 +3487,16 @@ public class Launcher extends Activity dispatchOnLauncherTransitionStart(toView, animated, false); revealView.setAlpha(initAlpha); + + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + } + if (Utilities.isLmpOrAbove()) { - for (int i = 0; i < layerViews.size(); i++) { - View v = layerViews.get(i); - if (v != null) { - if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); - } + for (View v : layerViews.keySet()) { + if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); } } mStateAnimation.start(); @@ -3545,7 +3552,7 @@ public class Launcher extends Activity final View fromView = mAppsCustomizeTabHost; final View toView = mWorkspace; Animator workspaceAnim = null; - final ArrayList layerViews = new ArrayList(); + final HashMap layerViews = new HashMap(); if (toState == Workspace.State.NORMAL) { workspaceAnim = mWorkspace.getChangeStateAnimation( @@ -3617,7 +3624,7 @@ public class Launcher extends Activity xDrift = 0; } - revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + layerViews.put(revealView, BUILD_AND_SET_LAYER); TimeInterpolator decelerateInterpolator = material ? new LogDecelerateInterpolator(100, 0) : new DecelerateInterpolator(1f); @@ -3651,7 +3658,7 @@ public class Launcher extends Activity } if (page != null) { - page.setLayerType(View.LAYER_TYPE_HARDWARE, null); + layerViews.put(page, BUILD_AND_SET_LAYER); ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY", 0, yDrift); @@ -3719,10 +3726,12 @@ public class Launcher extends Activity onCompleteRunnable.run(); } - revealView.setLayerType(View.LAYER_TYPE_NONE, null); - if (page != null) { - page.setLayerType(View.LAYER_TYPE_NONE, null); + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } } + content.setPageBackgroundsVisible(true); // Unhide side pages int count = content.getChildCount(); @@ -3756,12 +3765,15 @@ public class Launcher extends Activity dispatchOnLauncherTransitionStart(fromView, animated, false); dispatchOnLauncherTransitionStart(toView, animated, false); + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + } + if (Utilities.isLmpOrAbove()) { - for (int i = 0; i < layerViews.size(); i++) { - View v = layerViews.get(i); - if (v != null) { - if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); - } + for (View v : layerViews.keySet()) { + if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); } } mStateAnimation.start(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 44d77571b..402172717 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2091,7 +2091,7 @@ public class Workspace extends SmoothPagedView } Animator getChangeStateAnimation(final State state, boolean animated, - ArrayList layerViews) { + HashMap layerViews) { return getChangeStateAnimation(state, animated, 0, -1, layerViews); } @@ -2222,7 +2222,7 @@ public class Workspace extends SmoothPagedView } Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage, - ArrayList layerViews) { + HashMap layerViews) { if (mState == state) { return null; } @@ -2352,7 +2352,7 @@ public class Workspace extends SmoothPagedView cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); } else { if (layerViews != null) { - layerViews.add(cl); + layerViews.put(cl, Launcher.BUILD_LAYER); } if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { LauncherViewPropertyAnimator alphaAnim = @@ -2389,12 +2389,12 @@ public class Workspace extends SmoothPagedView pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0); } - Animator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) - .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); + LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) + .alpha(finalHotseatAndPageIndicatorAlpha); hotseatAlpha.addListener(new AlphaUpdateListener(hotseat)); - Animator overviewPanelAlpha = new LauncherViewPropertyAnimator(overviewPanel) - .alpha(finalOverviewPanelAlpha).withLayer(); + LauncherViewPropertyAnimator overviewPanelAlpha = + new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha); overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel)); // For animation optimations, we may need to provide the Launcher transition @@ -2402,8 +2402,14 @@ public class Workspace extends SmoothPagedView hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null); overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); if (layerViews != null) { - layerViews.add(hotseat); - layerViews.add(overviewPanel); + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(hotseat, Launcher.BUILD_AND_SET_LAYER); + layerViews.put(overviewPanel, Launcher.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + hotseatAlpha.withLayer(); + overviewPanelAlpha.withLayer(); } if (workspaceToOverview) { @@ -2421,12 +2427,17 @@ public class Workspace extends SmoothPagedView hotseatAlpha.setDuration(duration); if (searchBar != null) { - Animator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) - .alpha(finalSearchBarAlpha).withLayer(); + LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) + .alpha(finalSearchBarAlpha); searchBarAlpha.addListener(new AlphaUpdateListener(searchBar)); searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); if (layerViews != null) { - layerViews.add(searchBar); + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(searchBar, Launcher.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + searchBarAlpha.withLayer(); } searchBarAlpha.setDuration(duration); anim.play(searchBarAlpha); -- cgit v1.2.3 From 2434d40ef271966096fea707ac4207d80d1b2901 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 17 Feb 2015 11:44:15 -0800 Subject: Fixing crash when dragging and dropping a restored widget. > When the widget is in pending state, AppWidgetProviderInfo is not available Bug: 19364311 Change-Id: If3126a375853e6e710c6eba4b8824e18f3f0d391 --- src/com/android/launcher3/LauncherAppWidgetHostView.java | 4 ++-- src/com/android/launcher3/Workspace.java | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 2d04df2de..954d2d711 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -145,9 +145,9 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc @Override public AppWidgetProviderInfo getAppWidgetInfo() { AppWidgetProviderInfo info = super.getAppWidgetInfo(); - if (!(info instanceof LauncherAppWidgetProviderInfo)) { + if (info != null && !(info instanceof LauncherAppWidgetProviderInfo)) { throw new IllegalStateException("Launcher widget must have" - + "LauncherAppWidgetProviderInfo"); + + " LauncherAppWidgetProviderInfo"); } return info; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 44d77571b..69fa457ab 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3166,9 +3166,8 @@ public class Workspace extends SmoothPagedView // in its final location final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; - LauncherAppWidgetProviderInfo pInfo = (LauncherAppWidgetProviderInfo) - hostView.getAppWidgetInfo(); - if (pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { + AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo(); + if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { final Runnable addResizeFrame = new Runnable() { public void run() { DragLayer dragLayer = mLauncher.getDragLayer(); -- cgit v1.2.3 From a8f996d03982d76f59b2694fe9446d2a30eae97a Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Tue, 17 Feb 2015 12:56:04 -0800 Subject: Add clip-reveal animation to Launcher ClipReveal animation was recently added to the platform. This makes that animation the default launch animation when running on that version of the platform. Issue #19362772 Better material launch animations Change-Id: I077659c14dc426be465d163bea6ba0fa93a637c5 --- src/com/android/launcher3/Launcher.java | 54 +++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7e2ae4ede..58b085480 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -95,6 +95,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.TextView; import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; @@ -115,6 +116,9 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; @@ -355,6 +359,18 @@ public class Launcher extends Activity } } + // TODO: remove this field and call method directly when Launcher3 can depend on M APIs + private static Method sClipRevealMethod = null; + static { + Class activityOptionsClass = ActivityOptions.class; + try { + sClipRevealMethod = activityOptionsClass.getDeclaredMethod("makeClipRevealAnimation", + View.class, int.class, int.class, int.class, int.class); + } catch (Exception e) { + // Earlier version + } + } + private Runnable mBuildLayersRunnable = new Runnable() { public void run() { if (mWorkspace != null) { @@ -2893,9 +2909,41 @@ public class Launcher extends Activity Bundle optsBundle = null; if (useLaunchAnimation) { - ActivityOptions opts = Utilities.isLmpOrAbove() ? - ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) : - ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); + ActivityOptions opts = null; + if (sClipRevealMethod != null) { + // TODO: call method directly when Launcher3 can depend on M APIs + int left = 0, top = 0; + int width = v.getMeasuredWidth(), height = v.getMeasuredHeight(); + if (v instanceof TextView) { + // Launch from center of icon, not entire view + TextView tv = (TextView) v; + Drawable[] drawables = tv.getCompoundDrawables(); + if (drawables != null && drawables[1] != null) { + Rect bounds = drawables[1].getBounds(); + left = (width - bounds.width()) / 2; + top = tv.getPaddingTop(); + width = bounds.width(); + height = bounds.height(); + } + } + try { + opts = (ActivityOptions) sClipRevealMethod.invoke(null, v, + left, top, width, height); + } catch (IllegalAccessException e) { + Log.d(TAG, "Could not call makeClipRevealAnimation: " + e); + sClipRevealMethod = null; + } catch (InvocationTargetException e) { + Log.d(TAG, "Could not call makeClipRevealAnimation: " + e); + sClipRevealMethod = null; + } + } + if (opts == null) { + opts = Utilities.isLmpOrAbove() ? + ActivityOptions.makeCustomAnimation(this, + R.anim.task_open_enter, R.anim.no_anim) : + ActivityOptions.makeScaleUpAnimation(v, 0, 0, + v.getMeasuredWidth(), v.getMeasuredHeight()); + } optsBundle = opts.toBundle(); } -- cgit v1.2.3 From 87cf88393cedb66633bbcf51c283d525ef88932f Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 18 Feb 2015 17:26:22 -0800 Subject: Mark WIDGET_CATEGORY_HOME and WIDGET_CATEGORY_SEARCHBOX on the search widget Bug 17334589 Change-Id: I0e14381cfc19822451e68eb7f070d94525e126c3 --- src/com/android/launcher3/Launcher.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 61915b755..0fb8f4df1 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3910,8 +3910,11 @@ public class Launcher extends Activity } Bundle opts = new Bundle(); + // We indicate that this widget is both acting as a search box affordance as well + // as being present on the home screen. opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); + AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX | + AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); SharedPreferences sp = getSharedPreferences( LauncherAppState.getSharedPreferencesKey(), MODE_PRIVATE); -- cgit v1.2.3 From eb8e282a77279c7657f49a7391c41270d4997379 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 20 Feb 2015 16:38:48 +0000 Subject: Revert "Mark WIDGET_CATEGORY_HOME and WIDGET_CATEGORY_SEARCHBOX on the search widget" This reverts commit 87cf88393cedb66633bbcf51c283d525ef88932f. Change-Id: Ie15c71f76c71dfb6b85aefcc10cd66eb49bea6dc --- src/com/android/launcher3/Launcher.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0fb8f4df1..61915b755 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3910,11 +3910,8 @@ public class Launcher extends Activity } Bundle opts = new Bundle(); - // We indicate that this widget is both acting as a search box affordance as well - // as being present on the home screen. opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX | - AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); + AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); SharedPreferences sp = getSharedPreferences( LauncherAppState.getSharedPreferencesKey(), MODE_PRIVATE); -- cgit v1.2.3 From ee3e6a7b777e58552a26ab8a10641886588e9196 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 20 Feb 2015 14:25:27 -0800 Subject: [key event focus handling] Cleanup/Refactor/Feature 1) Focus navigation handling is refactored to Focus utility class. New 2 step dpad navigation algorithm is inside Focus class 2) Introduced a map (or matrix) that indicates where sparse icons are located inside a grid. This enables getting rid of the icon sorting logic which was costly. 3) Unified all the dpad handling logic inside the handleXXKeyEvent methods 4) DOWN/UP key will allow navigation between workspace icons and the hotseat 5) Folder icons allow DOWN/UP to navigate to the title b/19381790 Change-Id: Id45b3f215ef7c1ca5f99b08e3d721e219298627a --- src/com/android/launcher3/CellLayout.java | 4 +- src/com/android/launcher3/FocusHelper.java | 826 +++++++++---------------- src/com/android/launcher3/util/FocusLogic.java | 434 +++++++++++++ 3 files changed, 732 insertions(+), 532 deletions(-) create mode 100644 src/com/android/launcher3/util/FocusLogic.java diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index e6865b2e6..72e28918f 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -578,11 +578,11 @@ public class CellLayout extends ViewGroup { mInterceptTouchListener = listener; } - int getCountX() { + public int getCountX() { return mCountX; } - int getCountY() { + public int getCountY() { return mCountY; } diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index e60704718..c02d73cb6 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * 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. @@ -17,20 +17,20 @@ package com.android.launcher3; import android.content.res.Configuration; +import android.util.Log; import android.view.KeyEvent; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; import android.widget.ScrollView; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import com.android.launcher3.util.FocusLogic; /** * A keyboard listener we set on all the workspace icons. */ class IconKeyEventListener implements View.OnKeyListener { + @Override public boolean onKey(View v, int keyCode, KeyEvent event) { return FocusHelper.handleIconKeyEvent(v, keyCode, event); } @@ -40,6 +40,7 @@ class IconKeyEventListener implements View.OnKeyListener { * A keyboard listener we set on all the workspace icons. */ class FolderKeyEventListener implements View.OnKeyListener { + @Override public boolean onKey(View v, int keyCode, KeyEvent event) { return FocusHelper.handleFolderKeyEvent(v, keyCode, event); } @@ -49,30 +50,35 @@ class FolderKeyEventListener implements View.OnKeyListener { * A keyboard listener we set on all the hotseat buttons. */ class HotseatIconKeyEventListener implements View.OnKeyListener { + @Override public boolean onKey(View v, int keyCode, KeyEvent event) { - final Configuration configuration = v.getResources().getConfiguration(); - return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event, configuration.orientation); + return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event); } } public class FocusHelper { - /** - * Returns the Viewgroup containing page contents for the page at the index specified. - */ - private static ViewGroup getAppsCustomizePage(ViewGroup container, int index) { - ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index); - if (page instanceof CellLayout) { - // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren - page = ((CellLayout) page).getShortcutsAndWidgets(); - } - return page; - } + private static final String TAG = "FocusHelper"; + private static final boolean DEBUG = false; + + // + // Key code handling methods. + // /** - * Handles key events in a PageViewCellLayout containing PagedViewIcons. + * Handles key events in the all apps screen. */ static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) { + boolean consume = FocusLogic.shouldConsume(keyCode); + if (e.getAction() == KeyEvent.ACTION_UP) { + return consume; + } + if (DEBUG) { + Log.v(TAG, String.format("Handle ALL APPS keyevent=[%s].", + KeyEvent.keyCodeToString(keyCode))); + } + + // Initialize variables. ViewGroup parentLayout; ViewGroup itemContainer; int countX; @@ -82,341 +88,159 @@ public class FocusHelper { parentLayout = (ViewGroup) itemContainer.getParent(); countX = ((CellLayout) parentLayout).getCountX(); countY = ((CellLayout) parentLayout).getCountY(); - } else { + } else if (v.getParent() instanceof ViewGroup) { itemContainer = parentLayout = (ViewGroup) v.getParent(); countX = ((PagedViewGridLayout) parentLayout).getCellCountX(); countY = ((PagedViewGridLayout) parentLayout).getCellCountY(); + } else { + throw new IllegalStateException( + "Parent of the focused item inside all apps screen is not a supported type."); } - - // Note we have an extra parent because of the - // PagedViewCellLayout/PagedViewCellLayoutChildren relationship - final PagedView container = (PagedView) parentLayout.getParent(); final int iconIndex = itemContainer.indexOfChild(v); - final int itemCount = itemContainer.getChildCount(); - final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parentLayout)); + final PagedView container = (PagedView) parentLayout.getParent(); + final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout)); final int pageCount = container.getChildCount(); - - final int x = iconIndex % countX; - final int y = iconIndex / countX; - - final int action = e.getAction(); - final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP); ViewGroup newParent = null; - // Side pages do not always load synchronously, so check before focusing child siblings - // willy-nilly View child = null; - boolean wasHandled = false; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (handleKeyEvent) { - // Select the previous icon or the last icon on the previous page - if (iconIndex > 0) { - itemContainer.getChildAt(iconIndex - 1).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } else { - if (pageIndex > 0) { - newParent = getAppsCustomizePage(container, pageIndex - 1); - if (newParent != null) { - container.snapToPage(pageIndex - 1); - child = newParent.getChildAt(newParent.getChildCount() - 1); - if (child != null) { - child.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } - } - } - } + int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true); + + // Process focus. + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, + iconIndex, pageIndex, pageCount); + if (newIconIndex == FocusLogic.NOOP) { + return consume; + } + switch (newIconIndex) { + case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: + newParent = getAppsCustomizePage(container, pageIndex - 1); + if (newParent != null) { + container.snapToPage(pageIndex - 1); + child = newParent.getChildAt(0); } - wasHandled = true; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (handleKeyEvent) { - // Select the next icon or the first icon on the next page - if (iconIndex < (itemCount - 1)) { - itemContainer.getChildAt(iconIndex + 1).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); - } else { - if (pageIndex < (pageCount - 1)) { - newParent = getAppsCustomizePage(container, pageIndex + 1); - if (newParent != null) { - container.snapToPage(pageIndex + 1); - child = newParent.getChildAt(0); - if (child != null) { - child.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); - } - } - } - } + case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: + newParent = getAppsCustomizePage(container, pageIndex - 1); + if (newParent != null) { + container.snapToPage(pageIndex - 1); + child = newParent.getChildAt(newParent.getChildCount() - 1); } - wasHandled = true; break; - case KeyEvent.KEYCODE_DPAD_UP: - if (handleKeyEvent) { - // Select the closest icon in the previous row, otherwise select the tab bar - if (y > 0) { - int newiconIndex = ((y - 1) * countX) + x; - itemContainer.getChildAt(newiconIndex).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } + case FocusLogic.NEXT_PAGE_FIRST_ITEM: + newParent = getAppsCustomizePage(container, pageIndex + 1); + if (newParent != null) { + container.snapToPage(pageIndex + 1); + child = newParent.getChildAt(0); } - wasHandled = true; break; - case KeyEvent.KEYCODE_DPAD_DOWN: - if (handleKeyEvent) { - // Select the closest icon in the next row, otherwise do nothing - if (y < (countY - 1)) { - int newiconIndex = Math.min(itemCount - 1, ((y + 1) * countX) + x); - int newIconY = newiconIndex / countX; - if (newIconY != y) { - itemContainer.getChildAt(newiconIndex).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - } - } - wasHandled = true; + case FocusLogic.CURRENT_PAGE_FIRST_ITEM: + child = container.getChildAt(0); break; - case KeyEvent.KEYCODE_PAGE_UP: - if (handleKeyEvent) { - // Select the first icon on the previous page, or the first icon on this page - // if there is no previous page - if (pageIndex > 0) { - newParent = getAppsCustomizePage(container, pageIndex - 1); - if (newParent != null) { - container.snapToPage(pageIndex - 1); - child = newParent.getChildAt(0); - if (child != null) { - child.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - } - } else { - itemContainer.getChildAt(0).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_PAGE_DOWN: - if (handleKeyEvent) { - // Select the first icon on the next page, or the last icon on this page - // if there is no next page - if (pageIndex < (pageCount - 1)) { - newParent = getAppsCustomizePage(container, pageIndex + 1); - if (newParent != null) { - container.snapToPage(pageIndex + 1); - child = newParent.getChildAt(0); - if (child != null) { - child.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - } - } else { - itemContainer.getChildAt(itemCount - 1).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_MOVE_HOME: - if (handleKeyEvent) { - // Select the first icon on this page - itemContainer.getChildAt(0).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - wasHandled = true; + case FocusLogic.CURRENT_PAGE_LAST_ITEM: + child = itemContainer.getChildAt(itemContainer.getChildCount() - 1); break; - case KeyEvent.KEYCODE_MOVE_END: - if (handleKeyEvent) { - // Select the last icon on this page - itemContainer.getChildAt(itemCount - 1).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - wasHandled = true; + default: // Go to some item on the current page. + child = itemContainer.getChildAt(newIconIndex); break; - default: break; } - return wasHandled; + if (child != null) { + child.requestFocus(); + playSoundEffect(keyCode, v); + } + return consume; } /** - * Handles key events in the workspace hotseat (bottom of the screen). + * Handles key events in the workspace hot seat (bottom of the screen). + *

Currently we don't special case for the phone UI in different orientations, even though + * the hotseat is on the side in landscape mode. This is to ensure that accessibility + * consistency is maintained across rotations. */ - static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e, int orientation) { - ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); - final CellLayout layout = (CellLayout) parent.getParent(); + static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) { + boolean consume = FocusLogic.shouldConsume(keyCode); + if (e.getAction() == KeyEvent.ACTION_UP || !consume) { + return consume; + } + int orientation = v.getResources().getConfiguration().orientation; - // NOTE: currently we don't special case for the phone UI in different - // orientations, even though the hotseat is on the side in landscape mode. This - // is to ensure that accessibility consistency is maintained across rotations. - final int action = e.getAction(); - final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP); - boolean wasHandled = false; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (handleKeyEvent) { - ArrayList views = getCellLayoutChildrenSortedSpatially(layout, parent); - int myIndex = views.indexOf(v); - // Select the previous button, otherwise do nothing - if (myIndex > 0) { - views.get(myIndex - 1).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (handleKeyEvent) { - ArrayList views = getCellLayoutChildrenSortedSpatially(layout, parent); - int myIndex = views.indexOf(v); - // Select the next button, otherwise do nothing - if (myIndex < views.size() - 1) { - views.get(myIndex + 1).requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_DPAD_UP: - if (handleKeyEvent) { - final Workspace workspace = (Workspace) - v.getRootView().findViewById(R.id.workspace); - if (workspace != null) { - int pageIndex = workspace.getCurrentPage(); - CellLayout topLayout = (CellLayout) workspace.getChildAt(pageIndex); - ShortcutAndWidgetContainer children = topLayout.getShortcutsAndWidgets(); - final View newIcon = getIconInDirection(layout, children, -1, 1); - // Select the first bubble text view in the current page of the workspace - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } else { - workspace.requestFocus(); - } - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - // Do nothing - wasHandled = true; - break; - default: break; + if (DEBUG) { + Log.v(TAG, String.format( + "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, orientation=%d", + KeyEvent.keyCodeToString(keyCode), orientation)); } - return wasHandled; - } - /** - * Private helper method to get the CellLayoutChildren given a CellLayout index. - */ - private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( - ViewGroup container, int i) { - CellLayout parent = (CellLayout) container.getChildAt(i); - return parent.getShortcutsAndWidgets(); - } + // Initialize the variables. + final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent(); + final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent(); + Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); + int pageIndex = workspace.getCurrentPage(); + int pageCount = workspace.getChildCount(); + int countX, countY; + int iconIndex = findIndexOfView(hotseatParent, v); + + final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex); + final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); + + ViewGroup parent = null; + int[][] matrix; + + if (keyCode == KeyEvent.KEYCODE_DPAD_UP && + orientation == Configuration.ORIENTATION_PORTRAIT) { + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation); + // TODO: hotseat indexing should be symmetric. + iconIndex += iconParent.getChildCount(); + countX = iconLayout.getCountX(); + countY = iconLayout.getCountY() + hotseatLayout.getCountY(); + parent = iconParent; + } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && + orientation == Configuration.ORIENTATION_LANDSCAPE) { + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation); + // TODO: hotseat indexing should be symmetric. + iconIndex += iconParent.getChildCount(); + countX = iconLayout.getCountX() + hotseatLayout.getCountX(); + countY = iconLayout.getCountY(); + parent = iconParent; + } else { + // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the + // matrix extended with hotseat. + matrix = FocusLogic.createSparseMatrix(hotseatLayout); + parent = hotseatParent; + countX = hotseatLayout.getCountX(); + countY = hotseatLayout.getCountY(); - /** - * Private helper method to sort all the CellLayout children in order of their (x,y) spatially - * from top left to bottom right. - */ - private static ArrayList getCellLayoutChildrenSortedSpatially(CellLayout layout, - ViewGroup parent) { - // First we order each the CellLayout children by their x,y coordinates - final int cellCountX = layout.getCountX(); - final int count = parent.getChildCount(); - ArrayList views = new ArrayList(); - for (int j = 0; j < count; ++j) { - views.add(parent.getChildAt(j)); } - Collections.sort(views, new Comparator() { - @Override - public int compare(View lhs, View rhs) { - CellLayout.LayoutParams llp = (CellLayout.LayoutParams) lhs.getLayoutParams(); - CellLayout.LayoutParams rlp = (CellLayout.LayoutParams) rhs.getLayoutParams(); - int lvIndex = (llp.cellY * cellCountX) + llp.cellX; - int rvIndex = (rlp.cellY * cellCountX) + rlp.cellX; - return lvIndex - rvIndex; - } - }); - return views; - } - /** - * Private helper method to find the index of the next BubbleTextView or FolderIcon in the - * direction delta. - * - * @param delta either -1 or 1 depending on the direction we want to search - */ - private static View findIndexOfIcon(ArrayList views, int i, int delta) { - // Then we find the next BubbleTextView offset by delta from i - final int count = views.size(); - int newI = i + delta; - while (0 <= newI && newI < count) { - View newV = views.get(newI); - if (newV instanceof BubbleTextView || newV instanceof FolderIcon) { - return newV; - } - newI += delta; + + // Process the focus. + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, + iconIndex, pageIndex, pageCount); + + if (iconParent.getChildCount() <= newIconIndex && + newIconIndex < iconParent.getChildCount() + hotseatParent.getChildCount()) { + newIconIndex -= iconParent.getChildCount(); } - return null; - } - private static View getIconInDirection(CellLayout layout, ViewGroup parent, int i, - int delta) { - final ArrayList views = getCellLayoutChildrenSortedSpatially(layout, parent); - return findIndexOfIcon(views, i, delta); - } - private static View getIconInDirection(CellLayout layout, ViewGroup parent, View v, - int delta) { - final ArrayList views = getCellLayoutChildrenSortedSpatially(layout, parent); - return findIndexOfIcon(views, views.indexOf(v), delta); - } - /** - * Private helper method to find the next closest BubbleTextView or FolderIcon in the direction - * delta on the next line. - * - * @param delta either -1 or 1 depending on the line and direction we want to search - */ - private static View getClosestIconOnLine(CellLayout layout, ViewGroup parent, View v, - int lineDelta) { - final ArrayList views = getCellLayoutChildrenSortedSpatially(layout, parent); - final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - final int cellCountY = layout.getCountY(); - final int row = lp.cellY; - final int newRow = row + lineDelta; - if (0 <= newRow && newRow < cellCountY) { - float closestDistance = Float.MAX_VALUE; - int closestIndex = -1; - int index = views.indexOf(v); - int endIndex = (lineDelta < 0) ? -1 : views.size(); - while (index != endIndex) { - View newV = views.get(index); - CellLayout.LayoutParams tmpLp = (CellLayout.LayoutParams) newV.getLayoutParams(); - boolean satisfiesRow = (lineDelta < 0) ? (tmpLp.cellY < row) : (tmpLp.cellY > row); - if (satisfiesRow && - (newV instanceof BubbleTextView || newV instanceof FolderIcon)) { - float tmpDistance = (float) Math.sqrt(Math.pow(tmpLp.cellX - lp.cellX, 2) + - Math.pow(tmpLp.cellY - lp.cellY, 2)); - if (tmpDistance < closestDistance) { - closestIndex = index; - closestDistance = tmpDistance; - } - } - if (index <= endIndex) { - ++index; - } else { - --index; - } - } - if (closestIndex > -1) { - return views.get(closestIndex); + if (parent != null) { + View newIcon = parent.getChildAt(newIconIndex); + if (newIcon != null) { + newIcon.requestFocus(); + playSoundEffect(keyCode, v); } } - return null; + return consume; } /** - * Handles key events in a Workspace containing. + * Handles key events in a workspace containing icons. */ static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) { + boolean consume = FocusLogic.shouldConsume(keyCode); + if (e.getAction() == KeyEvent.ACTION_UP || !consume) { + return consume; + } + int orientation = v.getResources().getConfiguration().orientation; + if (DEBUG) { + Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] orientation=%d", + KeyEvent.keyCodeToString(keyCode), orientation)); + } + + // Initialize the variables. ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); final CellLayout layout = (CellLayout) parent.getParent(); final Workspace workspace = (Workspace) layout.getParent(); @@ -425,249 +249,191 @@ public class FocusHelper { final ViewGroup hotseat = (ViewGroup) launcher.findViewById(R.id.hotseat); int pageIndex = workspace.indexOfChild(layout); int pageCount = workspace.getChildCount(); + final int countX = layout.getCountX(); + int countY = layout.getCountY(); + final int iconIndex = findIndexOfView(parent, v); - final int action = e.getAction(); - final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP); - boolean wasHandled = false; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (handleKeyEvent) { - // Select the previous icon or the last icon on the previous page if possible - View newIcon = getIconInDirection(layout, parent, v, -1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } else { - if (pageIndex > 0) { - parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); - newIcon = getIconInDirection(layout, parent, - parent.getChildCount(), -1); - if (newIcon != null) { - newIcon.requestFocus(); - } else { - // Snap to the previous page - workspace.snapToPage(pageIndex - 1); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (handleKeyEvent) { - // Select the next icon or the first icon on the next page if possible - View newIcon = getIconInDirection(layout, parent, v, 1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); - } else { - if (pageIndex < (pageCount - 1)) { - parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); - newIcon = getIconInDirection(layout, parent, -1, 1); - if (newIcon != null) { - newIcon.requestFocus(); - } else { - // Snap to the next page - workspace.snapToPage(pageIndex + 1); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); - } - } - } - wasHandled = true; - break; - case KeyEvent.KEYCODE_DPAD_UP: - if (handleKeyEvent) { - // Select the closest icon in the previous line, otherwise select the tab bar - View newIcon = getClosestIconOnLine(layout, parent, v, -1); - if (newIcon != null) { - newIcon.requestFocus(); - wasHandled = true; - } else { - tabs.requestFocus(); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); + CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0); + ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets(); + int[][] matrix; + + // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_LEFT in landscape) is the only key allowed + // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended + // with the hotseat. + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && + orientation == Configuration.ORIENTATION_PORTRAIT) { + matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation); + countY = countY + 1; + } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && + orientation == Configuration.ORIENTATION_LANDSCAPE) { + matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation); + countY = countY + 1; + } else { + matrix = FocusLogic.createSparseMatrix(layout); + } + + // Process the focus. + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, + iconIndex, pageIndex, pageCount); + View newIcon = null; + switch (newIconIndex) { + case FocusLogic.NOOP: + if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { + newIcon = tabs; } break; - case KeyEvent.KEYCODE_DPAD_DOWN: - if (handleKeyEvent) { - // Select the closest icon in the next line, otherwise select the button bar - View newIcon = getClosestIconOnLine(layout, parent, v, 1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - wasHandled = true; - } else if (hotseat != null) { - hotseat.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - } + case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: + parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); + newIcon = parent.getChildAt(0); + workspace.snapToPage(pageIndex - 1); + case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: + parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); + newIcon = parent.getChildAt(parent.getChildCount() - 1); + workspace.snapToPage(pageIndex - 1); break; - case KeyEvent.KEYCODE_PAGE_UP: - if (handleKeyEvent) { - // Select the first icon on the previous page or the first icon on this page - // if there is no previous page - if (pageIndex > 0) { - parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); - View newIcon = getIconInDirection(layout, parent, -1, 1); - if (newIcon != null) { - newIcon.requestFocus(); - } else { - // Snap to the previous page - workspace.snapToPage(pageIndex - 1); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } else { - View newIcon = getIconInDirection(layout, parent, -1, 1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - } - } - wasHandled = true; + case FocusLogic.NEXT_PAGE_FIRST_ITEM: + parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); + newIcon = parent.getChildAt(0); + workspace.snapToPage(pageIndex - 1); break; - case KeyEvent.KEYCODE_PAGE_DOWN: - if (handleKeyEvent) { - // Select the first icon on the next page or the last icon on this page - // if there is no previous page - if (pageIndex < (pageCount - 1)) { - parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); - View newIcon = getIconInDirection(layout, parent, -1, 1); - if (newIcon != null) { - newIcon.requestFocus(); - } else { - // Snap to the next page - workspace.snapToPage(pageIndex + 1); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } else { - View newIcon = getIconInDirection(layout, parent, - parent.getChildCount(), -1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - } - } - wasHandled = true; + case FocusLogic.CURRENT_PAGE_FIRST_ITEM: + newIcon = parent.getChildAt(0); break; - case KeyEvent.KEYCODE_MOVE_HOME: - if (handleKeyEvent) { - // Select the first icon on this page - View newIcon = getIconInDirection(layout, parent, -1, 1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - } - wasHandled = true; + case FocusLogic.CURRENT_PAGE_LAST_ITEM: + newIcon = parent.getChildAt(parent.getChildCount() - 1); break; - case KeyEvent.KEYCODE_MOVE_END: - if (handleKeyEvent) { - // Select the last icon on this page - View newIcon = getIconInDirection(layout, parent, - parent.getChildCount(), -1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } + default: + // current page, some item. + if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) { + newIcon = parent.getChildAt(newIconIndex); + } else if (parent.getChildCount() <= newIconIndex && + newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) { + newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount()); } - wasHandled = true; break; - default: break; } - return wasHandled; + if (newIcon != null) { + newIcon.requestFocus(); + playSoundEffect(keyCode, v); + } + return consume; } /** * Handles key events for items in a Folder. */ static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) { + boolean consume = FocusLogic.shouldConsume(keyCode); + if (e.getAction() == KeyEvent.ACTION_UP || !consume) { + return consume; + } + if (DEBUG) { + Log.v(TAG, String.format("Handle FOLDER keyevent=[%s].", + KeyEvent.keyCodeToString(keyCode))); + } + + // Initialize the variables. ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); final CellLayout layout = (CellLayout) parent.getParent(); final ScrollView scrollView = (ScrollView) layout.getParent(); final Folder folder = (Folder) scrollView.getParent(); View title = folder.mFolderName; + Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); + final int countX = layout.getCountX(); + final int countY = layout.getCountY(); + final int iconIndex = findIndexOfView(parent, v); + int pageIndex = workspace.indexOfChild(layout); + int pageCount = workspace.getChildCount(); + int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order index */ + ); - final int action = e.getAction(); - final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP); - boolean wasHandled = false; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (handleKeyEvent) { - // Select the previous icon - View newIcon = getIconInDirection(layout, parent, v, -1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } + // Process the focus. + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex, + pageIndex, pageCount); + View newIcon = null; + switch (newIconIndex) { + case FocusLogic.NOOP: + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + newIcon = title; } - wasHandled = true; break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (handleKeyEvent) { - // Select the next icon - View newIcon = getIconInDirection(layout, parent, v, 1); - if (newIcon != null) { - newIcon.requestFocus(); - } else { - title.requestFocus(); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); + case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: + case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: + case FocusLogic.NEXT_PAGE_FIRST_ITEM: + case FocusLogic.CURRENT_PAGE_FIRST_ITEM: + case FocusLogic.CURRENT_PAGE_LAST_ITEM: + if (DEBUG) { + Log.v(TAG, "Page advance handling not supported on folder icons."); } - wasHandled = true; break; - case KeyEvent.KEYCODE_DPAD_UP: - if (handleKeyEvent) { - // Select the closest icon in the previous line - View newIcon = getClosestIconOnLine(layout, parent, v, -1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - } - wasHandled = true; + default: // current page some item. + newIcon = parent.getChildAt(newIconIndex); + break; + } + if (newIcon != null) { + newIcon.requestFocus(); + playSoundEffect(keyCode, v); + } + return consume; + } + + // + // Helper methods. + // + + /** + * Returns the Viewgroup containing page contents for the page at the index specified. + */ + private static ViewGroup getAppsCustomizePage(ViewGroup container, int index) { + ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index); + if (page instanceof CellLayout) { + // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren + page = ((CellLayout) page).getShortcutsAndWidgets(); + } + return page; + } + + /** + * Private helper method to get the CellLayoutChildren given a CellLayout index. + */ + private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( + ViewGroup container, int i) { + CellLayout parent = (CellLayout) container.getChildAt(i); + return parent.getShortcutsAndWidgets(); + } + + private static int findIndexOfView(ViewGroup parent, View v) { + for (int i = 0; i < parent.getChildCount(); i++) { + if (v != null && v.equals(parent.getChildAt(i))) { + return i; + } + } + return -1; + } + + /** + * Helper method to be used for playing sound effects. + */ + private static void playSoundEffect(int keyCode, View v) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); break; case KeyEvent.KEYCODE_DPAD_DOWN: - if (handleKeyEvent) { - // Select the closest icon in the next line - View newIcon = getClosestIconOnLine(layout, parent, v, 1); - if (newIcon != null) { - newIcon.requestFocus(); - } else { - title.requestFocus(); - } - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - wasHandled = true; + case KeyEvent.KEYCODE_PAGE_DOWN: + case KeyEvent.KEYCODE_MOVE_END: + v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); break; + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_PAGE_UP: case KeyEvent.KEYCODE_MOVE_HOME: - if (handleKeyEvent) { - // Select the first icon on this page - View newIcon = getIconInDirection(layout, parent, -1, 1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); - } - } - wasHandled = true; + v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP); break; - case KeyEvent.KEYCODE_MOVE_END: - if (handleKeyEvent) { - // Select the last icon on this page - View newIcon = getIconInDirection(layout, parent, - parent.getChildCount(), -1); - if (newIcon != null) { - newIcon.requestFocus(); - v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN); - } - } - wasHandled = true; + default: break; - default: break; } - return wasHandled; } } diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java new file mode 100644 index 000000000..23375dc43 --- /dev/null +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -0,0 +1,434 @@ +/* + * 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.util; + +import android.content.res.Configuration; +import android.util.Log; +import android.view.KeyEvent; +import android.view.ViewGroup; + +import com.android.launcher3.CellLayout; + +/** + * Calculates the next item that a {@link KeyEvent} should change the focus to. + *

+ * Note, this utility class calculates everything regards to icon index and its (x,y) coordinates. + * Currently supports: + *

    + *
  • full matrix of cells that are 1x1 + *
  • sparse matrix of cells that are 1x1 + * [ 1][ ][ 2][ ] + * [ ][ ][ 3][ ] + * [ ][ 4][ ][ ] + * [ ][ 5][ 6][ 7] + *
+ * *

+ * For testing, one can use a BT keyboard, or use following adb command. + * ex. $ adb shell input keyevent 20 // KEYCODE_DPAD_LEFT + */ +public class FocusLogic { + + private static final String TAG = "Focus"; + private static final boolean DEBUG = false; + + // Item and page index related constant used by {@link #handleKeyEvent}. + public static final int NOOP = -1; + public static final int PREVIOUS_PAGE_FIRST_ITEM = -2; + public static final int PREVIOUS_PAGE_LAST_ITEM = -3; + public static final int CURRENT_PAGE_FIRST_ITEM = -4; + public static final int CURRENT_PAGE_LAST_ITEM = -5; + public static final int NEXT_PAGE_FIRST_ITEM = -6; + + // Matrix related constant. + public static final int EMPTY = -1; + + /** + * Returns true only if this utility class handles the key code. + */ + public static boolean shouldConsume(int keyCode) { + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || + keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || + keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END || + keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN) { + return true; + } + return false; + } + + public static int handleKeyEvent(int keyCode, int cntX, int cntY, int [][] map, + int iconIdx, int pageIndex, int pageCount) { + + if (DEBUG) { + Log.v(TAG, String.format( + "handleKeyEvent START: cntX=%d, cntY=%d, iconIdx=%d, pageIdx=%d, pageCnt=%d", + cntX, cntY, iconIdx, pageIndex, pageCount)); + } + + int newIndex = NOOP; + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/); + if (newIndex == NOOP && pageIndex > 0) { + return PREVIOUS_PAGE_LAST_ITEM; + } + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/); + if (newIndex == NOOP && pageIndex < pageCount - 1) { + return NEXT_PAGE_FIRST_ITEM; + } + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, 1 /*increment*/); + break; + case KeyEvent.KEYCODE_DPAD_UP: + newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, -1 /*increment*/); + break; + case KeyEvent.KEYCODE_MOVE_HOME: + newIndex = handleMoveHome(); + break; + case KeyEvent.KEYCODE_MOVE_END: + newIndex = handleMoveEnd(); + break; + case KeyEvent.KEYCODE_PAGE_DOWN: + newIndex = handlePageDown(pageIndex, pageCount); + break; + case KeyEvent.KEYCODE_PAGE_UP: + newIndex = handlePageUp(pageIndex); + break; + default: + break; + } + + if (DEBUG) { + Log.v(TAG, String.format("handleKeyEvent FINISH: index [%d -> %s]", + iconIdx, getStringIndex(newIndex))); + } + return newIndex; + } + + /** + * Returns a matrix of size (m x n) that has been initialized with incremental index starting + * with 0 or a matrix where all the values are initialized to {@link #EMPTY}. + * + * @param m number of columns in the matrix + * @param n number of rows in the matrix + * @param incrementOrder {@code true} if the matrix contents should increment in reading + * order with 0 indexing. {@code false} if each cell should be + * initialized to {@link #EMPTY}; + */ + // TODO: get rid of dynamic matrix creation. + public static int[][] createFullMatrix(int m, int n, boolean incrementOrder) { + int[][] matrix = new int [m][n]; + for (int i=0; i < m;i++) { + for (int j=0; j < n; j++) { + if (incrementOrder) { + matrix[i][j] = j * m + i; + } else { + matrix[i][j] = EMPTY; + } + } + } + return matrix; + } + + /** + * Returns a matrix of size same as the {@link CellLayout} dimension that is initialized with the + * index of the child view. + */ + // TODO: get rid of the dynamic matrix creation + public static int[][] createSparseMatrix(CellLayout layout) { + ViewGroup parent = layout.getShortcutsAndWidgets(); + final int m = layout.getCountX(); + final int n = layout.getCountY(); + + int[][] matrix = createFullMatrix(m, n, false /* initialize to #EMPTY */); + + // Iterate thru the children. + for (int i = 0; i < parent.getChildCount(); i++ ) { + int cx = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellX; + int cy = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellY; + matrix[cx][cy] = i; + } + if (DEBUG) { + printMatrix(matrix, m, n); + } + return matrix; + } + + /** + * Creates a sparse matrix that merges the icon and hotseat view group using the cell layout. + * The size of the returning matrix is [icon column count x (icon + hotseat row count)] + * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)] + */ + // TODO: get rid of the dynamic matrix creation + public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout, + int orientation) { + + ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); + ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets(); + + int m, n; + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + m = iconLayout.getCountX(); + n = iconLayout.getCountY() + hotseatLayout.getCountY(); + } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + m = iconLayout.getCountX() + hotseatLayout.getCountX(); + n = iconLayout.getCountY(); + } else { + throw new IllegalStateException(String.format( + "orientation type=%d is not supported for key board events.", orientation)); + } + int[][] matrix = createFullMatrix(m, n, false /* set all cell to empty */); + + // Iterate thru the children of the top parent. + for (int i = 0; i < iconParent.getChildCount(); i++) { + int cx = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellX; + int cy = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellY; + matrix[cx][cy] = i; + } + + // Iterate thru the children of the bottom parent + for(int i = 0; i < hotseatParent.getChildCount(); i++) { + // If the hotseat view group contains more items than topColumnCnt, then just + // discard them. + // TODO: make this more elegant. (look at DynamicGrid) + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + int cx = ((CellLayout.LayoutParams) + hotseatParent.getChildAt(i).getLayoutParams()).cellX; + if (cx < iconLayout.getCountX()) { + matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i; + } + } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + int cy = ((CellLayout.LayoutParams) + hotseatParent.getChildAt(i).getLayoutParams()).cellY; + if (cy < iconLayout.getCountY()) { + matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i; + } + } + } + if (DEBUG) { + printMatrix(matrix, m, n); + } + return matrix; + } + + // + // key event handling methods. + // + + /** + * Calculates icon that has is closest to the horizontal axis in reference to the cur icon. + * + * Example of the check order for KEYCODE_DPAD_RIGHT: + * [ ][ ][13][14][15] + * [ ][ 6][ 8][10][12] + * [ X][ 1][ 2][ 3][ 4] + * [ ][ 5][ 7][ 9][11] + */ + // TODO: add unit tests to verify all permutation. + private static int handleDpadHorizontal(int iconIdx, int cntX, int cntY, + int[][] matrix, int increment) { + if(matrix == null) { + throw new IllegalStateException("Dpad navigation requires a matrix."); + } + int newIconIndex = NOOP; + + int xPos = -1; + int yPos = -1; + // Figure out the location of the icon. + for (int i = 0; i < cntX; i++) { + for (int j = 0; j < cntY; j++) { + if (matrix[i][j] == iconIdx) { + xPos = i; + yPos = j; + } + } + } + if (DEBUG) { + Log.v(TAG, String.format("\thandleDpadHorizontal: \t[x, y]=[%d, %d] iconIndex=%d", + xPos, yPos, iconIdx)); + } + + // Rule1: check first in the horizontal direction + for (int i = xPos + increment; 0 <= i && i < cntX; i = i + increment) { + if (DEBUG) { + Log.v(TAG, String.format("\t\tsearch: \t[x, y]=[%d, %d] iconIndex=%d", + i, yPos, matrix[i][yPos])); + } + if (matrix[i][yPos] != -1) { + newIconIndex = matrix[i][yPos]; + return newIconIndex; + } + } + + // Rule2: check (x1-n, yPos + increment), (x1-n, yPos - increment) + // (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment) + int nextYPos1; + int nextYPos2; + int i = -1; + for (int coeff = 1; coeff < cntY; coeff++) { + nextYPos1 = yPos + coeff * increment; + nextYPos2 = yPos - coeff * increment; + for (i = xPos + increment * coeff; 0 <= i && i < cntX; i = i + increment) { + if ((newIconIndex = inspectMatrix(i, nextYPos1, cntX, cntY, matrix)) != NOOP) { + return newIconIndex; + } + if ((newIconIndex = inspectMatrix(i, nextYPos2, cntX, cntY, matrix)) != NOOP) { + return newIconIndex; + } + } + } + return newIconIndex; + } + + /** + * Calculates icon that is closest to the vertical axis in reference to the current icon. + * + * Example of the check order for KEYCODE_DPAD_DOWN: + * [ ][ ][ ][ X][ ][ ][ ] + * [ ][ ][ 5][ 1][ 4][ ][ ] + * [ ][10][ 7][ 2][ 6][ 9][ ] + * [14][12][ 9][ 3][ 8][11][13] + */ + // TODO: add unit tests to verify all permutation. + private static int handleDpadVertical(int iconIndex, int cntX, int cntY, + int [][] matrix, int increment) { + int newIconIndex = NOOP; + if(matrix == null) { + throw new IllegalStateException("Dpad navigation requires a matrix."); + } + + int xPos = -1; + int yPos = -1; + // Figure out the location of the icon. + for (int i = 0; i< cntX; i++) { + for (int j = 0; j < cntY; j++) { + if (matrix[i][j] == iconIndex) { + xPos = i; + yPos = j; + } + } + } + + if (DEBUG) { + Log.v(TAG, String.format("\thandleDpadVertical: \t[x, y]=[%d, %d] iconIndex=%d", + xPos, yPos, iconIndex)); + } + + // Rule1: check first in the dpad direction + for (int j = yPos + increment; 0 <= j && j 0) { + return PREVIOUS_PAGE_FIRST_ITEM; + } else { + return CURRENT_PAGE_FIRST_ITEM; + } + } + + // + // Helper methods. + // + + private static boolean isValid(int xPos, int yPos, int countX, int countY) { + return (0 <= xPos && xPos < countX && 0 <= yPos && yPos < countY); + } + + private static int inspectMatrix(int x, int y, int cntX, int cntY, int[][] matrix) { + int newIconIndex = NOOP; + if (isValid(x, y, cntX, cntY)) { + if (matrix[x][y] != -1) { + newIconIndex = matrix[x][y]; + if (DEBUG) { + Log.v(TAG, String.format("\t\tinspect: \t[x, y]=[%d, %d] %d", + x, y, matrix[x][y])); + } + return newIconIndex; + } + } + return newIconIndex; + } + + private static String getStringIndex(int index) { + switch(index) { + case NOOP: return "NOOP"; + case PREVIOUS_PAGE_FIRST_ITEM: return "PREVIOUS_PAGE_FIRST"; + case PREVIOUS_PAGE_LAST_ITEM: return "PREVIOUS_PAGE_LAST"; + case CURRENT_PAGE_FIRST_ITEM: return "CURRENT_PAGE_FIRST"; + case CURRENT_PAGE_LAST_ITEM: return "CURRENT_PAGE_LAST"; + case NEXT_PAGE_FIRST_ITEM: return "NEXT_PAGE_FIRST"; + default: + return Integer.toString(index); + } + } + + private static void printMatrix(int[][] matrix, int m, int n) { + Log.v(TAG, "\tprintMap:"); + for (int j=0; j < n; j++) { + String colY = "\t\t"; + for (int i=0; i < m; i++) { + colY += String.format("%3d",matrix[i][j]); + } + Log.v(TAG, colY); + } + } +} -- cgit v1.2.3 From 7470c817e46f241bf37a7874fa7cf7493fc7cf94 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Feb 2015 14:28:44 -0800 Subject: Using SCREEN_ORIENTATION_LOCKED for locking to current orientation. Bug: 17298128 Change-Id: If64b6957a594bcc48f6454689d11cd63d31b9239 (cherry picked from commit 3c1865ad5050f594d1684fe8962bfbc8ffcbe4bb) --- src/com/android/launcher3/Launcher.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index eb2b5b803..cd861d4e0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4815,8 +4815,12 @@ public class Launcher extends Activity public void lockScreenOrientation() { if (Utilities.isRotationEnabled(this)) { - setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() - .getConfiguration().orientation)); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() + .getConfiguration().orientation)); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } } } public void unlockScreenOrientation(boolean immediate) { -- cgit v1.2.3 From 31178b8237ccb6af666df60ef60c116c8afdf316 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 24 Feb 2015 14:12:51 -0800 Subject: [key event focus handling] (1)hotseat <-> icon now symmetric, (2)support DEL keycode TL;DR; (1) Key event navigation from and to the hotseat and icons in the workspace is now symmetric. Since there is one more icon in the hotseat, only left N-1 icon navigation was symmetric. (2) KeyEvent.KEYCODE_DEL and KeyEvent.KEYCODE_FORWARD_DEL can now delete icons from the workspace. The focus move to the previous icon where the focus traveled from. Also contains minor styling and indexing issues. Bug: 15408321 Bug: 19381790 Change-Id: I16cbcb2693e92eebb830997d01c0bf674073dd51 --- src/com/android/launcher3/FocusHelper.java | 72 ++++++++++++++++---------- src/com/android/launcher3/Hotseat.java | 11 ++++ src/com/android/launcher3/Workspace.java | 3 ++ src/com/android/launcher3/util/FocusLogic.java | 45 ++++++++-------- 4 files changed, 81 insertions(+), 50 deletions(-) diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index c02d73cb6..bdfd7b287 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -170,54 +170,68 @@ public class FocusHelper { // Initialize the variables. final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent(); final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent(); + Hotseat hotseat = (Hotseat) hotseatLayout.getParent(); + Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); int pageIndex = workspace.getCurrentPage(); int pageCount = workspace.getChildCount(); - int countX, countY; + int countX = -1; + int countY = -1; int iconIndex = findIndexOfView(hotseatParent, v); final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex); final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); ViewGroup parent = null; - int[][] matrix; + int[][] matrix = null; if (keyCode == KeyEvent.KEYCODE_DPAD_UP && orientation == Configuration.ORIENTATION_PORTRAIT) { - matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation); - // TODO: hotseat indexing should be symmetric. + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, + hotseat.getAllAppsButtonRank(), true /* include all apps icon */); iconIndex += iconParent.getChildCount(); countX = iconLayout.getCountX(); countY = iconLayout.getCountY() + hotseatLayout.getCountY(); parent = iconParent; } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && orientation == Configuration.ORIENTATION_LANDSCAPE) { - matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation); - // TODO: hotseat indexing should be symmetric. + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, + hotseat.getAllAppsButtonRank(), true /* include all apps icon */); iconIndex += iconParent.getChildCount(); countX = iconLayout.getCountX() + hotseatLayout.getCountX(); countY = iconLayout.getCountY(); parent = iconParent; - } else { + } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && + orientation == Configuration.ORIENTATION_LANDSCAPE) { + keyCode = KeyEvent.KEYCODE_PAGE_DOWN; + }else { // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the // matrix extended with hotseat. matrix = FocusLogic.createSparseMatrix(hotseatLayout); - parent = hotseatParent; countX = hotseatLayout.getCountX(); countY = hotseatLayout.getCountY(); - + parent = hotseatParent; } // Process the focus. int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, iconIndex, pageIndex, pageCount); - if (iconParent.getChildCount() <= newIconIndex && - newIconIndex < iconParent.getChildCount() + hotseatParent.getChildCount()) { + View newIcon = null; + if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) { + parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); + newIcon = parent.getChildAt(0); + // TODO(hyunyoungs): handle cases where the child is not an icon but + // a folder or a widget. + workspace.snapToPage(pageIndex + 1); + } + if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) { newIconIndex -= iconParent.getChildCount(); } if (parent != null) { - View newIcon = parent.getChildAt(newIconIndex); + if (newIcon == null && newIconIndex >=0) { + newIcon = parent.getChildAt(newIconIndex); + } if (newIcon != null) { newIcon.requestFocus(); playSoundEffect(keyCode, v); @@ -242,34 +256,39 @@ public class FocusHelper { // Initialize the variables. ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); - final CellLayout layout = (CellLayout) parent.getParent(); - final Workspace workspace = (Workspace) layout.getParent(); + final CellLayout iconLayout = (CellLayout) parent.getParent(); + final Workspace workspace = (Workspace) iconLayout.getParent(); final ViewGroup launcher = (ViewGroup) workspace.getParent(); final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar); - final ViewGroup hotseat = (ViewGroup) launcher.findViewById(R.id.hotseat); - int pageIndex = workspace.indexOfChild(layout); + final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat); + int pageIndex = workspace.indexOfChild(iconLayout); int pageCount = workspace.getChildCount(); - final int countX = layout.getCountX(); - int countY = layout.getCountY(); + int countX = iconLayout.getCountX(); + int countY = iconLayout.getCountY(); final int iconIndex = findIndexOfView(parent, v); CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0); ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets(); int[][] matrix; - // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_LEFT in landscape) is the only key allowed + // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended // with the hotseat. if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && orientation == Configuration.ORIENTATION_PORTRAIT) { - matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation); + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, + hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */); countY = countY + 1; - } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && + } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && orientation == Configuration.ORIENTATION_LANDSCAPE) { - matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation); - countY = countY + 1; + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, + hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */); + countX = countX + 1; + } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { + workspace.removeWorkspaceItem(v); + return consume; } else { - matrix = FocusLogic.createSparseMatrix(layout); + matrix = FocusLogic.createSparseMatrix(iconLayout); } // Process the focus. @@ -294,7 +313,7 @@ public class FocusHelper { case FocusLogic.NEXT_PAGE_FIRST_ITEM: parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); newIcon = parent.getChildAt(0); - workspace.snapToPage(pageIndex - 1); + workspace.snapToPage(pageIndex + 1); break; case FocusLogic.CURRENT_PAGE_FIRST_ITEM: newIcon = parent.getChildAt(0); @@ -344,8 +363,7 @@ public class FocusHelper { final int iconIndex = findIndexOfView(parent, v); int pageIndex = workspace.indexOfChild(layout); int pageCount = workspace.getChildCount(); - int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order index */ - ); + int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order */); // Process the focus. int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex, diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 289b08b14..bd6c21ab3 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -83,13 +83,24 @@ public class Hotseat extends FrameLayout { int getOrderInHotseat(int x, int y) { return hasVerticalHotseat() ? (mContent.getCountY() - y - 1) : x; } + /* Get the orientation specific coordinates given an invariant order in the hotseat. */ int getCellXFromOrder(int rank) { return hasVerticalHotseat() ? 0 : rank; } + int getCellYFromOrder(int rank) { return hasVerticalHotseat() ? (mContent.getCountY() - (rank + 1)) : 0; } + + public int getAllAppsButtonRank() { + if (LauncherAppState.isDisableAllApps()) { + return -1; + } else { + return mAllAppsButtonRank; + } + } + public boolean isAllAppsButtonRank(int rank) { if (LauncherAppState.isDisableAllApps()) { return false; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 1a4afe8e7..4e16a451a 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4231,6 +4231,9 @@ public class Workspace extends SmoothPagedView mDragInfo = null; } + /** + * For opposite operation. See {@link #addInScreen}. + */ public void removeWorkspaceItem(View v) { CellLayout parentCell = getParentCellLayoutForView(v); if (parentCell != null) { diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index 23375dc43..c0730d93d 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -47,10 +47,13 @@ public class FocusLogic { // Item and page index related constant used by {@link #handleKeyEvent}. public static final int NOOP = -1; + public static final int PREVIOUS_PAGE_FIRST_ITEM = -2; public static final int PREVIOUS_PAGE_LAST_ITEM = -3; + public static final int CURRENT_PAGE_FIRST_ITEM = -4; public static final int CURRENT_PAGE_LAST_ITEM = -5; + public static final int NEXT_PAGE_FIRST_ITEM = -6; // Matrix related constant. @@ -63,7 +66,8 @@ public class FocusLogic { if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END || - keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN) { + keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || + keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { return true; } return false; @@ -175,9 +179,9 @@ public class FocusLogic { * The size of the returning matrix is [icon column count x (icon + hotseat row count)] * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)] */ - // TODO: get rid of the dynamic matrix creation + // TODO: get rid of the dynamic matrix creation public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout, - int orientation) { + int orientation, int allappsiconRank, boolean includeAllappsicon) { ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets(); @@ -203,22 +207,27 @@ public class FocusLogic { } // Iterate thru the children of the bottom parent - for(int i = 0; i < hotseatParent.getChildCount(); i++) { - // If the hotseat view group contains more items than topColumnCnt, then just - // discard them. - // TODO: make this more elegant. (look at DynamicGrid) + // The hotseat view group contains one more item than iconLayout column count. + // If {@param allappsiconRank} not negative, then the last icon in the hotseat + // is truncated. If it is negative, then all apps icon index is not inserted. + for(int i = hotseatParent.getChildCount() - 1; i >= (includeAllappsicon ? 0 : 1); i--) { + int delta = 0; if (orientation == Configuration.ORIENTATION_PORTRAIT) { int cx = ((CellLayout.LayoutParams) hotseatParent.getChildAt(i).getLayoutParams()).cellX; - if (cx < iconLayout.getCountX()) { - matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i; + if ((includeAllappsicon && cx >= allappsiconRank) || + (!includeAllappsicon && cx > allappsiconRank)) { + delta = -1; } + matrix[cx + delta][iconLayout.getCountY()] = iconParent.getChildCount() + i; } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { int cy = ((CellLayout.LayoutParams) hotseatParent.getChildAt(i).getLayoutParams()).cellY; - if (cy < iconLayout.getCountY()) { - matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i; + if ((includeAllappsicon && cy >= allappsiconRank) || + (!includeAllappsicon && cy > allappsiconRank)) { + delta = -1; } + matrix[iconLayout.getCountX()][cy + delta] = iconParent.getChildCount() + i; } } if (DEBUG) { @@ -266,12 +275,7 @@ public class FocusLogic { // Rule1: check first in the horizontal direction for (int i = xPos + increment; 0 <= i && i < cntX; i = i + increment) { - if (DEBUG) { - Log.v(TAG, String.format("\t\tsearch: \t[x, y]=[%d, %d] iconIndex=%d", - i, yPos, matrix[i][yPos])); - } - if (matrix[i][yPos] != -1) { - newIconIndex = matrix[i][yPos]; + if ((newIconIndex = inspectMatrix(i, yPos, cntX, cntY, matrix)) != NOOP) { return newIconIndex; } } @@ -332,12 +336,7 @@ public class FocusLogic { // Rule1: check first in the dpad direction for (int j = yPos + increment; 0 <= j && j Date: Thu, 12 Feb 2015 15:34:41 -0800 Subject: Reducing memory usage of wallpaper picker > Loading preview bitmap only once, instead of loading it twice at BitmapRegionTileSource and BitmapSource > Maintaing a weak-set of reusable bitmaps and reusing them for decoding bitmaps > Loading images on a HandlerThread (instead of AsyncTask) and removing any non-started task before submitting a new task > Loading inbuild images (from resources) on HandlerThread instead of UIThread > Freeing up unbound GL textures before binding a new texture. Bug: 18382606 Change-Id: Ic4ca630dd113ded65d2853eb0d291c9e5823637e (cherry picked from commit 283c2261bd4440f4108a564cea0f5fc499781213) --- .../android/gallery3d/glrenderer/GLES20Canvas.java | 1 + .../android/launcher3/WallpaperCropActivity.java | 195 ++++++++++++++++----- .../android/launcher3/WallpaperPickerActivity.java | 76 ++++---- .../com/android/photos/BitmapRegionTileSource.java | 147 +++++----------- .../com/android/photos/views/TiledImageView.java | 4 + 5 files changed, 241 insertions(+), 182 deletions(-) diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java index 8af1f5932..933260b48 100644 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java +++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java @@ -696,6 +696,7 @@ public class GLES20Canvas implements GLCanvas { } private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { + deleteRecycledResources(); GLES20.glUseProgram(program); checkError(); enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index 71c7a161d..0ddb79e4f 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -25,25 +25,36 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.RectF; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.Toast; + import com.android.gallery3d.common.BitmapCropTask; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; +import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider; +import com.android.photos.views.TiledImageRenderer.TileSource; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import java.util.WeakHashMap; -public class WallpaperCropActivity extends Activity { +public class WallpaperCropActivity extends Activity implements Handler.Callback { private static final String LOGTAG = "Launcher3.CropActivity"; protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; @@ -59,13 +70,29 @@ public class WallpaperCropActivity extends Activity { public static final int MAX_BMAP_IN_INTENT = 750000; public static final float WALLPAPER_SCREENS_SPAN = 2f; + private static final int MSG_LOAD_IMAGE = 1; + protected CropView mCropView; + protected View mProgressView; protected Uri mUri; protected View mSetWallpaperButton; + private HandlerThread mLoaderThread; + private Handler mLoaderHandler; + private LoadRequest mCurrentLoadRequest; + private byte[] mTempStorageForDecoding = new byte[16 * 1024]; + // A weak-set of reusable bitmaps + private Set mReusableBitmaps = + Collections.newSetFromMap(new WeakHashMap()); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + mLoaderThread = new HandlerThread("wallpaper_loader"); + mLoaderThread.start(); + mLoaderHandler = new Handler(mLoaderThread.getLooper(), this); + init(); if (!enableRotation()) { setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); @@ -76,6 +103,7 @@ public class WallpaperCropActivity extends Activity { setContentView(R.layout.wallpaper_cropper); mCropView = (CropView) findViewById(R.id.cropView); + mProgressView = findViewById(R.id.loading); Intent cropIntent = getIntent(); final Uri imageUri = cropIntent.getData(); @@ -116,7 +144,7 @@ public class WallpaperCropActivity extends Activity { } } }; - setCropViewTileSource(bitmapSource, true, false, onLoad); + setCropViewTileSource(bitmapSource, true, false, null, onLoad); } @Override @@ -124,65 +152,134 @@ public class WallpaperCropActivity extends Activity { if (mCropView != null) { mCropView.destroy(); } + if (mLoaderThread != null) { + mLoaderThread.quit(); + } super.onDestroy(); } - public void setCropViewTileSource( - final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled, - final boolean moveToLeft, final Runnable postExecute) { - final Context context = WallpaperCropActivity.this; - final View progressView = findViewById(R.id.loading); - final AsyncTask loadBitmapTask = new AsyncTask() { - protected Void doInBackground(Void...args) { - if (!isCancelled()) { - try { - bitmapSource.loadInBackground(); - } catch (SecurityException securityException) { - if (isDestroyed()) { - // Temporarily granted permissions are revoked when the activity - // finishes, potentially resulting in a SecurityException here. - // Even though {@link #isDestroyed} might also return true in different - // situations where the configuration changes, we are fine with - // catching these cases here as well. - cancel(false); - } else { - // otherwise it had a different cause and we throw it further - throw securityException; + /** + * This is called on {@link #mLoaderThread} + */ + @Override + public boolean handleMessage(Message msg) { + if (msg.what == MSG_LOAD_IMAGE) { + final LoadRequest req = (LoadRequest) msg.obj; + try { + req.src.loadInBackground(new InBitmapProvider() { + + @Override + public Bitmap forPixelCount(int count) { + synchronized (mReusableBitmaps) { + Iterator itr = mReusableBitmaps.iterator(); + while (itr.hasNext()) { + Bitmap b = itr.next(); + if (b.getWidth() * b.getHeight() >= count) { + itr.remove(); + return b; + } + } } + return null; } + }); + } catch (SecurityException securityException) { + if (isDestroyed()) { + // Temporarily granted permissions are revoked when the activity + // finishes, potentially resulting in a SecurityException here. + // Even though {@link #isDestroyed} might also return true in different + // situations where the configuration changes, we are fine with + // catching these cases here as well. + return true; + } else { + // otherwise it had a different cause and we throw it further + throw securityException; } - return null; } - protected void onPostExecute(Void arg) { - if (!isCancelled()) { - progressView.setVisibility(View.INVISIBLE); - if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { - mCropView.setTileSource( - new BitmapRegionTileSource(context, bitmapSource), null); - mCropView.setTouchEnabled(touchEnabled); - if (moveToLeft) { - mCropView.moveToLeft(); - } + + req.result = new BitmapRegionTileSource(this, req.src, mTempStorageForDecoding); + runOnUiThread(new Runnable() { + + @Override + public void run() { + if (req == mCurrentLoadRequest) { + onLoadRequestComplete(req, + req.src.getLoadingState() == BitmapSource.State.LOADED); + } else { + addReusableBitmap(req.result); } } - if (postExecute != null) { - postExecute.run(); + }); + return true; + } + return false; + } + + private void addReusableBitmap(TileSource src) { + synchronized (mReusableBitmaps) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + && src instanceof BitmapRegionTileSource) { + Bitmap preview = ((BitmapRegionTileSource) src).getBitmap(); + if (preview != null && preview.isMutable()) { + mReusableBitmaps.add(preview); } } - }; + } + } + + protected void onLoadRequestComplete(LoadRequest req, boolean success) { + mCurrentLoadRequest = null; + if (success) { + TileSource oldSrc = mCropView.getTileSource(); + mCropView.setTileSource(req.result, null); + mCropView.setTouchEnabled(req.touchEnabled); + if (req.moveToLeft) { + mCropView.moveToLeft(); + } + if (req.scaleProvider != null) { + mCropView.setScale(req.scaleProvider.getScale(req.result)); + } + + // Free last image + if (oldSrc != null) { + // Call yield instead of recycle, as we only want to free GL resource. + // We can still reuse the bitmap for decoding any other image. + oldSrc.getPreview().yield(); + } + addReusableBitmap(oldSrc); + } + if (req.postExecute != null) { + req.postExecute.run(); + } + } + + public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled, + boolean moveToLeft, CropViewScaleProvider scaleProvider, Runnable postExecute) { + final LoadRequest req = new LoadRequest(); + req.moveToLeft = moveToLeft; + req.src = bitmapSource; + req.touchEnabled = touchEnabled; + req.postExecute = postExecute; + req.scaleProvider = scaleProvider; + mCurrentLoadRequest = req; + + // Remove any pending requests + mLoaderHandler.removeMessages(MSG_LOAD_IMAGE); + Message.obtain(mLoaderHandler, MSG_LOAD_IMAGE, req).sendToTarget(); + // We don't want to show the spinner every time we load an image, because that would be // annoying; instead, only start showing the spinner if loading the image has taken // longer than 1 sec (ie 1000 ms) - progressView.postDelayed(new Runnable() { + mProgressView.postDelayed(new Runnable() { public void run() { - if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) { - progressView.setVisibility(View.VISIBLE); + if (mCurrentLoadRequest == req) { + mProgressView.setVisibility(View.VISIBLE); } } }, 1000); - loadBitmapTask.execute(); } + public boolean enableRotation() { return getResources().getBoolean(R.bool.allow_rotation); } @@ -372,4 +469,18 @@ public class WallpaperCropActivity extends Activity { wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); } } + + static class LoadRequest { + BitmapSource src; + boolean touchEnabled; + boolean moveToLeft; + Runnable postExecute; + CropViewScaleProvider scaleProvider; + + TileSource result; + } + + interface CropViewScaleProvider { + float getScale(TileSource src); + } } diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index f6cc6d0fd..cbed61bd2 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -74,6 +74,7 @@ import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; +import com.android.photos.views.TiledImageRenderer.TileSource; import java.io.File; import java.io.FileOutputStream; @@ -168,7 +169,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); - a.setCropViewTileSource(mBitmapSource, true, false, onLoad); + a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad); } @Override public void onSave(final WallpaperPickerActivity a) { @@ -205,7 +206,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public void onClick(WallpaperPickerActivity a) { BitmapRegionTileSource.UriBitmapSource bitmapSource = new BitmapRegionTileSource.UriBitmapSource(a, Uri.fromFile(mFile), 1024); - a.setCropViewTileSource(bitmapSource, false, true, null); + a.setCropViewTileSource(bitmapSource, false, true, null, null); } @Override public void onSave(WallpaperPickerActivity a) { @@ -231,22 +232,22 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mThumb = thumb; } @Override - public void onClick(WallpaperPickerActivity a) { + public void onClick(final WallpaperPickerActivity a) { BitmapRegionTileSource.ResourceBitmapSource bitmapSource = new BitmapRegionTileSource.ResourceBitmapSource( mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); - bitmapSource.loadInBackground(); - BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource); - CropView v = a.getCropView(); - v.setTileSource(source, null); - Point wallpaperSize = BitmapUtils.getDefaultWallpaperSize( - a.getResources(), a.getWindowManager()); - RectF crop = Utils.getMaxCropRect( - source.getImageWidth(), source.getImageHeight(), - wallpaperSize.x, wallpaperSize.y, false); - v.setScale(wallpaperSize.x / crop.width()); - v.setTouchEnabled(false); - a.setSystemWallpaperVisiblity(false); + a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() { + + @Override + public float getScale(TileSource src) { + Point wallpaperSize = BitmapUtils.getDefaultWallpaperSize( + a.getResources(), a.getWindowManager()); + RectF crop = Utils.getMaxCropRect( + src.getImageWidth(), src.getImageHeight(), + wallpaperSize.x, wallpaperSize.y, false); + return wallpaperSize.x / crop.width(); + } + }, null); } @Override public void onSave(WallpaperPickerActivity a) { @@ -271,21 +272,26 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(WallpaperPickerActivity a) { CropView c = a.getCropView(); - Drawable defaultWallpaper = WallpaperManager.getInstance(a).getBuiltInDrawable( c.getWidth(), c.getHeight(), false, 0.5f, 0.5f); - if (defaultWallpaper == null) { Log.w(TAG, "Null default wallpaper encountered."); c.setTileSource(null, null); return; } - c.setTileSource( - new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE), null); - c.setScale(1f); - c.setTouchEnabled(false); - a.setSystemWallpaperVisiblity(false); + LoadRequest req = new LoadRequest(); + req.moveToLeft = false; + req.touchEnabled = false; + req.scaleProvider = new CropViewScaleProvider() { + + @Override + public float getScale(TileSource src) { + return 1f; + } + }; + req.result = new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE); + a.onLoadRequestComplete(req, true); } @Override public void onSave(WallpaperPickerActivity a) { @@ -348,24 +354,11 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } @Override - public void setCropViewTileSource(BitmapSource bitmapSource, - boolean touchEnabled, - boolean moveToLeft, - final Runnable postExecute) { - // we also want to show our own wallpaper instead of the one in the background - Runnable showPostExecuteRunnable = new Runnable() { - @Override - public void run() { - if(postExecute != null) { - postExecute.run(); - } - setSystemWallpaperVisiblity(false); - } - }; - super.setCropViewTileSource(bitmapSource, - touchEnabled, - moveToLeft, - showPostExecuteRunnable); + protected void onLoadRequestComplete(LoadRequest req, boolean success) { + super.onLoadRequestComplete(req, success); + if (success) { + setSystemWallpaperVisiblity(false); + } } // called by onCreate; this is subclassed to overwrite WallpaperCropActivity @@ -375,6 +368,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mCropView = (CropView) findViewById(R.id.cropView); mCropView.setVisibility(View.INVISIBLE); + mProgressView = findViewById(R.id.loading); + + mWallpaperStrip = findViewById(R.id.wallpaper_strip); mCropView.setTouchCallback(new CropView.TouchCallback() { ViewPropertyAnimator mAnim; diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java index 66ece4ff6..15f97e5b1 100644 --- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java +++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java @@ -20,7 +20,6 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Canvas; @@ -28,7 +27,6 @@ import android.graphics.Paint; import android.graphics.Rect; import android.net.Uri; import android.os.Build; -import android.os.Build.VERSION_CODES; import android.util.Log; import com.android.gallery3d.common.BitmapUtils; @@ -148,8 +146,6 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { private static final String TAG = "BitmapRegionTileSource"; - private static final boolean REUSE_BITMAP = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; private static final int GL_SIZE_LIMIT = 2048; // This must be no larger than half the size of the GL_SIZE_LIMIT // due to decodePreview being allowed to be up to 2x the size of the target @@ -158,14 +154,14 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { public static abstract class BitmapSource { private SimpleBitmapRegionDecoder mDecoder; private Bitmap mPreview; - private int mPreviewSize; + private final int mPreviewSize; private int mRotation; public enum State { NOT_LOADED, LOADED, ERROR_LOADING }; private State mState = State.NOT_LOADED; public BitmapSource(int previewSize) { - mPreviewSize = previewSize; + mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); } - public boolean loadInBackground() { + public boolean loadInBackground(InBitmapProvider bitmapProvider) { ExifInterface ei = new ExifInterface(); if (readExif(ei)) { Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); @@ -181,15 +177,33 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { int width = mDecoder.getWidth(); int height = mDecoder.getHeight(); if (mPreviewSize != 0) { - int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE); BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inPreferredConfig = Bitmap.Config.ARGB_8888; opts.inPreferQualityOverSpeed = true; - float scale = (float) previewSize / Math.max(width, height); + float scale = (float) mPreviewSize / Math.max(width, height); opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); opts.inJustDecodeBounds = false; - mPreview = loadPreviewBitmap(opts); + opts.inMutable = true; + + if (bitmapProvider != null) { + int expectedPixles = (width / opts.inSampleSize) * (height / opts.inSampleSize); + Bitmap reusableBitmap = bitmapProvider.forPixelCount(expectedPixles); + if (reusableBitmap != null) { + // Try loading with reusable bitmap + opts.inBitmap = reusableBitmap; + try { + mPreview = loadPreviewBitmap(opts); + } catch (IllegalArgumentException e) { + Log.d(TAG, "Unable to reusage bitmap", e); + opts.inBitmap = null; + mPreview = null; + } + } + } + if (mPreview == null) { + mPreview = loadPreviewBitmap(opts); + } } mState = State.LOADED; return true; @@ -208,10 +222,6 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { return mPreview; } - public int getPreviewSize() { - return mPreviewSize; - } - public int getRotation() { return mRotation; } @@ -219,6 +229,10 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { public abstract boolean readExif(ExifInterface ei); public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder(); public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options); + + public interface InBitmapProvider { + Bitmap forPixelCount(int count); + } } public static class FilePathBitmapSource extends BitmapSource { @@ -306,13 +320,13 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { Utils.closeSilently(is); return true; } catch (FileNotFoundException e) { - Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); + Log.d("BitmapRegionTileSource", "Failed to load URI " + mUri, e); return false; } catch (IOException e) { - Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); + Log.d("BitmapRegionTileSource", "Failed to load URI " + mUri, e); return false; } catch (NullPointerException e) { - Log.e("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e); + Log.d("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e); return false; } finally { Utils.closeSilently(is); @@ -372,11 +386,9 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { // For use only by getTile private Rect mWantRegion = new Rect(); - private Rect mOverlapRegion = new Rect(); private BitmapFactory.Options mOptions; - private Canvas mCanvas; - public BitmapRegionTileSource(Context context, BitmapSource source) { + public BitmapRegionTileSource(Context context, BitmapSource source, byte[] tempStorage) { mTileSize = TiledImageRenderer.suggestedTileSize(context); mRotation = source.getRotation(); mDecoder = source.getBitmapRegionDecoder(); @@ -386,27 +398,26 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { mOptions = new BitmapFactory.Options(); mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; mOptions.inPreferQualityOverSpeed = true; - mOptions.inTempStorage = new byte[16 * 1024]; - int previewSize = source.getPreviewSize(); - if (previewSize != 0) { - previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); - // Although this is the same size as the Bitmap that is likely already - // loaded, the lifecycle is different and interactions are on a different - // thread. Thus to simplify, this source will decode its own bitmap. - Bitmap preview = decodePreview(source, previewSize); - if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) { + mOptions.inTempStorage = tempStorage; + + Bitmap preview = source.getPreviewBitmap(); + if (preview != null && + preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) { mPreview = new BitmapTexture(preview); - } else { - Log.w(TAG, String.format( - "Failed to create preview of apropriate size! " - + " in: %dx%d, out: %dx%d", - mWidth, mHeight, - preview.getWidth(), preview.getHeight())); - } + } else { + Log.w(TAG, String.format( + "Failed to create preview of apropriate size! " + + " in: %dx%d, out: %dx%d", + mWidth, mHeight, + preview.getWidth(), preview.getHeight())); } } } + public Bitmap getBitmap() { + return mPreview instanceof BitmapTexture ? ((BitmapTexture) mPreview).getBitmap() : null; + } + @Override public int getTileSize() { return mTileSize; @@ -435,10 +446,6 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { @Override public Bitmap getTile(int level, int x, int y, Bitmap bitmap) { int tileSize = getTileSize(); - if (!REUSE_BITMAP) { - return getTileWithoutReusingBitmap(level, x, y, tileSize); - } - int t = tileSize << level; mWantRegion.set(x, y, x + t, y + t); @@ -462,64 +469,4 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { } return bitmap; } - - private Bitmap getTileWithoutReusingBitmap( - int level, int x, int y, int tileSize) { - - int t = tileSize << level; - mWantRegion.set(x, y, x + t, y + t); - - mOverlapRegion.set(0, 0, mWidth, mHeight); - - mOptions.inSampleSize = (1 << level); - Bitmap bitmap = mDecoder.decodeRegion(mOverlapRegion, mOptions); - - if (bitmap == null) { - Log.w(TAG, "fail in decoding region"); - } - - if (mWantRegion.equals(mOverlapRegion)) { - return bitmap; - } - - Bitmap result = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888); - if (mCanvas == null) { - mCanvas = new Canvas(); - } - mCanvas.setBitmap(result); - mCanvas.drawBitmap(bitmap, - (mOverlapRegion.left - mWantRegion.left) >> level, - (mOverlapRegion.top - mWantRegion.top) >> level, null); - mCanvas.setBitmap(null); - return result; - } - - /** - * Note that the returned bitmap may have a long edge that's longer - * than the targetSize, but it will always be less than 2x the targetSize - */ - private Bitmap decodePreview(BitmapSource source, int targetSize) { - Bitmap result = source.getPreviewBitmap(); - if (result == null) { - return null; - } - - // We need to resize down if the decoder does not support inSampleSize - // or didn't support the specified inSampleSize (some decoders only do powers of 2) - float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight())); - - if (scale <= 0.5) { - result = BitmapUtils.resizeBitmapByScale(result, scale, true); - } - return ensureGLCompatibleBitmap(result); - } - - private static Bitmap ensureGLCompatibleBitmap(Bitmap bitmap) { - if (bitmap == null || bitmap.getConfig() != null) { - return bitmap; - } - Bitmap newBitmap = bitmap.copy(Config.ARGB_8888, false); - bitmap.recycle(); - return newBitmap; - } } diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java index 524fa2e47..56ee7a658 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java @@ -125,6 +125,10 @@ public class TiledImageView extends FrameLayout { invalidate(); } + public TileSource getTileSource() { + return mRenderer.source; + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { -- cgit v1.2.3 From 9f9d0a59c4dab713b6cd8f5a29ea20f646bc5ac0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 25 Feb 2015 11:34:17 -0800 Subject: Picking a bitmap to reuse where least pixels are wasted Change-Id: I4217bc68a5caa2d1526e4ebb101dbaf0348066d3 --- .../android/launcher3/WallpaperCropActivity.java | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index 0ddb79e4f..a3a3c537b 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -50,7 +50,6 @@ import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider; import com.android.photos.views.TiledImageRenderer.TileSource; import java.util.Collections; -import java.util.Iterator; import java.util.Set; import java.util.WeakHashMap; @@ -170,17 +169,23 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback @Override public Bitmap forPixelCount(int count) { + Bitmap bitmapToReuse = null; + // Find the smallest bitmap that satisfies the pixel count limit synchronized (mReusableBitmaps) { - Iterator itr = mReusableBitmaps.iterator(); - while (itr.hasNext()) { - Bitmap b = itr.next(); - if (b.getWidth() * b.getHeight() >= count) { - itr.remove(); - return b; + int currentBitmapSize = Integer.MAX_VALUE; + for (Bitmap b : mReusableBitmaps) { + int bitmapSize = b.getWidth() * b.getHeight(); + if ((bitmapSize >= count) && (bitmapSize < currentBitmapSize)) { + bitmapToReuse = b; + currentBitmapSize = bitmapSize; } } + + if (bitmapToReuse != null) { + mReusableBitmaps.remove(bitmapToReuse); + } } - return null; + return bitmapToReuse; } }); } catch (SecurityException securityException) { -- cgit v1.2.3 From 633325c4c22061c745ff89f9141f18cca29b352b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 25 Feb 2015 10:46:34 -0800 Subject: DO NOT MERGE: Setting workspace id as primary key Bug: 19475231 Change-Id: I0385e97868db73dafe148ce45af7dda4555ee593 --- src/com/android/launcher3/LauncherProvider.java | 61 +++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 0088f26cf..ec033b3f7 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -58,7 +58,7 @@ public class LauncherProvider extends ContentProvider { private static final boolean LOGD = false; private static final int MIN_DATABASE_VERSION = 12; - private static final int DATABASE_VERSION = 21; + private static final int DATABASE_VERSION = 22; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -235,7 +235,7 @@ public class LauncherProvider extends ContentProvider { } } - private void addModifiedTime(ContentValues values) { + private static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -434,7 +434,7 @@ public class LauncherProvider extends ContentProvider { private void addWorkspacesTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" + - LauncherSettings.WorkspaceScreens._ID + " INTEGER," + + LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," + LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," + LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" + ");"); @@ -599,6 +599,13 @@ public class LauncherProvider extends ContentProvider { } } + if (version == 21) { + // Recreate workspace table with screen id a primary key + if (recreateWorkspaceTable(db)) { + version = 22; + } + } + if (version != DATABASE_VERSION) { Log.w(TAG, "Destroying all old data."); createEmptyDB(db); @@ -623,6 +630,54 @@ public class LauncherProvider extends ContentProvider { onCreate(db); } + /** + * Recreates workspace table and migrates data to the new table. + */ + public boolean recreateWorkspaceTable(SQLiteDatabase db) { + db.beginTransaction(); + try { + Cursor c = db.query(TABLE_WORKSPACE_SCREENS, + new String[] {LauncherSettings.WorkspaceScreens._ID}, + null, null, null, null, + LauncherSettings.WorkspaceScreens.SCREEN_RANK); + ArrayList sortedIDs = new ArrayList(); + long maxId = 0; + try { + while (c.moveToNext()) { + Long id = c.getLong(0); + if (!sortedIDs.contains(id)) { + sortedIDs.add(id); + maxId = Math.max(maxId, id); + } + } + } finally { + c.close(); + } + + db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS); + addWorkspacesTable(db); + + // Add all screen ids back + int total = sortedIDs.size(); + for (int i = 0; i < total; i++) { + ContentValues values = new ContentValues(); + values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i)); + values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); + addModifiedTime(values); + db.insertOrThrow(TABLE_WORKSPACE_SCREENS, null, values); + } + db.setTransactionSuccessful(); + mMaxScreenId = maxId; + } catch (SQLException ex) { + // Old version remains, which means we wipe old data + Log.e(TAG, ex.getMessage(), ex); + return false; + } finally { + db.endTransaction(); + } + return true; + } + private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { db.beginTransaction(); try { -- cgit v1.2.3 From 1587d5362f5a66e005aa36fdfc7082c34b8ea3b7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 29 Jan 2015 09:57:16 -0800 Subject: Refactoring common methods Change-Id: Id6d3072dd3a6d7f54e9591abbeffd9bd51c7403d --- res/values-sw720dp/dimens.xml | 5 ---- res/values/dimens.xml | 4 --- src/com/android/launcher3/CellLayout.java | 6 +---- src/com/android/launcher3/DropTarget.java | 23 +++++++++++++++++ src/com/android/launcher3/Folder.java | 36 +------------------------- src/com/android/launcher3/Workspace.java | 42 +++---------------------------- 6 files changed, 28 insertions(+), 88 deletions(-) diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index 8be996474..68fc1ecaf 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -21,11 +21,6 @@ 8dip 8dip - - 0dp - 0dp - 96dp 320dp diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 54689ec25..d6fc508d1 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -68,10 +68,6 @@ or right while you're dragging. --> 20dp - - 0dp - 0dp 12dp diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 72e28918f..a3500aa82 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -613,11 +613,7 @@ public class CellLayout extends ViewGroup { if (lp.cellVSpan < 0) lp.cellVSpan = mCountY; child.setId(childId); - if (inLayout) { - mShortcutsAndWidgets.addView(child, index, lp, true); - } else { - mShortcutsAndWidgets.addView(child, index, lp, false); - } + mShortcutsAndWidgets.addView(child, index, lp, inLayout); if (markCells) markCellsAsOccupiedForView(child); diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 64f0ac867..7ede42751 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -65,6 +65,29 @@ public interface DropTarget { public DragObject() { } + + /** + * This is used to compute the visual center of the dragView. This point is then + * used to visualize drop locations and determine where to drop an item. The idea is that + * the visual center represents the user's interpretation of where the item is, and hence + * is the appropriate point to use when determining drop location. + */ + public final float[] getVisualCenter(float[] recycle) { + final float res[] = (recycle == null) ? new float[2] : recycle; + + // These represent the visual top and left of drag view if a dragRect was provided. + // If a dragRect was not provided, then they correspond to the actual view left and + // top, as the dragRect is in that case taken to be the entire dragView. + // R.dimen.dragViewOffsetY. + int left = x - xOffset; + int top = y - yOffset; + + // In order to find the visual center, we shift by half the dragRect + res[0] = left + dragView.getDragRegion().width() / 2; + res[1] = top + dragView.getDragRegion().height() / 2; + + return res; + } } public static class DragEnforcer implements DragController.DragListener { diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 69254776c..786d4a257 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -27,7 +27,6 @@ import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; -import android.os.SystemClock; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -53,7 +52,6 @@ import com.android.launcher3.FolderInfo.FolderListener; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; /** * Represents a set of icons chosen by the user or generated by the system. @@ -704,17 +702,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void onDragOver(DragObject d) { - final DragView dragView = d.dragView; final int scrollOffset = mScrollView.getScrollY(); - final float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, dragView, null); + final float[] r = d.getVisualCenter(null); r[0] -= getPaddingLeft(); r[1] -= getPaddingTop(); - final long downTime = SystemClock.uptimeMillis(); - final MotionEvent translatedEv = MotionEvent.obtain( - downTime, downTime, MotionEvent.ACTION_MOVE, d.x, d.y, 0); - - translatedEv.recycle(); mTargetCell = mContent.findNearestArea( (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); if (isLayoutRtl()) { @@ -730,32 +722,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } - // This is used to compute the visual center of the dragView. The idea is that - // the visual center represents the user's interpretation of where the item is, and hence - // is the appropriate point to use when determining drop location. - private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset, - DragView dragView, float[] recycle) { - float res[]; - if (recycle == null) { - res = new float[2]; - } else { - res = recycle; - } - - // These represent the visual top and left of drag view if a dragRect was provided. - // If a dragRect was not provided, then they correspond to the actual view left and - // top, as the dragRect is in that case taken to be the entire dragView. - // R.dimen.dragViewOffsetY. - int left = x - xOffset; - int top = y - yOffset; - - // In order to find the visual center, we shift by half the dragRect - res[0] = left + dragView.getDragRegion().width() / 2; - res[1] = top + dragView.getDragRegion().height() / 2; - - return res; - } - OnAlarmListener mOnExitAlarmListener = new OnAlarmListener() { public void onAlarm(Alarm alarm) { completeDragExit(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 4e16a451a..626154e7c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2831,8 +2831,7 @@ public class Workspace extends SmoothPagedView } if (!transitionStateShouldAllowDrop()) return false; - mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, - d.dragView, mDragViewVisualCenter); + mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter); // We want the point to be mapped to the dragTarget. if (mLauncher.isHotseatLayout(dropTargetLayout)) { @@ -3034,9 +3033,7 @@ public class Workspace extends SmoothPagedView } public void onDrop(final DragObject d) { - mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, - mDragViewVisualCenter); - + mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter); CellLayout dropTargetLayout = mDropToLayout; // We want the point to be mapped to the dragTarget. @@ -3542,38 +3539,6 @@ public class Workspace extends SmoothPagedView return bestMatchingScreen; } - // This is used to compute the visual center of the dragView. This point is then - // used to visualize drop locations and determine where to drop an item. The idea is that - // the visual center represents the user's interpretation of where the item is, and hence - // is the appropriate point to use when determining drop location. - private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset, - DragView dragView, float[] recycle) { - float res[]; - if (recycle == null) { - res = new float[2]; - } else { - res = recycle; - } - - // First off, the drag view has been shifted in a way that is not represented in the - // x and y values or the x/yOffsets. Here we account for that shift. - x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX); - y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); - - // These represent the visual top and left of drag view if a dragRect was provided. - // If a dragRect was not provided, then they correspond to the actual view left and - // top, as the dragRect is in that case taken to be the entire dragView. - // R.dimen.dragViewOffsetY. - int left = x - xOffset; - int top = y - yOffset; - - // In order to find the visual center, we shift by half the dragRect - res[0] = left + dragView.getDragRegion().width() / 2; - res[1] = top + dragView.getDragRegion().height() / 2; - - return res; - } - private boolean isDragWidget(DragObject d) { return (d.dragInfo instanceof LauncherAppWidgetInfo || d.dragInfo instanceof PendingAddWidgetInfo); @@ -3598,8 +3563,7 @@ public class Workspace extends SmoothPagedView // Ensure that we have proper spans for the item that we are dropping if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found"); - mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, - d.dragView, mDragViewVisualCenter); + mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter); final View child = (mDragInfo == null) ? null : mDragInfo.cell; // Identify whether we have dragged over a side page -- cgit v1.2.3 From 4fbc3828c5ae1e8c5789ede974447fa365f3c5a1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Feb 2015 16:46:50 -0800 Subject: Updating IconCache to maintain a persistent of icons > IconDB maintains a DB of icons keyed on ComponentName + User > During loader, icons & labels are loaded first from the DB, and if the entry doesn't exist, loaded using packageManager > After the loader completes, IconDB updates any entry which might have changed, while the launcher was dead. Change-Id: I7a6021cb6d1ca1e66fa5a0bdd21e1543e0cf66fc --- src/com/android/launcher3/AllAppsList.java | 13 +- src/com/android/launcher3/AppInfo.java | 10 +- src/com/android/launcher3/IconCache.java | 413 +++++++++++++-------- src/com/android/launcher3/Launcher.java | 2 +- .../android/launcher3/LauncherBackupHelper.java | 25 +- src/com/android/launcher3/LauncherFiles.java | 4 +- src/com/android/launcher3/LauncherModel.java | 234 +++--------- src/com/android/launcher3/ShortcutInfo.java | 10 +- src/com/android/launcher3/Utilities.java | 11 + src/com/android/launcher3/Workspace.java | 2 +- 10 files changed, 358 insertions(+), 366 deletions(-) diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 72c6693b3..5ed7a629a 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -98,14 +98,14 @@ class AllAppsList { user); for (LauncherActivityInfoCompat info : matches) { - add(new AppInfo(context, info, user, mIconCache, null)); + add(new AppInfo(context, info, user, mIconCache)); } } /** * Remove the apps for the given apk identified by packageName. */ - public void removePackage(String packageName, UserHandleCompat user, boolean clearCache) { + public void removePackage(String packageName, UserHandleCompat user) { final List data = this.data; for (int i = data.size() - 1; i >= 0; i--) { AppInfo info = data.get(i); @@ -115,9 +115,6 @@ class AllAppsList { data.remove(i); } } - if (clearCache) { - mIconCache.remove(packageName, user); - } } /** @@ -137,7 +134,6 @@ class AllAppsList { && packageName.equals(component.getPackageName())) { if (!findActivity(matches, component)) { removed.add(applicationInfo); - mIconCache.remove(component, user); data.remove(i); } } @@ -150,10 +146,9 @@ class AllAppsList { info.getComponentName().getPackageName(), user, info.getComponentName().getClassName()); if (applicationInfo == null) { - add(new AppInfo(context, info, user, mIconCache, null)); + add(new AppInfo(context, info, user, mIconCache)); } else { - mIconCache.remove(applicationInfo.componentName, user); - mIconCache.getTitleAndIcon(applicationInfo, info, null); + mIconCache.getTitleAndIcon(applicationInfo, info); modified.add(applicationInfo); } } diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index a66bac08a..455c6d16d 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -19,19 +19,15 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.util.Log; import com.android.launcher3.compat.LauncherActivityInfoCompat; -import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; /** * Represents an app in AllAppsView. @@ -77,13 +73,13 @@ public class AppInfo extends ItemInfo { * Must not hold the Context. */ public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, - IconCache iconCache, HashMap labelCache) { + IconCache iconCache) { this.componentName = info.getComponentName(); this.container = ItemInfo.NO_ID; flags = initFlags(info); firstInstallTime = info.getFirstInstallTime(); - iconCache.getTitleAndIcon(this, info, labelCache); + iconCache.getTitleAndIcon(this, info); intent = makeLaunchIntent(context, info, user); this.user = user; } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 5a0875b30..91d4aaf21 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -18,15 +18,19 @@ package com.android.launcher3; import android.app.ActivityManager; import android.content.ComponentName; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.text.TextUtils; @@ -37,15 +41,10 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map.Entry; /** @@ -56,7 +55,6 @@ public class IconCache { private static final String TAG = "Launcher.IconCache"; private static final int INITIAL_ICON_CACHE_CAPACITY = 50; - private static final String RESOURCE_FILE_PREFIX = "icon_"; // Empty class name is used for storing package default entry. private static final String EMPTY_CLASS_NAME = "."; @@ -98,7 +96,8 @@ public class IconCache { private final LauncherAppsCompat mLauncherApps; private final HashMap mCache = new HashMap(INITIAL_ICON_CACHE_CAPACITY); - private int mIconDpi; + private final int mIconDpi; + private final IconDB mIconDb; public IconCache(Context context) { ActivityManager activityManager = @@ -109,13 +108,10 @@ public class IconCache { mUserManager = UserManagerCompat.getInstance(mContext); mLauncherApps = LauncherAppsCompat.getInstance(mContext); mIconDpi = activityManager.getLauncherLargeIconDensity(); - - // need to set mIconDpi before getting default icon - UserHandleCompat myUser = UserHandleCompat.myUserHandle(); - mDefaultIcons.put(myUser, makeDefaultIcon(myUser)); + mIconDb = new IconDB(context); } - public Drawable getFullResDefaultActivityIcon() { + private Drawable getFullResDefaultActivityIcon() { return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon); } @@ -188,9 +184,9 @@ public class IconCache { } /** - * Remove any records for the supplied package name. + * Remove any records for the supplied package name from memory. */ - public synchronized void remove(String packageName, UserHandleCompat user) { + private void removeFromMemCacheLocked(String packageName, UserHandleCompat user) { HashSet forDeletion = new HashSet(); for (CacheKey key: mCache.keySet()) { if (key.componentName.getPackageName().equals(packageName) @@ -203,6 +199,140 @@ public class IconCache { } } + /** + * Updates the entries related to the given package in memory and persistent DB. + */ + public synchronized void updateIconsForPkg(String packageName, UserHandleCompat user) { + removeIconsForPkg(packageName, user); + try { + PackageInfo info = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES); + long userSerial = mUserManager.getSerialNumberForUser(user); + for (LauncherActivityInfoCompat app : mLauncherApps.getActivityList(packageName, user)) { + addIconToDB(app, info, userSerial); + } + } catch (NameNotFoundException e) { + Log.d(TAG, "Package not found", e); + return; + } + } + + /** + * Removes the entries related to the given package in memory and persistent DB. + */ + public synchronized void removeIconsForPkg(String packageName, UserHandleCompat user) { + removeFromMemCacheLocked(packageName, user); + long userSerial = mUserManager.getSerialNumberForUser(user); + mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, + IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?", + new String[] {packageName + "/%", Long.toString(userSerial)}); + } + + /** + * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in + * the DB and are updated. + * @return The set of packages for which icons have updated. + */ + public HashSet updateDBIcons(UserHandleCompat user, List apps) { + long userSerial = mUserManager.getSerialNumberForUser(user); + PackageManager pm = mContext.getPackageManager(); + HashMap pkgInfoMap = new HashMap(); + for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { + pkgInfoMap.put(info.packageName, info); + } + + HashMap componentMap = new HashMap<>(); + for (LauncherActivityInfoCompat app : apps) { + componentMap.put(app.getComponentName(), app); + } + + Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, + new String[] {IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION}, + IconDB.COLUMN_USER + " = ? ", + new String[] {Long.toString(userSerial)}, + null, null, null); + + final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); + final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); + final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); + final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + + HashSet itemsToRemove = new HashSet(); + HashSet updatedPackages = new HashSet(); + + while (c.moveToNext()) { + String cn = c.getString(indexComponent); + ComponentName component = ComponentName.unflattenFromString(cn); + PackageInfo info = pkgInfoMap.get(component.getPackageName()); + if (info == null) { + itemsToRemove.add(c.getInt(rowIndex)); + continue; + } + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { + // Application is not present + continue; + } + + long updateTime = c.getLong(indexLastUpdate); + int version = c.getInt(indexVersion); + LauncherActivityInfoCompat app = componentMap.remove(component); + if (version == info.versionCode && updateTime == info.lastUpdateTime) { + continue; + } + if (app == null) { + itemsToRemove.add(c.getInt(rowIndex)); + continue; + } + ContentValues values = updateCacheAndGetContentValues(app); + mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, + IconDB.COLUMN_COMPONENT + " = ?", + new String[] { cn }); + + updatedPackages.add(component.getPackageName()); + } + c.close(); + if (!itemsToRemove.isEmpty()) { + mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, + IconDB.COLUMN_ROWID + " IN ( " + TextUtils.join(", ", itemsToRemove) +" )", + null); + } + + // Insert remaining apps. + for (LauncherActivityInfoCompat app : componentMap.values()) { + PackageInfo info = pkgInfoMap.get(app.getComponentName().getPackageName()); + if (info == null) { + continue; + } + addIconToDB(app, info, userSerial); + } + return updatedPackages; + } + + private void addIconToDB(LauncherActivityInfoCompat app, PackageInfo info, long userSerial) { + ContentValues values = updateCacheAndGetContentValues(app); + values.put(IconDB.COLUMN_COMPONENT, app.getComponentName().flattenToString()); + values.put(IconDB.COLUMN_USER, userSerial); + values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime); + values.put(IconDB.COLUMN_VERSION, info.versionCode); + mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + } + + private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app) { + CacheEntry entry = new CacheEntry(); + entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext); + entry.title = app.getLabel(); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); + mCache.put(new CacheKey(app.getComponentName(), app.getUser()), entry); + + ContentValues values = new ContentValues(); + values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(entry.icon)); + values.put(IconDB.COLUMN_LABEL, entry.title.toString()); + return values; + } + + /** * Empty out the cache. */ @@ -227,10 +357,8 @@ public class IconCache { /** * Fill in "application" with the icon and label for "info." */ - public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, - HashMap labelCache) { - CacheEntry entry = cacheLocked(application.componentName, info, labelCache, - info.getUser(), false); + public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info) { + CacheEntry entry = cacheLocked(application.componentName, info, info.getUser(), false); application.title = entry.title; application.iconBitmap = entry.icon; @@ -246,15 +374,16 @@ public class IconCache { } LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, true); + CacheEntry entry = cacheLocked(component, launcherActInfo, user, true); return entry.icon; } /** - * Fill in "shortcutInfo" with the icon and label for "info." + * Fill in {@param shortcutInfo} with the icon and label for {@param intent}. If the + * corresponding activity is not found, it reverts to the package icon. */ public synchronized void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, - UserHandleCompat user, boolean usePkgIcon) { + UserHandleCompat user) { ComponentName component = intent.getComponent(); // null info means not installed, but if we have a component from the intent then // we should still look in the cache for restored app icons. @@ -263,16 +392,22 @@ public class IconCache { shortcutInfo.title = ""; shortcutInfo.usingFallbackIcon = true; } else { - LauncherActivityInfoCompat launcherActInfo = - mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon); - - shortcutInfo.setIcon(entry.icon); - shortcutInfo.title = entry.title; - shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); + LauncherActivityInfoCompat info = mLauncherApps.resolveActivity(intent, user); + getTitleAndIcon(shortcutInfo, component, info, user, true); } } + /** + * Fill in {@param shortcutInfo} with the icon and label for {@param info} + */ + public synchronized void getTitleAndIcon( + ShortcutInfo shortcutInfo, ComponentName component, LauncherActivityInfoCompat info, + UserHandleCompat user, boolean usePkgIcon) { + CacheEntry entry = cacheLocked(component, info, user, usePkgIcon); + shortcutInfo.setIcon(entry.icon); + shortcutInfo.title = entry.title; + shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); + } public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { if (!mDefaultIcons.containsKey(user)) { @@ -281,16 +416,6 @@ public class IconCache { return mDefaultIcons.get(user); } - public synchronized Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info, - HashMap labelCache) { - if (info == null || component == null) { - return null; - } - - CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false); - return entry.icon; - } - public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) { return mDefaultIcons.get(user) == icon; } @@ -300,35 +425,17 @@ public class IconCache { * This method is not thread safe, it must be called from a synchronized method. */ private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info, - HashMap labelCache, UserHandleCompat user, boolean usePackageIcon) { + UserHandleCompat user, boolean usePackageIcon) { CacheKey cacheKey = new CacheKey(componentName, user); CacheEntry entry = mCache.get(cacheKey); if (entry == null) { entry = new CacheEntry(); - mCache.put(cacheKey, entry); - if (info != null) { - ComponentName labelKey = info.getComponentName(); - if (labelCache != null && labelCache.containsKey(labelKey)) { - entry.title = labelCache.get(labelKey).toString(); - } else { - entry.title = info.getLabel().toString(); - if (labelCache != null) { - labelCache.put(labelKey, entry.title); - } - } - - entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); - entry.icon = Utilities.createIconBitmap( - info.getBadgedIcon(mIconDpi), mContext); - } else { - entry.title = ""; - Bitmap preloaded = getPreloadedIcon(componentName, user); - if (preloaded != null) { - if (DEBUG) Log.d(TAG, "using preloaded icon for " + - componentName.toShortString()); - entry.icon = preloaded; + // Check the DB first. + if (!getEntryFromDB(componentName, user, entry)) { + if (info != null) { + entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext); } else { if (usePackageIcon) { CacheEntry packageEntry = getEntryForPackage( @@ -338,6 +445,7 @@ public class IconCache { componentName.toShortString()); entry.icon = packageEntry.icon; entry.title = packageEntry.title; + entry.contentDescription = packageEntry.contentDescription; } } if (entry.icon == null) { @@ -347,6 +455,11 @@ public class IconCache { } } } + + if (TextUtils.isEmpty(entry.title) && info != null) { + entry.title = info.getLabel().toString(); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); + } } return entry; } @@ -357,7 +470,7 @@ public class IconCache { */ public synchronized void cachePackageInstallInfo(String packageName, UserHandleCompat user, Bitmap icon, CharSequence title) { - remove(packageName, user); + removeFromMemCacheLocked(packageName, user); CacheEntry entry = getEntryForPackage(packageName, user); if (!TextUtils.isEmpty(title)) { @@ -379,48 +492,36 @@ public class IconCache { if (entry == null) { entry = new CacheEntry(); entry.title = ""; + entry.contentDescription = ""; mCache.put(cacheKey, entry); try { ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); - entry.title = info.loadLabel(mPackageManager); entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext); + entry.title = info.loadLabel(mPackageManager); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); } catch (NameNotFoundException e) { if (DEBUG) Log.d(TAG, "Application not installed " + packageName); } - - if (entry.icon == null) { - entry.icon = getPreloadedIcon(cn, user); - } } return entry; } - public synchronized HashMap getAllIcons() { - HashMap set = new HashMap(); - for (CacheKey ck : mCache.keySet()) { - final CacheEntry e = mCache.get(ck); - set.put(ck.componentName, e.icon); - } - return set; - } - /** * Pre-load an icon into the persistent cache. * *

Queries for a component that does not exist in the package manager * will be answered by the persistent cache. * - * @param context application context * @param componentName the icon should be returned for this component * @param icon the icon to be persisted * @param dpi the native density of the icon */ - public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon, - int dpi) { + public void preloadIcon(ComponentName componentName, Bitmap icon, int dpi, String label, + long userSerial) { // TODO rescale to the correct native DPI try { - PackageManager packageManager = context.getPackageManager(); + PackageManager packageManager = mContext.getPackageManager(); packageManager.getActivityIcon(componentName); // component is present on the system already, do nothing return; @@ -428,100 +529,86 @@ public class IconCache { // pass } - final String key = componentName.flattenToString(); - FileOutputStream resourceFile = null; + ContentValues values = new ContentValues(); + values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString()); + values.put(IconDB.COLUMN_USER, userSerial); + values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon)); + values.put(IconDB.COLUMN_LABEL, label); + mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + } + + private boolean getEntryFromDB(ComponentName component, UserHandleCompat user, CacheEntry entry) { + Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, + new String[] {IconDB.COLUMN_ICON, IconDB.COLUMN_LABEL}, + IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", + new String[] {component.flattenToString(), + Long.toString(mUserManager.getSerialNumberForUser(user))}, + null, null, null); try { - resourceFile = context.openFileOutput(getResourceFilename(componentName), - Context.MODE_PRIVATE); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) { - byte[] buffer = os.toByteArray(); - resourceFile.write(buffer, 0, buffer.length); - } else { - Log.w(TAG, "failed to encode cache for " + key); - return; - } - } catch (FileNotFoundException e) { - Log.w(TAG, "failed to pre-load cache for " + key, e); - } catch (IOException e) { - Log.w(TAG, "failed to pre-load cache for " + key, e); - } finally { - if (resourceFile != null) { - try { - resourceFile.close(); - } catch (IOException e) { - Log.d(TAG, "failed to save restored icon for: " + key, e); + if (c.moveToNext()) { + entry.icon = Utilities.createIconBitmap(c, 0, mContext); + entry.title = c.getString(1); + if (entry.title == null) { + entry.title = ""; + entry.contentDescription = ""; + } else { + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); } + return true; } + } finally { + c.close(); } + return false; } - /** - * Read a pre-loaded icon from the persistent icon cache. - * - * @param componentName the component that should own the icon - * @returns a bitmap if one is cached, or null. - */ - private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) { - final String key = componentName.flattenToShortString(); + private static final class IconDB extends SQLiteOpenHelper { + private final static int DB_VERSION = 1; - // We don't keep icons for other profiles in persistent cache. - if (!user.equals(UserHandleCompat.myUserHandle())) { - return null; + private final static String TABLE_NAME = "icons"; + private final static String COLUMN_ROWID = "rowid"; + private final static String COLUMN_COMPONENT = "componentName"; + private final static String COLUMN_USER = "profileId"; + private final static String COLUMN_LAST_UPDATED = "lastUpdated"; + private final static String COLUMN_VERSION = "version"; + private final static String COLUMN_ICON = "icon"; + private final static String COLUMN_LABEL = "label"; + + public IconDB(Context context) { + super(context, LauncherFiles.APP_ICONS_DB, null, DB_VERSION); } - if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key); - Bitmap icon = null; - FileInputStream resourceFile = null; - try { - resourceFile = mContext.openFileInput(getResourceFilename(componentName)); - byte[] buffer = new byte[1024]; - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - int bytesRead = 0; - while(bytesRead >= 0) { - bytes.write(buffer, 0, bytesRead); - bytesRead = resourceFile.read(buffer, 0, buffer.length); - } - if (DEBUG) Log.d(TAG, "read " + bytes.size()); - icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size()); - if (icon == null) { - Log.w(TAG, "failed to decode pre-load icon for " + key); - } - } catch (FileNotFoundException e) { - if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key); - } catch (IOException e) { - Log.w(TAG, "failed to read pre-load icon for: " + key, e); - } finally { - if(resourceFile != null) { - try { - resourceFile.close(); - } catch (IOException e) { - Log.d(TAG, "failed to manage pre-load icon file: " + key, e); - } - } + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + + COLUMN_COMPONENT + " TEXT NOT NULL, " + + COLUMN_USER + " INTEGER NOT NULL, " + + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_ICON + " BLOB, " + + COLUMN_LABEL + " TEXT, " + + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " + + ");"); } - return icon; - } + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } - /** - * Remove a pre-loaded icon from the persistent icon cache. - * - * @param componentName the component that should own the icon - */ - public void deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) { - // We don't keep icons for other profiles in persistent cache. - if (!user.equals(UserHandleCompat.myUserHandle()) || componentName == null) { - return; + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } } - remove(componentName, user); - boolean success = mContext.deleteFile(getResourceFilename(componentName)); - if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache"); - } - private static String getResourceFilename(ComponentName component) { - String resourceName = component.flattenToShortString(); - String filename = resourceName.replace(File.separatorChar, '_'); - return RESOURCE_FILE_PREFIX + filename; + private void clearDB(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(db); + } } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 58b085480..06a8ab9a2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -5033,7 +5033,7 @@ public class Launcher extends Activity if (activityInfo == null) { return null; } - return new AppInfo(this, activityInfo, myUser, mIconCache, null); + return new AppInfo(this, activityInfo, myUser, mIconCache); } public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption, diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 353bf3f00..97ff32790 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -144,6 +144,7 @@ public class LauncherBackupHelper implements BackupHelper { private final HashSet mExistingKeys; private final ArrayList mKeys; private final ItemTypeMatcher[] mItemTypeMatchers; + private final long mUserSerial; private IconCache mIconCache; private BackupManager mBackupManager; @@ -161,6 +162,9 @@ public class LauncherBackupHelper implements BackupHelper { mKeys = new ArrayList(); restoreSuccessful = true; mItemTypeMatchers = new ItemTypeMatcher[CommonAppTypeParser.SUPPORTED_TYPE_COUNT]; + + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + mUserSerial = userManager.getSerialNumberForUser(UserHandleCompat.myUserHandle()); } private void dataChanged() { @@ -297,6 +301,12 @@ public class LauncherBackupHelper implements BackupHelper { if (!restoreSuccessful) { return; } + if (!initializeIconCache()) { + // During restore we do not need an initialized instance of IconCache. We can create + // a temporary icon cache here, as the process will be rebooted after restore + // is complete. + mIconCache = new IconCache(mContext); + } int dataSize = data.size(); if (mBuffer.length < dataSize) { @@ -601,7 +611,8 @@ public class LauncherBackupHelper implements BackupHelper { Log.w(TAG, "failed to unpack icon for " + key.name); } if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name); - IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(key.name), icon, res.dpi); + mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi, + "" /* label */, mUserSerial); } /** @@ -693,8 +704,8 @@ public class LauncherBackupHelper implements BackupHelper { if (icon == null) { Log.w(TAG, "failed to unpack widget icon for " + key.name); } else { - IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider), - icon, widget.icon.dpi); + mIconCache.preloadIcon(ComponentName.unflattenFromString(widget.provider), + icon, widget.icon.dpi, widget.label, mUserSerial); } } @@ -1145,9 +1156,11 @@ public class LauncherBackupHelper implements BackupHelper { final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); if (appState == null) { - Throwable stackTrace = new Throwable(); - stackTrace.fillInStackTrace(); - Log.w(TAG, "Failed to get app state during backup/restore", stackTrace); + if (DEBUG) { + Throwable stackTrace = new Throwable(); + stackTrace.fillInStackTrace(); + Log.w(TAG, "Failed to get app state during backup/restore", stackTrace); + } return false; } mIconCache = appState.getIconCache(); diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index fa053650f..cedb3975d 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -25,6 +25,7 @@ public class LauncherFiles { WallpaperCropActivity.class.getName(); public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db"; public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; + public static final String APP_ICONS_DB = "app_icons.db"; public static final List ALL_FILES = Collections.unmodifiableList(Arrays.asList( DEFAULT_WALLPAPER_THUMBNAIL, @@ -36,5 +37,6 @@ public class LauncherFiles { STATS_LOG, WALLPAPER_CROP_PREFERENCES_KEY + XML, WALLPAPER_IMAGES_DB, - WIDGET_PREVIEWS_DB)); + WIDGET_PREVIEWS_DB, + APP_ICONS_DB)); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3983835dc..489e3299d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -36,7 +36,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Rect; import android.net.Uri; import android.os.Environment; @@ -163,9 +162,6 @@ public class LauncherModel extends BroadcastReceiver // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders() static final HashMap sBgFolders = new HashMap(); - // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database - static final HashMap sBgDbIconCache = new HashMap(); - // sBgWorkspaceScreens is the ordered set of workspace screens. static final ArrayList sBgWorkspaceScreens = new ArrayList(); @@ -1131,7 +1127,6 @@ public class LauncherModel extends BroadcastReceiver break; } sBgItemsIdMap.remove(item.id); - sBgDbIconCache.remove(item); } } } @@ -1204,7 +1199,6 @@ public class LauncherModel extends BroadcastReceiver synchronized (sBgLock) { sBgItemsIdMap.remove(info.id); sBgFolders.remove(info.id); - sBgDbIconCache.remove(info); sBgWorkspaceItems.remove(info); } @@ -1214,7 +1208,6 @@ public class LauncherModel extends BroadcastReceiver synchronized (sBgLock) { for (ItemInfo childInfo : info.contents) { sBgItemsIdMap.remove(childInfo.id); - sBgDbIconCache.remove(childInfo); } } } @@ -1481,12 +1474,9 @@ public class LauncherModel extends BroadcastReceiver private boolean mLoadAndBindStepFinished; private int mFlags; - private HashMap mLabelCache; - LoaderTask(Context context, boolean isLaunching, int flags) { mContext = context; mIsLaunching = isLaunching; - mLabelCache = new HashMap(); mFlags = flags; } @@ -1635,15 +1625,6 @@ public class LauncherModel extends BroadcastReceiver } } - // Update the saved icons if necessary - if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); - synchronized (sBgLock) { - for (Object key : sBgDbIconCache.keySet()) { - updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key)); - } - sBgDbIconCache.clear(); - } - if (LauncherAppState.isDisableAllApps()) { // Ensure that all the applications that are in the system are // represented on the home screen. @@ -1819,7 +1800,6 @@ public class LauncherModel extends BroadcastReceiver sBgAppWidgets.clear(); sBgFolders.clear(); sBgItemsIdMap.clear(); - sBgDbIconCache.clear(); sBgWorkspaceScreens.clear(); } } @@ -2068,8 +2048,8 @@ public class LauncherModel extends BroadcastReceiver if (itemReplaced) { if (user.equals(UserHandleCompat.myUserHandle())) { - info = getShortcutInfo(manager, intent, user, context, null, - iconIndex, titleIndex, mLabelCache, false); + info = getAppShortcutInfo(manager, intent, user, context, null, + iconIndex, titleIndex, false); } else { // Don't replace items for other profiles. itemsToRemove.add(id); @@ -2089,8 +2069,8 @@ public class LauncherModel extends BroadcastReceiver } } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = getShortcutInfo(manager, intent, user, context, c, - iconIndex, titleIndex, mLabelCache, allowMissingTarget); + info = getAppShortcutInfo(manager, intent, user, context, c, + iconIndex, titleIndex, allowMissingTarget); } else { info = getShortcutInfo(c, context, iconTypeIndex, iconPackageIndex, iconResourceIndex, iconIndex, @@ -2145,10 +2125,6 @@ public class LauncherModel extends BroadcastReceiver break; } sBgItemsIdMap.put(info.id, info); - - // now that we've loaded everthing re-save it with the - // icon in case it disappears somehow. - queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex); } else { throw new RuntimeException("Unexpected null ShortcutInfo"); } @@ -2842,20 +2818,48 @@ public class LauncherModel extends BroadcastReceiver if (apps == null || apps.isEmpty()) { return; } - // Sort the applications by name - final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - Collections.sort(apps, - new LauncherModel.ShortcutNameComparator(mLabelCache)); - if (DEBUG_LOADERS) { - Log.d(TAG, "sort took " - + (SystemClock.uptimeMillis()-sortTime) + "ms"); + + // Update icon cache + HashSet updatedPackages = mIconCache.updateDBIcons(user, apps); + + // If any package icon has changed (app was updated while launcher was dead), + // update the corresponding shortcuts. + if (!updatedPackages.isEmpty()) { + final ArrayList updates = new ArrayList(); + synchronized (sBgLock) { + for (ItemInfo info : sBgItemsIdMap.values()) { + if (info instanceof ShortcutInfo && user.equals(info.user) + && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (cn != null && updatedPackages.contains(cn.getPackageName())) { + si.updateIcon(mIconCache); + updates.add(si); + } + } + } + } + + if (!updates.isEmpty()) { + final UserHandleCompat userFinal = user; + mHandler.post(new Runnable() { + + public void run() { + Callbacks cb = getCallback(); + if (cb != null) { + cb.bindShortcutsChanged( + updates, new ArrayList(), userFinal); + } + } + }); + } } // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfoCompat app = apps.get(i); // This builds the icon bitmaps. - mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache)); + mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache)); } if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) { @@ -2987,7 +2991,7 @@ public class LauncherModel extends BroadcastReceiver case OP_ADD: for (int i=0; i labelCache, boolean allowMissingTarget) { + boolean allowMissingTarget) { if (user == null) { Log.d(TAG, "Null user found in getShortcutInfo"); return null; @@ -3461,48 +3459,22 @@ public class LauncherModel extends BroadcastReceiver } final ShortcutInfo info = new ShortcutInfo(); - - // the resource -- This may implicitly give us back the fallback icon, - // but don't worry about that. All we're doing with usingFallbackIcon is - // to avoid saving lots of copies of that in the database, and most apps - // have icons anyway. - Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache); - - // the db - if (icon == null) { - if (c != null) { - icon = getIconFromCursor(c, iconIndex, context); - } - } - // the fallback icon - if (icon == null) { - icon = mIconCache.getDefaultIcon(user); - info.usingFallbackIcon = true; - } - info.setIcon(icon); - - // From the cache. - if (labelCache != null) { - info.title = labelCache.get(componentName); + mIconCache.getTitleAndIcon(info, componentName, lai, user, false); + if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) { + Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context); + info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon); } - // from the resource - if (info.title == null && lai != null) { - info.title = lai.getLabel(); - if (labelCache != null) { - labelCache.put(componentName, info.title); - } - } // from the db - if (info.title == null) { - if (c != null) { - info.title = c.getString(titleIndex); - } + if (TextUtils.isEmpty(info.title) && c != null) { + info.title = c.getString(titleIndex); } + // fall back to the class name of the activity if (info.title == null) { info.title = componentName.getClassName(); } + info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; info.user = user; info.contentDescription = mUserManager.getBadgedLabelForUser( @@ -3581,7 +3553,7 @@ public class LauncherModel extends BroadcastReceiver icon = Utilities.createIconBitmap(packageName, resourceName, mIconCache, context); // the db if (icon == null) { - icon = getIconFromCursor(c, iconIndex, context); + icon = Utilities.createIconBitmap(c, iconIndex, context); } // the fallback icon if (icon == null) { @@ -3590,7 +3562,7 @@ public class LauncherModel extends BroadcastReceiver } break; case LauncherSettings.Favorites.ICON_TYPE_BITMAP: - icon = getIconFromCursor(c, iconIndex, context); + icon = Utilities.createIconBitmap(c, iconIndex, context); if (icon == null) { icon = mIconCache.getDefaultIcon(info.user); info.customIcon = false; @@ -3609,22 +3581,6 @@ public class LauncherModel extends BroadcastReceiver return info; } - Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) { - @SuppressWarnings("all") // suppress dead code warning - final boolean debug = false; - if (debug) { - Log.d(TAG, "getIconFromCursor app=" - + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE))); - } - byte[] data = c.getBlob(iconIndex); - try { - return Utilities.createIconBitmap( - BitmapFactory.decodeByteArray(data, 0, data.length), context); - } catch (Exception e) { - return null; - } - } - ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); @@ -3673,45 +3629,6 @@ public class LauncherModel extends BroadcastReceiver return info; } - boolean queueIconToBeChecked(HashMap cache, ShortcutInfo info, Cursor c, - int iconIndex) { - // If apps can't be on SD, don't even bother. - if (!mAppsCanBeOnRemoveableStorage) { - return false; - } - // If this icon doesn't have a custom icon, check to see - // what's stored in the DB, and if it doesn't match what - // we're going to show, store what we are going to show back - // into the DB. We do this so when we're loading, if the - // package manager can't find an icon (for example because - // the app is on SD) then we can use that instead. - if (!info.customIcon && !info.usingFallbackIcon) { - cache.put(info, c.getBlob(iconIndex)); - return true; - } - return false; - } - void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) { - boolean needSave = false; - try { - if (data != null) { - Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length); - Bitmap loaded = info.getIcon(mIconCache); - needSave = !saved.sameAs(loaded); - } else { - needSave = true; - } - } catch (Exception e) { - needSave = true; - } - if (needSave) { - Log.d(TAG, "going to save icon bitmap for info=" + info); - // This is slower than is ideal, but this only happens once - // or when the app is updated with a new icon. - updateItemInDatabase(context, info); - } - } - /** * Return an existing FolderInfo object if we have encountered this ID previously, * or make a new one. @@ -3761,38 +3678,7 @@ public class LauncherModel extends BroadcastReceiver return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); } } - public static class ShortcutNameComparator implements Comparator { - private Collator mCollator; - private HashMap mLabelCache; - ShortcutNameComparator(PackageManager pm) { - mLabelCache = new HashMap(); - mCollator = Collator.getInstance(); - } - ShortcutNameComparator(HashMap labelCache) { - mLabelCache = labelCache; - mCollator = Collator.getInstance(); - } - public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) { - String labelA, labelB; - ComponentName keyA = a.getComponentName(); - ComponentName keyB = b.getComponentName(); - if (mLabelCache.containsKey(keyA)) { - labelA = mLabelCache.get(keyA).toString(); - } else { - labelA = a.getLabel().toString().trim(); - - mLabelCache.put(keyA, labelA); - } - if (mLabelCache.containsKey(keyB)) { - labelB = mLabelCache.get(keyB).toString(); - } else { - labelB = b.getLabel().toString().trim(); - mLabelCache.put(keyB, labelB); - } - return mCollator.compare(labelA, labelB); - } - }; public static class WidgetAndShortcutNameComparator implements Comparator { private final AppWidgetManagerCompat mManager; private final PackageManager mPackageManager; diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 15d6a3e1c..08ffaa299 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.util.Log; +import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; @@ -190,8 +191,9 @@ public class ShortcutInfo extends ItemInfo { } public void updateIcon(IconCache iconCache) { - mIcon = iconCache.getIcon(promisedIntent != null ? promisedIntent : intent, user); - usingFallbackIcon = iconCache.isDefaultIcon(mIcon, user); + if (itemType == Favorites.ITEM_TYPE_APPLICATION) { + iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user); + } } @Override @@ -213,9 +215,9 @@ public class ShortcutInfo extends ItemInfo { if (!usingFallbackIcon) { writeBitmap(values, mIcon); } - values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, - LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE); if (iconResource != null) { + values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, + LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE); values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, iconResource.packageName); values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 1a9b9a16c..497b43874 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -31,7 +31,9 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.database.Cursor; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -112,6 +114,15 @@ public final class Utilities { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } + static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { + byte[] data = c.getBlob(iconIndex); + try { + return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); + } catch (Exception e) { + return null; + } + } + /** * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 626154e7c..b9c1f4d3d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4772,7 +4772,7 @@ public class Workspace extends SmoothPagedView if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { // For auto install apps update the icon as well as label. mIconCache.getTitleAndIcon(shortcutInfo, - shortcutInfo.promisedIntent, user, true); + shortcutInfo.promisedIntent, user); } else { // Only update the icon for restored apps. shortcutInfo.updateIcon(mIconCache); -- cgit v1.2.3 From 560616da70648bed04c00aa804503ec72ace1337 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Feb 2015 11:26:19 -0800 Subject: Build fix. Some references of a resources still remained after ag/619757 Change-Id: Ia05a12ff5af6fa13d75cbedd8bd2fd6d33a78b7f --- src/com/android/launcher3/AppsCustomizePagedView.java | 15 --------------- src/com/android/launcher3/DragView.java | 6 ++---- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 9e7e523e0..875cd2108 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -16,8 +16,6 @@ package com.android.launcher3; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -42,7 +40,6 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.Toast; @@ -450,18 +447,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add, Toast.LENGTH_SHORT); mWidgetInstructionToast.show(); - - // Create a little animation to show that the widget can move - float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); - final ImageView p = (ImageView) v.findViewById(R.id.widget_preview); - AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet(); - ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY); - tyuAnim.setDuration(125); - ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f); - tydAnim.setDuration(100); - bounce.play(tyuAnim).before(tydAnim); - bounce.setInterpolator(new AccelerateInterpolator()); - bounce.start(); } public boolean onKey(View v, int keyCode, KeyEvent event) { diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index ea34e46f9..78d72b30f 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -70,8 +70,6 @@ public class DragView extends View { mInitialScale = initialScale; final Resources res = getResources(); - final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX); - final float offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY); final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); final float scale = (width + scaleDps) / width; @@ -87,8 +85,8 @@ public class DragView extends View { public void onAnimationUpdate(ValueAnimator animation) { final float value = (Float) animation.getAnimatedValue(); - final int deltaX = (int) ((value * offsetX) - mOffsetX); - final int deltaY = (int) ((value * offsetY) - mOffsetY); + final int deltaX = (int) (-mOffsetX); + final int deltaY = (int) (-mOffsetY); mOffsetX += deltaX; mOffsetY += deltaY; -- cgit v1.2.3 From 9c83a0da6d6faf4d8a81effd27997ffb12bf3fdd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 13 Feb 2015 10:40:07 -0800 Subject: Cleaning up some resources in WallpaperPickerActivity Change-Id: I4923403c0c094d0ae0b00e3a08a13099c3addb10 --- WallpaperPicker/res/layout/wallpaper_cropper.xml | 3 +- WallpaperPicker/res/layout/wallpaper_picker.xml | 42 +++++++++----- .../layout/wallpaper_picker_image_picker_item.xml | 1 - .../res/layout/wallpaper_picker_item.xml | 1 - .../wallpaper_picker_live_wallpaper_item.xml | 1 - .../layout/wallpaper_picker_third_party_item.xml | 1 - WallpaperPicker/res/values-sw720dp-v19/styles.xml | 2 +- WallpaperPicker/res/values-sw720dp/styles.xml | 2 +- .../launcher3/LiveWallpaperListAdapter.java | 2 - .../ThirdPartyWallpaperPickerListAdapter.java | 2 - .../android/launcher3/WallpaperPickerActivity.java | 64 +++++----------------- .../com/android/launcher3/WallpaperRootView.java | 39 ------------- 12 files changed, 46 insertions(+), 114 deletions(-) delete mode 100644 WallpaperPicker/src/com/android/launcher3/WallpaperRootView.java diff --git a/WallpaperPicker/res/layout/wallpaper_cropper.xml b/WallpaperPicker/res/layout/wallpaper_cropper.xml index abb860898..ffe8df0fb 100644 --- a/WallpaperPicker/res/layout/wallpaper_cropper.xml +++ b/WallpaperPicker/res/layout/wallpaper_cropper.xml @@ -19,7 +19,6 @@ --> - + android:layout_height="match_parent" > + + + android:visibility="invisible" /> + + + - - - - + - + + \ No newline at end of file diff --git a/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml index ae3c43d8e..dc6524486 100644 --- a/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml +++ b/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml @@ -20,7 +20,6 @@ android:layout_height="@dimen/wallpaperThumbnailHeight" android:focusable="true" android:clickable="true" - android:background="@drawable/wallpaper_tile_fg" android:foreground="@drawable/wallpaper_tile_fg"> - diff --git a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java index 0a9050cff..72f2d7e49 100644 --- a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java @@ -90,8 +90,6 @@ public class LiveWallpaperListAdapter extends BaseAdapter implements ListAdapter view = convertView; } - WallpaperPickerActivity.setWallpaperItemPaddingToZero((FrameLayout) view); - LiveWallpaperTile wallpaperInfo = mWallpapers.get(position); wallpaperInfo.setView(view); ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image); diff --git a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java index 7a4d48ca9..27e65aa31 100644 --- a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java @@ -126,8 +126,6 @@ public class ThirdPartyWallpaperPickerListAdapter extends BaseAdapter implements view = convertView; } - WallpaperPickerActivity.setWallpaperItemPaddingToZero((FrameLayout) view); - ResolveInfo info = mThirdPartyWallpaperPickers.get(position).mResolveInfo; TextView label = (TextView) view.findViewById(R.id.wallpaper_item_label); label.setText(info.loadLabel(mPackageManager)); diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index cbed61bd2..d16fc31e5 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -34,11 +34,9 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PorterDuff; -import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.LevelListDrawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -95,7 +93,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { private OnClickListener mThumbnailOnClickListener; private LinearLayout mWallpapersView; - private View mWallpaperStrip; + private HorizontalScrollView mWallpaperScrollContainer; private ActionMode.Callback mActionModeCallback; private ActionMode mActionMode; @@ -313,10 +311,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - public void setWallpaperStripYOffset(float offset) { - mWallpaperStrip.setPadding(0, 0, 0, (int) offset); - } - /** * shows the system wallpaper behind the window and hides the {@link * #mCropView} if visible @@ -369,9 +363,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mCropView.setVisibility(View.INVISIBLE); mProgressView = findViewById(R.id.loading); - - - mWallpaperStrip = findViewById(R.id.wallpaper_strip); + mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); mCropView.setTouchCallback(new CropView.TouchCallback() { ViewPropertyAnimator mAnim; @Override @@ -379,15 +371,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (mAnim != null) { mAnim.cancel(); } - if (mWallpaperStrip.getAlpha() == 1f) { + if (mWallpaperScrollContainer.getAlpha() == 1f) { mIgnoreNextTap = true; } - mAnim = mWallpaperStrip.animate(); + mAnim = mWallpaperScrollContainer.animate(); mAnim.alpha(0f) .setDuration(150) .withEndAction(new Runnable() { public void run() { - mWallpaperStrip.setVisibility(View.INVISIBLE); + mWallpaperScrollContainer.setVisibility(View.INVISIBLE); } }); mAnim.setInterpolator(new AccelerateInterpolator(0.75f)); @@ -405,8 +397,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (mAnim != null) { mAnim.cancel(); } - mWallpaperStrip.setVisibility(View.VISIBLE); - mAnim = mWallpaperStrip.animate(); + mWallpaperScrollContainer.setVisibility(View.VISIBLE); + mAnim = mWallpaperScrollContainer.animate(); mAnim.alpha(1f) .setDuration(150) .setInterpolator(new DecelerateInterpolator(0.75f)); @@ -487,7 +479,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); FrameLayout pickImageTile = (FrameLayout) getLayoutInflater(). inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false); - setWallpaperItemPaddingToZero(pickImageTile); masterWallpaperList.addView(pickImageTile, 0); // Make its background the last photo taken on external storage @@ -659,17 +650,14 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } private void initializeScrollForRtl() { - final HorizontalScrollView scroll = - (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); - - if (scroll.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { - final ViewTreeObserver observer = scroll.getViewTreeObserver(); + if (mWallpaperScrollContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { public void onGlobalLayout() { LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); - scroll.scrollTo(masterWallpaperList.getWidth(), 0); - scroll.getViewTreeObserver().removeOnGlobalLayoutListener(this); + mWallpaperScrollContainer.scrollTo(masterWallpaperList.getWidth(), 0); + mWallpaperScrollContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); } @@ -696,10 +684,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { protected void onStop() { super.onStop(); - mWallpaperStrip = findViewById(R.id.wallpaper_strip); - if (mWallpaperStrip.getAlpha() < 1f) { - mWallpaperStrip.setAlpha(1f); - mWallpaperStrip.setVisibility(View.VISIBLE); + mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); + if (mWallpaperScrollContainer.getAlpha() < 1f) { + mWallpaperScrollContainer.setAlpha(1f); + mWallpaperScrollContainer.setVisibility(View.VISIBLE); } } @@ -823,7 +811,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater(). inflate(R.layout.wallpaper_picker_item, mWallpapersView, false); pickedImageThumbnail.setVisibility(View.GONE); - setWallpaperItemPaddingToZero(pickedImageThumbnail); mWallpapersView.addView(pickedImageThumbnail, 0); // Load the thumbnail @@ -886,11 +873,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - static void setWallpaperItemPaddingToZero(FrameLayout frameLayout) { - frameLayout.setPadding(0, 0, 0, 0); - frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground())); - } - private void addLongPressHandler(View v) { v.setOnLongClickListener(mLongClickListener); } @@ -1089,20 +1071,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return mSavedImages; } - static class ZeroPaddingDrawable extends LevelListDrawable { - public ZeroPaddingDrawable(Drawable d) { - super(); - addLevel(0, 0, d); - setLevel(0); - } - - @Override - public boolean getPadding(Rect padding) { - padding.set(0, 0, 0, 0); - return true; - } - } - private static class SimpleWallpapersAdapter extends ArrayAdapter { private final LayoutInflater mLayoutInflater; @@ -1130,8 +1098,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { view = convertView; } - setWallpaperItemPaddingToZero((FrameLayout) view); - ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image); if (thumb != null) { diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperRootView.java b/WallpaperPicker/src/com/android/launcher3/WallpaperRootView.java deleted file mode 100644 index ceaa043a7..000000000 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperRootView.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2013 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.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.widget.RelativeLayout; - -public class WallpaperRootView extends RelativeLayout { - private final WallpaperPickerActivity a; - public WallpaperRootView(Context context, AttributeSet attrs) { - super(context, attrs); - a = (WallpaperPickerActivity) context; - } - public WallpaperRootView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - a = (WallpaperPickerActivity) context; - } - - protected boolean fitSystemWindows(Rect insets) { - a.setWallpaperStripYOffset(insets.bottom); - return true; - } -} -- cgit v1.2.3 From c9acdd51c40c1b397adae6ba62c4acd01914b473 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Feb 2015 12:34:42 -0800 Subject: Removed disableAllApps flag Change-Id: I50ba511b8493bca2506ab6010f141c093bfa7499 --- .../android/launcher3/AppsCustomizePagedView.java | 30 +++----- src/com/android/launcher3/DeleteDropTarget.java | 22 +----- src/com/android/launcher3/DeviceProfile.java | 2 +- src/com/android/launcher3/DynamicGrid.java | 15 ++-- src/com/android/launcher3/Folder.java | 17 +---- src/com/android/launcher3/Hotseat.java | 88 +++++++--------------- .../android/launcher3/InstallShortcutReceiver.java | 5 -- src/com/android/launcher3/Launcher.java | 28 ++----- src/com/android/launcher3/LauncherAppState.java | 9 --- src/com/android/launcher3/LauncherModel.java | 36 +-------- 10 files changed, 63 insertions(+), 189 deletions(-) diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 875cd2108..c1aa19ae7 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -358,7 +358,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen super.onLayout(changed, l, t, r, b); if (!isDataReady()) { - if ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty()) && !mWidgets.isEmpty()) { + if ((!mApps.isEmpty()) && !mWidgets.isEmpty()) { post(new Runnable() { // This code triggers requestLayout so must be posted outside of the // layout pass. @@ -1402,11 +1402,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } public void setApps(ArrayList list) { - if (!LauncherAppState.isDisableAllApps()) { - mApps = list; - Collections.sort(mApps, LauncherModel.getAppNameComparator()); - updatePageCountsAndInvalidateData(); - } + mApps = list; + Collections.sort(mApps, LauncherModel.getAppNameComparator()); + updatePageCountsAndInvalidateData(); } public ArrayList getApps() { @@ -1425,10 +1423,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } public void addApps(ArrayList list) { - if (!LauncherAppState.isDisableAllApps()) { - addAppsWithoutInvalidate(list); - updatePageCountsAndInvalidateData(); - } + addAppsWithoutInvalidate(list); + updatePageCountsAndInvalidateData(); } private int findAppByComponent(List list, AppInfo item) { ComponentName removeComponent = item.intent.getComponent(); @@ -1454,20 +1450,16 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } public void removeApps(ArrayList appInfos) { - if (!LauncherAppState.isDisableAllApps()) { - removeAppsWithoutInvalidate(appInfos); - updatePageCountsAndInvalidateData(); - } + removeAppsWithoutInvalidate(appInfos); + updatePageCountsAndInvalidateData(); } public void updateApps(ArrayList list) { // We remove and re-add the updated applications list because it's properties may have // changed (ie. the title), and this will ensure that the items will be in their proper // place in the list. - if (!LauncherAppState.isDisableAllApps()) { - removeAppsWithoutInvalidate(list); - addAppsWithoutInvalidate(list); - updatePageCountsAndInvalidateData(); - } + removeAppsWithoutInvalidate(list); + addAppsWithoutInvalidate(list); + updatePageCountsAndInvalidateData(); } public void reset() { diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index ebe874f38..1ada1a912 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -144,13 +144,11 @@ public class DeleteDropTarget extends ButtonDropTarget { return true; } - if (!LauncherAppState.isDisableAllApps() && - item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { return true; } - if (!LauncherAppState.isDisableAllApps() && - item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && item instanceof AppInfo) { AppInfo appInfo = (AppInfo) info; return (appInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0; @@ -158,12 +156,7 @@ public class DeleteDropTarget extends ButtonDropTarget { if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && item instanceof ShortcutInfo) { - if (LauncherAppState.isDisableAllApps()) { - ShortcutInfo shortcutInfo = (ShortcutInfo) info; - return (shortcutInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0; - } else { - return true; - } + return true; } } return false; @@ -173,8 +166,7 @@ public class DeleteDropTarget extends ButtonDropTarget { @Override public void onDragStart(DragSource source, Object info, int dragAction) { boolean isVisible = true; - boolean useUninstallLabel = !LauncherAppState.isDisableAllApps() && - isAllAppsApplication(source, info); + boolean useUninstallLabel = isAllAppsApplication(source, info); boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget(); // If we are dragging an application from AppsCustomize, only show the control if we can @@ -277,12 +269,6 @@ public class DeleteDropTarget extends ButtonDropTarget { } private boolean isUninstallFromWorkspace(DragObject d) { - if (LauncherAppState.isDisableAllApps() && isDragSourceWorkspaceOrFolder(d) - && (d.dragInfo instanceof ShortcutInfo)) { - ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo; - // Only allow manifest shortcuts to initiate an un-install. - return !InstallShortcutReceiver.isValidShortcutLaunchIntent(shortcut.intent); - } return false; } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 34e1f3c5f..b97f0f2f7 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -137,7 +137,7 @@ public class DeviceProfile { DeviceProfile(String n, float w, float h, float r, float c, float is, float its, float hs, float his, int dlId) { // Ensure that we have an odd number of hotseat items (since we need to place all apps) - if (!LauncherAppState.isDisableAllApps() && hs % 2 == 0) { + if (hs % 2 == 0) { throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); } diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java index aa08148d2..24da97fc6 100644 --- a/src/com/android/launcher3/DynamicGrid.java +++ b/src/com/android/launcher3/DynamicGrid.java @@ -56,23 +56,22 @@ public class DynamicGrid { DisplayMetrics dm = resources.getDisplayMetrics(); ArrayList deviceProfiles = new ArrayList(); - boolean hasAA = !LauncherAppState.isDisableAllApps(); DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm); // Our phone profiles include the bar sizes in each orientation deviceProfiles.add(new DeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4)); + 255, 300, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4)); + 255, 400, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Short Stubby", - 275, 420, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4)); + 275, 420, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Stubby", - 255, 450, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4)); + 255, 450, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4)); + 296, 491.33f, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 4", - 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4)); + 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 5", - 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4)); + 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Large Phone", 406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); // The tablet profile is odd in that the landscape orientation diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 786d4a257..11e9835e8 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -143,13 +143,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList Resources res = getResources(); mMaxCountX = (int) grid.numColumns; - // Allow scrolling folders when DISABLE_ALL_APPS is true. - if (LauncherAppState.isDisableAllApps()) { - mMaxCountY = mMaxNumItems = Integer.MAX_VALUE; - } else { - mMaxCountY = (int) grid.numRows; - mMaxNumItems = mMaxCountX * mMaxCountY; - } + mMaxCountY = (int) grid.numRows; + mMaxNumItems = mMaxCountX * mMaxCountY; mInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -1012,13 +1007,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(getContentAreaHeight(), MeasureSpec.EXACTLY); - if (LauncherAppState.isDisableAllApps()) { - // Don't cap the height of the content to allow scrolling. - mContent.setFixedSize(getContentAreaWidth(), mContent.getDesiredHeight()); - } else { - mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight()); - } - + mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight()); mScrollView.measure(contentAreaWidthSpec, contentAreaHeightSpec); mFolderName.measure(contentAreaWidthSpec, MeasureSpec.makeMeasureSpec(mFolderNameHeight, MeasureSpec.EXACTLY)); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index bd6c21ab3..b614bc628 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -24,12 +24,9 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.View; import android.widget.FrameLayout; import android.widget.TextView; -import java.util.ArrayList; - public class Hotseat extends FrameLayout { private CellLayout mContent; @@ -94,19 +91,11 @@ public class Hotseat extends FrameLayout { } public int getAllAppsButtonRank() { - if (LauncherAppState.isDisableAllApps()) { - return -1; - } else { - return mAllAppsButtonRank; - } + return mAllAppsButtonRank; } public boolean isAllAppsButtonRank(int rank) { - if (LauncherAppState.isDisableAllApps()) { - return false; - } else { - return rank == mAllAppsButtonRank; - } + return rank == mAllAppsButtonRank; } /** This returns the coordinates of an app in a given cell, relative to the DragLayer */ @@ -149,35 +138,33 @@ public class Hotseat extends FrameLayout { void resetLayout() { mContent.removeAllViewsInLayout(); - if (!LauncherAppState.isDisableAllApps()) { - // Add the Apps button - Context context = getContext(); - - LayoutInflater inflater = LayoutInflater.from(context); - TextView allAppsButton = (TextView) - inflater.inflate(R.layout.all_apps_button, mContent, false); - Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon); - - Utilities.resizeIconDrawable(d); - allAppsButton.setCompoundDrawables(null, d, null, null); - - allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label)); - allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener()); - if (mLauncher != null) { - allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener()); - mLauncher.setAllAppsButton(allAppsButton); - allAppsButton.setOnClickListener(mLauncher); - allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler); - } - - // Note: We do this to ensure that the hotseat is always laid out in the orientation of - // the hotseat in order regardless of which orientation they were added - int x = getCellXFromOrder(mAllAppsButtonRank); - int y = getCellYFromOrder(mAllAppsButtonRank); - CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1); - lp.canReorder = false; - mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true); + // Add the Apps button + Context context = getContext(); + + LayoutInflater inflater = LayoutInflater.from(context); + TextView allAppsButton = (TextView) + inflater.inflate(R.layout.all_apps_button, mContent, false); + Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon); + + Utilities.resizeIconDrawable(d); + allAppsButton.setCompoundDrawables(null, d, null, null); + + allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label)); + allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener()); + if (mLauncher != null) { + allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener()); + mLauncher.setAllAppsButton(allAppsButton); + allAppsButton.setOnClickListener(mLauncher); + allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler); } + + // Note: We do this to ensure that the hotseat is always laid out in the orientation of + // the hotseat in order regardless of which orientation they were added + int x = getCellXFromOrder(mAllAppsButtonRank); + int y = getCellYFromOrder(mAllAppsButtonRank); + CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1); + lp.canReorder = false; + mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true); } @Override @@ -189,23 +176,4 @@ public class Hotseat extends FrameLayout { } return false; } - - void addAppsToAllAppsFolder(ArrayList apps) { - if (LauncherAppState.isDisableAllApps()) { - View v = mContent.getChildAt(getCellXFromOrder(mAllAppsButtonRank), getCellYFromOrder(mAllAppsButtonRank)); - FolderIcon fi = null; - - if (v instanceof FolderIcon) { - fi = (FolderIcon) v; - } else { - return; - } - - FolderInfo info = fi.getFolderInfo(); - for (AppInfo a: apps) { - ShortcutInfo si = a.makeShortcut(); - info.add(si); - } - } - } } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 1ab308558..201531ea8 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -186,11 +186,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver { final PendingInstallShortcutInfo pendingInfo = iter.next(); final Intent intent = pendingInfo.launchIntent; - if (LauncherAppState.isDisableAllApps() && !isValidShortcutLaunchIntent(intent)) { - if (DBG) Log.d(TAG, "Ignoring shortcut with launchIntent:" + intent); - continue; - } - // If the intent specifies a package, make sure the package exists String packageName = pendingInfo.getTargetPackage(); if (!TextUtils.isEmpty(packageName)) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 58b085480..30c27496b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -167,7 +167,6 @@ public class Launcher extends Activity // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] static final String DUMP_STATE_PROPERTY = "launcher_dump_state"; - static final String DISABLE_ALL_APPS_PROPERTY = "launcher_noallapps"; // The Intent extra that defines whether to ignore the launch animation static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = @@ -3344,8 +3343,7 @@ public class Launcher extends Activity Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN; Animator workspaceAnim = mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews); - if (!LauncherAppState.isDisableAllApps() - || contentType == AppsCustomizePagedView.ContentType.Widgets) { + if (contentType == AppsCustomizePagedView.ContentType.Widgets) { // Set the content type for the all apps/widgets space mAppsCustomizeTabHost.setContentTypeImmediate(contentType); } @@ -4238,8 +4236,7 @@ public class Launcher extends Activity // Remove the extra empty screen mWorkspace.removeExtraEmptyScreen(false, false); - if (!LauncherAppState.isDisableAllApps() && - addedApps != null && mAppsCustomizeContent != null) { + if (addedApps != null && mAppsCustomizeContent != null) { mAppsCustomizeContent.addApps(addedApps); } } @@ -4614,17 +4611,10 @@ public class Launcher extends Activity * Implementation of the method from LauncherModel.Callbacks. */ public void bindAllApplications(final ArrayList apps) { - if (LauncherAppState.isDisableAllApps()) { - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this)); - } - } else { - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setApps(apps); - mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this)); - } + if (mAppsCustomizeContent != null) { + mAppsCustomizeContent.setApps(apps); + mAppsCustomizeContent.onPackagesUpdated( + LauncherModel.getSortedWidgetsAndShortcuts(this)); } if (mLauncherCallbacks != null) { mLauncherCallbacks.bindAllApplications(apps); @@ -4646,8 +4636,7 @@ public class Launcher extends Activity return; } - if (!LauncherAppState.isDisableAllApps() && - mAppsCustomizeContent != null) { + if (mAppsCustomizeContent != null) { mAppsCustomizeContent.updateApps(apps); } } @@ -4762,8 +4751,7 @@ public class Launcher extends Activity } // Update AllApps - if (!LauncherAppState.isDisableAllApps() && - mAppsCustomizeContent != null) { + if (mAppsCustomizeContent != null) { mAppsCustomizeContent.removeApps(appInfos); } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 87e9aae15..8e6557f73 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -43,9 +43,6 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { - private static final String TAG = "LauncherAppState"; - - private static final boolean DEBUG = false; private final AppFilter mAppFilter; private final BuildInfo mBuildInfo; @@ -283,12 +280,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { Utilities.setIconSize(grid.iconSizePx); } - public static boolean isDisableAllApps() { - // Returns false on non-dogfood builds. - return getInstance().mBuildInfo.isDogfoodBuild() && - Utilities.isPropertyEnabled(Launcher.DISABLE_ALL_APPS_PROPERTY); - } - public static boolean isDogfoodBuild() { return getInstance().mBuildInfo.isDogfoodBuild(); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3983835dc..f1f7a3542 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1644,12 +1644,6 @@ public class LauncherModel extends BroadcastReceiver sBgDbIconCache.clear(); } - if (LauncherAppState.isDisableAllApps()) { - // Ensure that all the applications that are in the system are - // represented on the home screen. - verifyApplications(); - } - // Clear out this reference, otherwise we end up holding it until all of the // callback runnables are done. mContext = null; @@ -1700,28 +1694,6 @@ public class LauncherModel extends BroadcastReceiver } } - private void verifyApplications() { - final Context context = mApp.getContext(); - - // Cross reference all the applications in our apps list with items in the workspace - ArrayList tmpInfos; - ArrayList added = new ArrayList(); - synchronized (sBgLock) { - for (AppInfo app : mBgAllAppsList.data) { - tmpInfos = getItemInfoForComponentName(app.componentName, app.user); - if (tmpInfos.isEmpty()) { - // We are missing an application icon, so add this to the workspace - added.add(app); - // This is a rare event, so lets log it - Log.e(TAG, "Missing Application on load: " + app); - } - } - } - if (!added.isEmpty()) { - addAndBindAddedWorkspaceApps(context, added); - } - } - // check & update map of what's occupied; used to discard overlapping/invalid items private boolean checkItemPlacement(HashMap occupied, ItemInfo item) { LauncherAppState app = LauncherAppState.getInstance(); @@ -3077,13 +3049,7 @@ public class LauncherModel extends BroadcastReceiver new HashMap(); if (added != null) { - // Ensure that we add all the workspace applications to the db - if (LauncherAppState.isDisableAllApps()) { - final ArrayList addedInfos = new ArrayList(added); - addAndBindAddedWorkspaceApps(context, addedInfos); - } else { - addAppsToAllApps(context, added); - } + addAppsToAllApps(context, added); for (AppInfo ai : added) { addedOrUpdatedApps.put(ai.componentName, ai); } -- cgit v1.2.3 From d2f3819c34d8ecdfdb42bf973b6db3aac259bd62 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 25 Feb 2015 10:46:34 -0800 Subject: Setting workspace id as primary key Bug: 19475231 Change-Id: I0385e97868db73dafe148ce45af7dda4555ee593 --- src/com/android/launcher3/LauncherProvider.java | 61 +++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index ab1347b0b..0cdd64c2a 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -57,7 +57,7 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 21; + private static final int DATABASE_VERSION = 22; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -233,7 +233,7 @@ public class LauncherProvider extends ContentProvider { } } - private void addModifiedTime(ContentValues values) { + private static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -426,7 +426,7 @@ public class LauncherProvider extends ContentProvider { private void addWorkspacesTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" + - LauncherSettings.WorkspaceScreens._ID + " INTEGER," + + LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," + LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," + LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" + ");"); @@ -563,7 +563,12 @@ public class LauncherProvider extends ContentProvider { if (!updateFolderItemsRank(db, true)) { break; } - case 21: { + case 21: + // Recreate workspace table with screen id a primary key + if (!recreateWorkspaceTable(db)) { + break; + } + case 22: { // DB Upgraded successfully return; } @@ -592,6 +597,54 @@ public class LauncherProvider extends ContentProvider { onCreate(db); } + /** + * Recreates workspace table and migrates data to the new table. + */ + public boolean recreateWorkspaceTable(SQLiteDatabase db) { + db.beginTransaction(); + try { + Cursor c = db.query(TABLE_WORKSPACE_SCREENS, + new String[] {LauncherSettings.WorkspaceScreens._ID}, + null, null, null, null, + LauncherSettings.WorkspaceScreens.SCREEN_RANK); + ArrayList sortedIDs = new ArrayList(); + long maxId = 0; + try { + while (c.moveToNext()) { + Long id = c.getLong(0); + if (!sortedIDs.contains(id)) { + sortedIDs.add(id); + maxId = Math.max(maxId, id); + } + } + } finally { + c.close(); + } + + db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS); + addWorkspacesTable(db); + + // Add all screen ids back + int total = sortedIDs.size(); + for (int i = 0; i < total; i++) { + ContentValues values = new ContentValues(); + values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i)); + values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); + addModifiedTime(values); + db.insertOrThrow(TABLE_WORKSPACE_SCREENS, null, values); + } + db.setTransactionSuccessful(); + mMaxScreenId = maxId; + } catch (SQLException ex) { + // Old version remains, which means we wipe old data + Log.e(TAG, ex.getMessage(), ex); + return false; + } finally { + db.endTransaction(); + } + return true; + } + private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { db.beginTransaction(); try { -- cgit v1.2.3 From 53fca52d3b2db08a35ab7af6f9bc52f6cd8f28a4 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 2 Mar 2015 10:55:45 -0800 Subject: [unit tests] Delete stress package b/19566571 TL;DR;; no longer working AND also the one test that is set up is useless to keep. Change-Id: If44457c7bc19a973450edf5aad3e744daf20624a --- tests/stress/Android.mk | 31 ----------- tests/stress/AndroidManifest.xml | 29 ---------- .../stress/LauncherRotationStressTest.java | 63 ---------------------- 3 files changed, 123 deletions(-) delete mode 100644 tests/stress/Android.mk delete mode 100644 tests/stress/AndroidManifest.xml delete mode 100644 tests/stress/src/com/android/launcher3/stress/LauncherRotationStressTest.java diff --git a/tests/stress/Android.mk b/tests/stress/Android.mk deleted file mode 100644 index 68289bd3e..000000000 --- a/tests/stress/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2011 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. -# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -LOCAL_JAVA_LIBRARIES := android.test.runner - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := LauncherRotationStressTest - -LOCAL_CERTIFICATE := shared - -LOCAL_INSTRUMENTATION_FOR := Launcher2 - -include $(BUILD_PACKAGE) diff --git a/tests/stress/AndroidManifest.xml b/tests/stress/AndroidManifest.xml deleted file mode 100644 index bcca1ff53..000000000 --- a/tests/stress/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/stress/src/com/android/launcher3/stress/LauncherRotationStressTest.java b/tests/stress/src/com/android/launcher3/stress/LauncherRotationStressTest.java deleted file mode 100644 index a5b85eb19..000000000 --- a/tests/stress/src/com/android/launcher3/stress/LauncherRotationStressTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2011 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.stress; - - -import com.android.launcher3.Launcher; - -import android.content.pm.ActivityInfo; -import android.os.SystemClock; -import android.test.ActivityInstrumentationTestCase2; -import android.test.RepetitiveTest; -import android.util.Log; - -/** - * Run rotation stress test using Launcher2 for 50 iterations. - */ -public class LauncherRotationStressTest extends ActivityInstrumentationTestCase2 { - - private static final int NUM_ITERATIONS = 50; - private static final int WAIT_TIME_MS = 500; - private static final String LOG_TAG = "LauncherRotationStressTest"; - - public LauncherRotationStressTest() { - super(Launcher.class); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - @RepetitiveTest(numIterations=NUM_ITERATIONS) - public void testLauncherRotationStress() throws Exception { - Launcher launcher = getActivity(); - getInstrumentation().waitForIdleSync(); - SystemClock.sleep(WAIT_TIME_MS); - launcher.setRequestedOrientation( - ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - getInstrumentation().waitForIdleSync(); - SystemClock.sleep(WAIT_TIME_MS); - launcher.setRequestedOrientation( - ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } -} -- cgit v1.2.3 From 170a9f3a1c82bb84b731347c7d38da0702d42d22 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 2 Mar 2015 11:55:42 -0800 Subject: [unit tests] Setup instrument target for the launcher3 unit test b/19566571 Change-Id: I47da40aa13a3e7c5ec8531e7ef6a9e52f764c565 --- tests/Android.mk | 27 ++++++++++++-- tests/AndroidManifest.xml | 29 +++++++++++++++ .../com/android/launcher3/util/FocusLogicTest.java | 42 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 tests/AndroidManifest.xml create mode 100644 tests/src/com/android/launcher3/util/FocusLogicTest.java diff --git a/tests/Android.mk b/tests/Android.mk index 762a52b8d..eba4ade48 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -1,4 +1,4 @@ -# Copyright (C) 2011 The Android Open Source Project +# 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. @@ -12,5 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. # -#LOCAL_PATH := $(call my-dir) -#include $(call all-makefiles-under,$(LOCAL_PATH)) + +LOCAL_PATH := $(call my-dir) + +src_dirs := src +res_dirs := res + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) +LOCAL_AAPT_FLAGS := --auto-add-overlay + +LOCAL_SDK_VERSION := 21 + +LOCAL_PACKAGE_NAME := Launcher3Tests + +LOCAL_INSTRUMENTATION_FOR := Launcher3 + +include $(BUILD_PACKAGE) diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml new file mode 100644 index 000000000..42ae5a38a --- /dev/null +++ b/tests/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/tests/src/com/android/launcher3/util/FocusLogicTest.java b/tests/src/com/android/launcher3/util/FocusLogicTest.java new file mode 100644 index 000000000..fd2e2a823 --- /dev/null +++ b/tests/src/com/android/launcher3/util/FocusLogicTest.java @@ -0,0 +1,42 @@ +/* + * 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.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Tests the {@link FocusLogic} class that handles key event based focus handling. + */ +@SmallTest +public final class FocusLogicTest extends AndroidTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + // Nothing to set up as this class only tests static methods. + } + + @Override + protected void tearDown() throws Exception { + // Nothing to tear down as this class only tests static methods. + } + + public void testShouldConsume() { + // write tests. + } +} -- cgit v1.2.3 From e72f3d53f8c35b94aece6fafa6fd12cce9d09fe5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Mar 2015 14:24:21 -0800 Subject: Fixing wronk rowID check. Since screenId is changed to primary key, the rowId can now be 0 (earlier it was auto-increment and never 0) Bug: 19566734 Change-Id: Icf0e4294a581039101df1ad1756fdb6e581c4cba --- src/com/android/launcher3/LauncherProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index ec033b3f7..3888b709c 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -153,7 +153,7 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); addModifiedTime(initialValues); final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues); - if (rowId <= 0) return null; + if (rowId < 0) return null; uri = ContentUris.withAppendedId(uri, rowId); sendNotify(uri); -- cgit v1.2.3 From 9282059e44604330763340e6c37ffb653b8ef187 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Mar 2015 11:31:35 -0800 Subject: Fixing wrong check to determine the drag source > target it the drop target, i.e., the InfoDropTarget or DeleteDropTarget Change-Id: If4a9f8eb72ee6afb4d80c7df6f0f2ada4bc35b22 --- src/com/android/launcher3/Launcher.java | 2 +- src/com/android/launcher3/Workspace.java | 19 +++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1ad8b274d..ab6ebd3b6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3206,7 +3206,7 @@ public class Launcher extends Activity /** * Returns the CellLayout of the specified container at the specified screen. */ - CellLayout getCellLayout(long container, long screenId) { + public CellLayout getCellLayout(long container, long screenId) { if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { if (mHotseat != null) { return mHotseat.getLayout(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b9c1f4d3d..60457fd7c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4169,23 +4169,18 @@ public class Workspace extends SmoothPagedView boolean beingCalledAfterUninstall = mDeferredAction != null; if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) { - if (target != this && mDragInfo != null) { + if (mDragInfo != null) { removeWorkspaceItem(mDragInfo.cell); } } else if (mDragInfo != null) { - CellLayout cellLayout; - if (mLauncher.isHotseatLayout(target)) { - cellLayout = mLauncher.getHotseat().getLayout(); - } else { - cellLayout = getScreenWithId(mDragInfo.screenId); - } - if (cellLayout == null && LauncherAppState.isDogfoodBuild()) { - throw new RuntimeException("Invalid state: cellLayout == null in " - + "Workspace#onDropCompleted. Please file a bug. "); - } + final CellLayout cellLayout = mLauncher.getCellLayout( + mDragInfo.container, mDragInfo.screenId); if (cellLayout != null) { cellLayout.onDropChild(mDragInfo.cell); - } + } else if (LauncherAppState.isDogfoodBuild()) { + throw new RuntimeException("Invalid state: cellLayout == null in " + + "Workspace#onDropCompleted. Please file a bug. "); + }; } if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful)) && mDragInfo.cell != null) { -- cgit v1.2.3 From 38531719636eba7c924e3e71c583ebce2c89a1d0 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 3 Mar 2015 19:25:16 -0800 Subject: [key event focus] DPAD navigates to the nearest item on next/previous page b/19381790 b/16351792 TL;DR;; Previously, when RIGHT is handled on the right most column of the current page or when LEFT is handled on the left most column, the next icon of focus is next page 'first' icon or the previous page 'last icon'. With this change, the row information is preserved when trying to locate an icon to give focus in the next/previous page. Next CL: long awaited unit tests that capture corner cases for different orientation/ device configuration. Change-Id: I5278bed45275b3e4cb39fb698df35f90bb45a415 --- src/com/android/launcher3/FocusHelper.java | 46 ++++++++++- src/com/android/launcher3/util/FocusLogic.java | 94 +++++++++++++++++++--- .../com/android/launcher3/util/FocusLogicTest.java | 20 ++++- 3 files changed, 148 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index bdfd7b287..ebb319b0b 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -89,6 +89,7 @@ public class FocusHelper { countX = ((CellLayout) parentLayout).getCountX(); countY = ((CellLayout) parentLayout).getCountY(); } else if (v.getParent() instanceof ViewGroup) { + //TODO(hyunyoungs): figure out when this needs to be called. itemContainer = parentLayout = (ViewGroup) v.getParent(); countX = ((PagedViewGridLayout) parentLayout).getCellCountX(); countY = ((PagedViewGridLayout) parentLayout).getCellCountY(); @@ -102,6 +103,7 @@ public class FocusHelper { final int pageCount = container.getChildCount(); ViewGroup newParent = null; View child = null; + // TODO(hyunyoungs): this matrix is not applicable on the last page. int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true); // Process focus. @@ -111,12 +113,22 @@ public class FocusHelper { return consume; } switch (newIconIndex) { + case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: + newParent = getAppsCustomizePage(container, pageIndex -1); + if (newParent != null) { + int row = FocusLogic.findRow(matrix, iconIndex); + container.snapToPage(pageIndex - 1); + // no need to create a new matrix. + child = newParent.getChildAt(matrix[countX-1][row]); + } + break; case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: newParent = getAppsCustomizePage(container, pageIndex - 1); if (newParent != null) { container.snapToPage(pageIndex - 1); child = newParent.getChildAt(0); } + break; case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: newParent = getAppsCustomizePage(container, pageIndex - 1); if (newParent != null) { @@ -131,6 +143,14 @@ public class FocusHelper { child = newParent.getChildAt(0); } break; + case FocusLogic.NEXT_PAGE_LEFT_COLUMN: + newParent = getAppsCustomizePage(container, pageIndex + 1); + if (newParent != null) { + container.snapToPage(pageIndex + 1); + int row = FocusLogic.findRow(matrix, iconIndex); + child = newParent.getChildAt(matrix[0][row]); + } + break; case FocusLogic.CURRENT_PAGE_FIRST_ITEM: child = container.getChildAt(0); break; @@ -256,7 +276,7 @@ public class FocusHelper { // Initialize the variables. ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); - final CellLayout iconLayout = (CellLayout) parent.getParent(); + CellLayout iconLayout = (CellLayout) parent.getParent(); final Workspace workspace = (Workspace) iconLayout.getParent(); final ViewGroup launcher = (ViewGroup) workspace.getParent(); final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar); @@ -301,10 +321,23 @@ public class FocusHelper { newIcon = tabs; } break; + case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: + int row = FocusLogic.findRow(matrix, iconIndex); + parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); + if (parent != null) { + iconLayout = (CellLayout) parent.getParent(); + matrix = FocusLogic.createSparseMatrix(iconLayout, orientation, + iconLayout.getCountX(), row); + newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, + FocusLogic.PIVOT, pageIndex - 1, pageCount); + newIcon = parent.getChildAt(newIconIndex); + } + break; case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); newIcon = parent.getChildAt(0); workspace.snapToPage(pageIndex - 1); + break; case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); newIcon = parent.getChildAt(parent.getChildCount() - 1); @@ -315,6 +348,17 @@ public class FocusHelper { newIcon = parent.getChildAt(0); workspace.snapToPage(pageIndex + 1); break; + case FocusLogic.NEXT_PAGE_LEFT_COLUMN: + row = FocusLogic.findRow(matrix, iconIndex); + parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); + if (parent != null) { + iconLayout = (CellLayout) parent.getParent(); + matrix = FocusLogic.createSparseMatrix(iconLayout, orientation, -1, row); + newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, + FocusLogic.PIVOT, pageIndex, pageCount); + newIcon = parent.getChildAt(newIconIndex); + } + break; case FocusLogic.CURRENT_PAGE_FIRST_ITEM: newIcon = parent.getChildAt(0); break; diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index c0730d93d..0c6bfbf35 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -48,16 +48,19 @@ public class FocusLogic { // Item and page index related constant used by {@link #handleKeyEvent}. public static final int NOOP = -1; - public static final int PREVIOUS_PAGE_FIRST_ITEM = -2; - public static final int PREVIOUS_PAGE_LAST_ITEM = -3; + public static final int PREVIOUS_PAGE_RIGHT_COLUMN = -2; + public static final int PREVIOUS_PAGE_FIRST_ITEM = -3; + public static final int PREVIOUS_PAGE_LAST_ITEM = -4; - public static final int CURRENT_PAGE_FIRST_ITEM = -4; - public static final int CURRENT_PAGE_LAST_ITEM = -5; + public static final int CURRENT_PAGE_FIRST_ITEM = -5; + public static final int CURRENT_PAGE_LAST_ITEM = -6; - public static final int NEXT_PAGE_FIRST_ITEM = -6; + public static final int NEXT_PAGE_FIRST_ITEM = -7; + public static final int NEXT_PAGE_LEFT_COLUMN = -8; // Matrix related constant. public static final int EMPTY = -1; + public static final int PIVOT = 100; /** * Returns true only if this utility class handles the key code. @@ -87,13 +90,13 @@ public class FocusLogic { case KeyEvent.KEYCODE_DPAD_LEFT: newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/); if (newIndex == NOOP && pageIndex > 0) { - return PREVIOUS_PAGE_LAST_ITEM; + newIndex = PREVIOUS_PAGE_RIGHT_COLUMN; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/); if (newIndex == NOOP && pageIndex < pageCount - 1) { - return NEXT_PAGE_FIRST_ITEM; + newIndex = NEXT_PAGE_LEFT_COLUMN; } break; case KeyEvent.KEYCODE_DPAD_DOWN: @@ -169,7 +172,7 @@ public class FocusLogic { matrix[cx][cy] = i; } if (DEBUG) { - printMatrix(matrix, m, n); + printMatrix(matrix); } return matrix; } @@ -231,7 +234,47 @@ public class FocusLogic { } } if (DEBUG) { - printMatrix(matrix, m, n); + printMatrix(matrix); + } + return matrix; + } + + /** + * Creates a sparse matrix that merges the icon of previous/next page and last column of + * current page. When left key is triggered on the leftmost column, sparse matrix is created + * that combines previous page matrix and an extra column on the right. Likewise, when right + * key is triggered on the rightmost column, sparse matrix is created that combines this column + * on the 0th column and the next page matrix. + * + * @param pivotX x coordinate of the focused item in the current page + * @param pivotY y coordinate of the focused item in the current page + */ + // TODO: get rid of the dynamic matrix creation + public static int[][] createSparseMatrix(CellLayout iconLayout, int pivotX, int pivotY) { + + ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); + + int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY(), + false /* set all cell to empty */); + + // Iterate thru the children of the top parent. + for (int i = 0; i < iconParent.getChildCount(); i++) { + int cx = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellX; + int cy = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellY; + if (pivotX < 0) { + matrix[cx - pivotX][cy] = i; + } else { + matrix[cx][cy] = i; + } + } + + if (pivotX < 0) { + matrix[0][pivotY] = PIVOT; + } else { + matrix[pivotX][pivotY] = PIVOT; + } + if (DEBUG) { + printMatrix(matrix); } return matrix; } @@ -407,21 +450,32 @@ public class FocusLogic { return newIconIndex; } + /** + * Only used for debugging. + */ private static String getStringIndex(int index) { switch(index) { case NOOP: return "NOOP"; case PREVIOUS_PAGE_FIRST_ITEM: return "PREVIOUS_PAGE_FIRST"; case PREVIOUS_PAGE_LAST_ITEM: return "PREVIOUS_PAGE_LAST"; + case PREVIOUS_PAGE_RIGHT_COLUMN:return "PREVIOUS_PAGE_RIGHT_COLUMN"; case CURRENT_PAGE_FIRST_ITEM: return "CURRENT_PAGE_FIRST"; case CURRENT_PAGE_LAST_ITEM: return "CURRENT_PAGE_LAST"; case NEXT_PAGE_FIRST_ITEM: return "NEXT_PAGE_FIRST"; + case NEXT_PAGE_LEFT_COLUMN: return "NEXT_PAGE_LEFT_COLUMN"; default: return Integer.toString(index); } } - private static void printMatrix(int[][] matrix, int m, int n) { + /** + * Only used for debugging. + */ + private static void printMatrix(int[][] matrix) { Log.v(TAG, "\tprintMap:"); + int m = matrix.length; + int n = matrix[0].length; + for (int j=0; j < n; j++) { String colY = "\t\t"; for (int i=0; i < m; i++) { @@ -430,4 +484,24 @@ public class FocusLogic { Log.v(TAG, colY); } } + + /** + * Figure out the location of the icon. + * + */ + //TODO(hyunyoungs): this helper method should move to CellLayout class while removing the + // dynamic matrix creation all together. + public static int findRow(int[][] matrix, int iconIndex) { + int cntX = matrix.length; + int cntY = matrix[0].length; + + for (int i = 0; i < cntX; i++) { + for (int j = 0; j < cntY; j++) { + if (matrix[i][j] == iconIndex) { + return j; + } + } + } + return -1; + } } diff --git a/tests/src/com/android/launcher3/util/FocusLogicTest.java b/tests/src/com/android/launcher3/util/FocusLogicTest.java index fd2e2a823..ea87014e9 100644 --- a/tests/src/com/android/launcher3/util/FocusLogicTest.java +++ b/tests/src/com/android/launcher3/util/FocusLogicTest.java @@ -18,6 +18,9 @@ package com.android.launcher3; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import android.view.KeyEvent; + +import com.android.launcher3.util.FocusLogic; /** * Tests the {@link FocusLogic} class that handles key event based focus handling. @@ -37,6 +40,21 @@ public final class FocusLogicTest extends AndroidTestCase { } public void testShouldConsume() { - // write tests. + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_DPAD_LEFT)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_DPAD_RIGHT)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_DPAD_UP)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_DPAD_DOWN)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_MOVE_HOME)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_MOVE_END)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_PAGE_UP)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_PAGE_DOWN)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_DEL)); + assertTrue(FocusLogic.shouldConsume(KeyEvent.KEYCODE_FORWARD_DEL)); + } + + public void testCreateSparseMatrix() { + // Either, 1) create a helper method to generate/instantiate all possible cell layout that + // may get created in real world to test this method. OR 2) Move all the matrix + // management routine to celllayout and write tests for them. } } -- cgit v1.2.3 From fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 4 Mar 2015 10:43:45 -0800 Subject: Refactoring max id logic to a common method Change-Id: I1f649b570ee43d6c0540a207693d2cbee4538fb8 --- src/com/android/launcher3/LauncherProvider.java | 59 ++++++++++--------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index b61844452..cf66e5157 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -733,23 +733,7 @@ public class LauncherProvider extends ContentProvider { } private long initializeMaxItemId(SQLiteDatabase db) { - Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null); - - // get the result - final int maxIdIndex = 0; - long id = -1; - if (c != null && c.moveToNext()) { - id = c.getLong(maxIdIndex); - } - if (c != null) { - c.close(); - } - - if (id == -1) { - throw new RuntimeException("Error: could not query max item id"); - } - - return id; + return getMaxId(db, TABLE_FAVORITES); } // Generates a new ID to use for an workspace screen in your database. This method @@ -768,25 +752,7 @@ public class LauncherProvider extends ContentProvider { } private long initializeMaxScreenId(SQLiteDatabase db) { - Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null); - - // get the result - final int maxIdIndex = 0; - long id = -1; - if (c != null && c.moveToNext()) { - id = c.getLong(maxIdIndex); - } - if (c != null) { - c.close(); - } - - if (id == -1) { - throw new RuntimeException("Error: could not query max screen id"); - } - - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true); - return id; + return getMaxId(db, TABLE_WORKSPACE_SCREENS); } private boolean initializeExternalAdd(ContentValues values) { @@ -1199,6 +1165,27 @@ public class LauncherProvider extends ContentProvider { } } + /** + * @return the max _id in the provided table. + */ + private static long getMaxId(SQLiteDatabase db, String table) { + Cursor c = db.rawQuery("SELECT MAX(_id) FROM " + table, null); + // get the result + long id = -1; + if (c != null && c.moveToNext()) { + id = c.getLong(0); + } + if (c != null) { + c.close(); + } + + if (id == -1) { + throw new RuntimeException("Error: could not query max id in " + table); + } + + return id; + } + static class SqlArguments { public final String table; public final String where; -- cgit v1.2.3 From 64b3fcc9f7bd951530bf9b78339882d7b104b50e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 4 Mar 2015 13:54:52 -0800 Subject: Ensuring setQsbSearchBar is called whenever the QSB is created > On launcher3 when the QSB is created for the first time after a widget drop, it is not set in searchDropTarget and as a result the "Remove" and "App Info" drop targets are not visible until Launcher3 restarts and rebinds QSB Change-Id: I599a9a18cc5d46af790b3145dae2eb385b32b20e --- src/com/android/launcher3/Launcher.java | 12 +++++++----- src/com/android/launcher3/Workspace.java | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1ad8b274d..e3a2d7c5f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -116,7 +116,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.DateFormat; @@ -147,7 +146,6 @@ public class Launcher extends Activity private static final int REQUEST_CREATE_SHORTCUT = 1; private static final int REQUEST_CREATE_APPWIDGET = 5; - private static final int REQUEST_PICK_SHORTCUT = 7; private static final int REQUEST_PICK_APPWIDGET = 9; private static final int REQUEST_PICK_WALLPAPER = 10; @@ -1444,7 +1442,10 @@ public class Launcher extends Activity dragController.addDropTarget(mWorkspace); if (mSearchDropTargetBar != null) { mSearchDropTargetBar.setup(this, dragController); - mSearchDropTargetBar.setQsbSearchBar(getQsbBar()); + if (getOrCreateQsbBar() == null) { + // Explicitly set it to null during initialization. + mSearchDropTargetBar.setQsbSearchBar(null); + } } if (getResources().getBoolean(R.bool.debug_memory_enabled)) { @@ -3952,7 +3953,7 @@ public class Launcher extends Activity // NO-OP } - public View getQsbBar() { + public View getOrCreateQsbBar() { if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) { return mLauncherCallbacks.getQsbBar(); } @@ -4001,6 +4002,7 @@ public class Launcher extends Activity mQsb.updateAppWidgetOptions(opts); mQsb.setPadding(0, 0, 0, 0); mSearchDropTargetBar.addView(mQsb); + mSearchDropTargetBar.setQsbSearchBar(mQsb); } } return mQsb; @@ -4602,7 +4604,7 @@ public class Launcher extends Activity mSearchDropTargetBar.removeView(mQsb); mQsb = null; } - mSearchDropTargetBar.setQsbSearchBar(getQsbBar()); + getOrCreateQsbBar(); } /** diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b9c1f4d3d..da893b6ee 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2305,7 +2305,7 @@ public class Workspace extends SmoothPagedView } } - final View searchBar = mLauncher.getQsbBar(); + final View searchBar = mLauncher.getOrCreateQsbBar(); final View overviewPanel = mLauncher.getOverviewPanel(); final View hotseat = mLauncher.getHotseat(); final View pageIndicator = getPageIndicator(); -- cgit v1.2.3 From c6c8fef3e90f84b26c3b8550ec6a1d668ca9200d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 4 Mar 2015 09:51:18 -0800 Subject: Clearing DB if necessary, when applying the default layout Bug: 19574498 Change-Id: I34850d9a7f7316bcc2cb9fc8a5be57d2488c16a6 --- src/com/android/launcher3/LauncherProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 3888b709c..1040b1173 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -305,6 +305,10 @@ public class LauncherProvider extends ContentProvider { if (loader == null) { loader = getDefaultLayoutParser(); } + + // There might be some partially restored DB items, due to buggy restore logic in + // previous versions of launcher. + createEmptyDB(); // Populate favorites table with initial favorites if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0) && usingExternallyProvidedLayout) { -- cgit v1.2.3 From c3a609f950a713d995f1968574d8ed7b4449f415 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Feb 2015 17:43:50 -0800 Subject: Refactoring folder content > Refactoring the CellLayout in folder with a custom view so that it can be replaced easily with a scrollable paged view. > Moving some methods from the folder to this new view which assume a single page layout for the folder > Changing folder from LinearLayout to FrameLayout to properly handle focus traversal in case of multi-page folders Change-Id: I073c00b995488f9f5d8123b00357e094ca2cec7c --- res/layout/user_folder.xml | 57 +-- src/com/android/launcher3/FocusHelper.java | 4 +- src/com/android/launcher3/FocusOnlyTabWidget.java | 86 ----- src/com/android/launcher3/Folder.java | 433 ++++++---------------- src/com/android/launcher3/FolderCellLayout.java | 285 ++++++++++++++ 5 files changed, 442 insertions(+), 423 deletions(-) delete mode 100644 src/com/android/launcher3/FocusOnlyTabWidget.java create mode 100644 src/com/android/launcher3/FolderCellLayout.java diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index ed8d43e46..74cdf11dc 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -1,5 +1,6 @@ - - + android:background="@drawable/quantum_panel" + android:orientation="vertical" > - - - + android:layout_height="match_parent" > + + + + + + + - + android:textSize="14sp" /> + + \ No newline at end of file diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index bdfd7b287..3ca16d476 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -22,7 +22,6 @@ import android.view.KeyEvent; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; -import android.widget.ScrollView; import com.android.launcher3.util.FocusLogic; @@ -354,8 +353,7 @@ public class FocusHelper { // Initialize the variables. ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); final CellLayout layout = (CellLayout) parent.getParent(); - final ScrollView scrollView = (ScrollView) layout.getParent(); - final Folder folder = (Folder) scrollView.getParent(); + final Folder folder = (Folder) layout.getParent().getParent(); View title = folder.mFolderName; Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); final int countX = layout.getCountX(); diff --git a/src/com/android/launcher3/FocusOnlyTabWidget.java b/src/com/android/launcher3/FocusOnlyTabWidget.java deleted file mode 100644 index 08fc311bc..000000000 --- a/src/com/android/launcher3/FocusOnlyTabWidget.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2011 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.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.TabWidget; - -public class FocusOnlyTabWidget extends TabWidget { - public FocusOnlyTabWidget(Context context) { - super(context); - } - - public FocusOnlyTabWidget(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public FocusOnlyTabWidget(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public View getSelectedTab() { - final int count = getTabCount(); - for (int i = 0; i < count; ++i) { - View v = getChildTabViewAt(i); - if (v.isSelected()) { - return v; - } - } - return null; - } - - public int getChildTabIndex(View v) { - final int tabCount = getTabCount(); - for (int i = 0; i < tabCount; ++i) { - if (getChildTabViewAt(i) == v) { - return i; - } - } - return -1; - } - - public void setCurrentTabToFocusedTab() { - View tab = null; - int index = -1; - final int count = getTabCount(); - for (int i = 0; i < count; ++i) { - View v = getChildTabViewAt(i); - if (v.hasFocus()) { - tab = v; - index = i; - break; - } - } - if (index > -1) { - super.setCurrentTab(index); - super.onFocusChange(tab, true); - } - } - public void superOnFocusChange(View v, boolean hasFocus) { - super.onFocusChange(v, hasFocus); - } - - @Override - public void onFocusChange(android.view.View v, boolean hasFocus) { - if (v == this && hasFocus && getTabCount() > 0) { - getSelectedTab().requestFocus(); - return; - } - } -} diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 11e9835e8..80fc32880 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -45,7 +45,6 @@ import android.view.animation.AccelerateInterpolator; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.TextView; import com.android.launcher3.FolderInfo.FolderListener; @@ -61,64 +60,65 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View.OnFocusChangeListener { private static final String TAG = "Launcher.Folder"; - protected DragController mDragController; - protected Launcher mLauncher; - protected FolderInfo mInfo; + /** + * We avoid measuring {@link #mContentWrapper} with a 0 width or height, as this + * results in CellLayout being measured as UNSPECIFIED, which it does not support. + */ + private static final int MIN_CONTENT_DIMEN = 5; static final int STATE_NONE = -1; static final int STATE_SMALL = 0; static final int STATE_ANIMATING = 1; static final int STATE_OPEN = 2; - private int mExpandDuration; - private int mMaterialExpandDuration; - private int mMaterialExpandStagger; - protected CellLayout mContent; - private ScrollView mScrollView; - private final LayoutInflater mInflater; - private final IconCache mIconCache; - private int mState = STATE_NONE; - private static final int REORDER_ANIMATION_DURATION = 230; private static final int REORDER_DELAY = 250; private static final int ON_EXIT_CLOSE_DELAY = 400; - private boolean mRearrangeOnClose = false; + private static final Rect sTempRect = new Rect(); + + private static String sDefaultFolderName; + private static String sHintText; + + private final Alarm mReorderAlarm = new Alarm(); + private final Alarm mOnExitAlarm = new Alarm(); + + private final ArrayList mItemsInReadingOrder = new ArrayList(); + + private final int mExpandDuration; + private final int mMaterialExpandDuration; + private final int mMaterialExpandStagger; + + private final InputMethodManager mInputMethodManager; + + protected final Launcher mLauncher; + protected DragController mDragController; + protected FolderInfo mInfo; + private FolderIcon mFolderIcon; - private int mMaxCountX; - private int mMaxCountY; - private int mMaxNumItems; - private ArrayList mItemsInReadingOrder = new ArrayList(); + + private FolderCellLayout mContent; + private View mContentWrapper; + FolderEditText mFolderName; + + private View mBottomContent; + private int mBottomContentHeight; + + // Cell ranks used for drag and drop + private int mTargetRank, mPrevTargetRank, mEmptyCellRank; + + private int mState = STATE_NONE; + private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; private ShortcutInfo mCurrentDragInfo; private View mCurrentDragView; private boolean mIsExternalDrag; boolean mSuppressOnAdd = false; - private int[] mTargetCell = new int[2]; - private int[] mPreviousTargetCell = new int[2]; - private int[] mEmptyCell = new int[2]; - private Alarm mReorderAlarm = new Alarm(); - private Alarm mOnExitAlarm = new Alarm(); - private int mFolderNameHeight; - private Rect mTempRect = new Rect(); private boolean mDragInProgress = false; private boolean mDeleteFolderOnDropCompleted = false; private boolean mSuppressFolderDeletion = false; private boolean mItemAddedBackToSelfViaIcon = false; - FolderEditText mFolderName; private float mFolderIconPivotX; private float mFolderIconPivotY; - private boolean mIsEditingName = false; - private InputMethodManager mInputMethodManager; - - private static String sDefaultFolderName; - private static String sHintText; - - private FocusIndicatorView mFocusIndicatorHandler; - - // We avoid measuring the scroll view with a 0 width or height, as this - // results in CellLayout being measured as UNSPECIFIED, which it does - // not support. - private static final int MIN_CONTENT_DIMEN = 5; private boolean mDestroyed; @@ -130,25 +130,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * Used to inflate the Workspace from XML. * * @param context The application's context. - * @param attrs The attribtues set containing the Workspace's customization values. + * @param attrs The attributes set containing the Workspace's customization values. */ public Folder(Context context, AttributeSet attrs) { super(context, attrs); - - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); setAlwaysDrawnWithCacheEnabled(false); - mInflater = LayoutInflater.from(context); - mIconCache = app.getIconCache(); - - Resources res = getResources(); - mMaxCountX = (int) grid.numColumns; - mMaxCountY = (int) grid.numRows; - mMaxNumItems = mMaxCountX * mMaxCountY; - mInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + Resources res = getResources(); mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration); mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration); mMaterialExpandStagger = res.getInteger(R.integer.config_materialFolderExpandStagger); @@ -162,20 +152,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mLauncher = (Launcher) context; // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving - // reliable behvior when clicking the text field (since it will always gain focus on click). + // reliable behavior when clicking the text field (since it will always gain focus on click). setFocusableInTouchMode(true); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mScrollView = (ScrollView) findViewById(R.id.scroll_view); - mContent = (CellLayout) findViewById(R.id.folder_content); - - mFocusIndicatorHandler = new FocusIndicatorView(getContext()); - mContent.addView(mFocusIndicatorHandler, 0); - mFocusIndicatorHandler.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE; - mFocusIndicatorHandler.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE; + mContentWrapper = findViewById(R.id.folder_content_wrapper); + mContent = (FolderCellLayout) findViewById(R.id.folder_content); + mContent.setFolder(this); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -184,22 +170,25 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setGridSize(0, 0); mContent.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); mContent.setInvertIfRtl(true); + mFolderName = (FolderEditText) findViewById(R.id.folder_name); mFolderName.setFolder(this); mFolderName.setOnFocusChangeListener(this); - // We find out how tall the text view wants to be (it is set to wrap_content), so that - // we can allocate the appropriate amount of space for it. - int measureSpec = MeasureSpec.UNSPECIFIED; - mFolderName.measure(measureSpec, measureSpec); - mFolderNameHeight = mFolderName.getMeasuredHeight(); - // We disable action mode for now since it messes up the view on phones mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback); mFolderName.setOnEditorActionListener(this); mFolderName.setSelectAllOnFocus(true); mFolderName.setInputType(mFolderName.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); + + // We only have the folder name at the bottom for now + mBottomContent = mFolderName; + // We find out how tall the bottom content wants to be (it is set to wrap_content), so that + // we can allocate the appropriate amount of space for it. + int measureSpec = MeasureSpec.UNSPECIFIED; + mBottomContent.measure(measureSpec, measureSpec); + mBottomContentHeight = mBottomContent.getMeasuredHeight(); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -240,8 +229,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mLauncher.getWorkspace().beginDragShared(v, this); mCurrentDragInfo = item; - mEmptyCell[0] = item.cellX; - mEmptyCell[1] = item.cellY; + mEmptyCellRank = item.rank; mCurrentDragView = v; mContent.removeView(mCurrentDragView); @@ -298,10 +286,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mFolderName; } - public CellLayout getContent() { - return mContent; - } - /** * We need to handle touch events to prevent them from falling through to the workspace below. */ @@ -314,7 +298,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mDragController = dragController; } - void setFolderIcon(FolderIcon icon) { + public void setFolderIcon(FolderIcon icon) { mFolderIcon = icon; } @@ -334,30 +318,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList void bind(FolderInfo info) { mInfo = info; ArrayList children = info.contents; - ArrayList overflow = new ArrayList(); - - final int totalChildren = children.size(); - setupContentForNumItems(totalChildren); - - // Arrange children in the grid based on the rank. Collections.sort(children, Utilities.RANK_COMPARATOR); - final int countX = mContent.getCountX(); - - int visibleChildren = 0; - for (int i = 0; i < children.size(); i++) { - ShortcutInfo child = children.get(i); - child.cellX = i % countX; - child.cellY = i / countX; - - if (createAndAddShortcut(child) == null) { - overflow.add(child); - } else { - visibleChildren++; - } - } - // We rearrange the items in case there are any empty gaps - setupContentForNumItems(visibleChildren); + ArrayList overflow = mContent.bindItems(children); // If our folder has too many items we prune them from the list. This is an issue // when upgrading from the old Folders implementation which could contain an unlimited @@ -367,6 +330,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList LauncherModel.deleteItemFromDatabase(mLauncher, item); } + DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); + if (lp == null) { + lp = new DragLayer.LayoutParams(0, 0); + lp.customPosition = true; + setLayoutParams(lp); + } + centerAboutIcon(); + mItemsInvalidated = true; updateTextViewFocus(); mInfo.addListener(this); @@ -470,8 +441,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList iconsAlpha.setStartDelay(mMaterialExpandStagger); iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - mFolderName.setAlpha(0f); - Animator textAlpha = LauncherAnimUtils.ofFloat(mFolderName, "alpha", 0f, 1f); + mBottomContent.setAlpha(0f); + Animator textAlpha = LauncherAnimUtils.ofFloat(mBottomContent, "alpha", 0f, 1f); textAlpha.setDuration(mMaterialExpandDuration); textAlpha.setStartDelay(mMaterialExpandStagger); textAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); @@ -524,14 +495,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void beginExternalDrag(ShortcutInfo item) { - setupContentForNumItems(getItemCount() + 1); - findAndSetEmptyCells(item); - mCurrentDragInfo = item; - mEmptyCell[0] = item.cellX; - mEmptyCell[1] = item.cellY; + mEmptyCellRank = mContent.allocateNewLastItemRank(); mIsExternalDrag = true; - mDragInProgress = true; } @@ -588,132 +554,34 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList !isFull()); } - protected boolean findAndSetEmptyCells(ShortcutInfo item) { - int[] emptyCell = new int[2]; - if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) { - item.cellX = emptyCell[0]; - item.cellY = emptyCell[1]; - return true; - } else { - return false; - } - } - - protected View createAndAddShortcut(ShortcutInfo item) { - final BubbleTextView textView = - (BubbleTextView) mInflater.inflate(R.layout.folder_application, this, false); - textView.applyFromShortcutInfo(item, mIconCache, false); - - textView.setOnClickListener(this); - textView.setOnLongClickListener(this); - textView.setOnFocusChangeListener(mFocusIndicatorHandler); - - // We need to check here to verify that the given item's location isn't already occupied - // by another item. - if (mContent.getChildAt(item.cellX, item.cellY) != null || item.cellX < 0 || item.cellY < 0 - || item.cellX >= mContent.getCountX() || item.cellY >= mContent.getCountY()) { - // This shouldn't happen, log it. - Log.e(TAG, "Folder order not properly persisted during bind"); - if (!findAndSetEmptyCells(item)) { - return null; - } - } - - CellLayout.LayoutParams lp = - new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY); - boolean insert = false; - textView.setOnKeyListener(new FolderKeyEventListener()); - mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true); - return textView; - } - public void onDragEnter(DragObject d) { - mPreviousTargetCell[0] = -1; - mPreviousTargetCell[1] = -1; + mPrevTargetRank = -1; mOnExitAlarm.cancelAlarm(); } OnAlarmListener mReorderAlarmListener = new OnAlarmListener() { public void onAlarm(Alarm alarm) { - realTimeReorder(mEmptyCell, mTargetCell); + mContent.realTimeReorder(mEmptyCellRank, mTargetRank); + mEmptyCellRank = mTargetRank; } }; - boolean readingOrderGreaterThan(int[] v1, int[] v2) { - if (v1[1] > v2[1] || (v1[1] == v2[1] && v1[0] > v2[0])) { - return true; - } else { - return false; - } - } - - private void realTimeReorder(int[] empty, int[] target) { - boolean wrap; - int startX; - int endX; - int startY; - int delay = 0; - float delayAmount = 30; - if (readingOrderGreaterThan(target, empty)) { - wrap = empty[0] >= mContent.getCountX() - 1; - startY = wrap ? empty[1] + 1 : empty[1]; - for (int y = startY; y <= target[1]; y++) { - startX = y == empty[1] ? empty[0] + 1 : 0; - endX = y < target[1] ? mContent.getCountX() - 1 : target[0]; - for (int x = startX; x <= endX; x++) { - View v = mContent.getChildAt(x,y); - if (mContent.animateChildToPosition(v, empty[0], empty[1], - REORDER_ANIMATION_DURATION, delay, true, true)) { - empty[0] = x; - empty[1] = y; - delay += delayAmount; - delayAmount *= 0.9; - } - } - } - } else { - wrap = empty[0] == 0; - startY = wrap ? empty[1] - 1 : empty[1]; - for (int y = startY; y >= target[1]; y--) { - startX = y == empty[1] ? empty[0] - 1 : mContent.getCountX() - 1; - endX = y > target[1] ? 0 : target[0]; - for (int x = startX; x >= endX; x--) { - View v = mContent.getChildAt(x,y); - if (mContent.animateChildToPosition(v, empty[0], empty[1], - REORDER_ANIMATION_DURATION, delay, true, true)) { - empty[0] = x; - empty[1] = y; - delay += delayAmount; - delayAmount *= 0.9; - } - } - } - } - } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } public void onDragOver(DragObject d) { - final int scrollOffset = mScrollView.getScrollY(); final float[] r = d.getVisualCenter(null); r[0] -= getPaddingLeft(); r[1] -= getPaddingTop(); - mTargetCell = mContent.findNearestArea( - (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); - if (isLayoutRtl()) { - mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; - } - if (mTargetCell[0] != mPreviousTargetCell[0] - || mTargetCell[1] != mPreviousTargetCell[1]) { + mTargetRank = mContent.findNearestArea((int) r[0], (int) r[1], 1, 1); + if (mTargetRank != mPrevTargetRank) { mReorderAlarm.cancelAlarm(); mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); mReorderAlarm.setAlarm(REORDER_DELAY); - mPreviousTargetCell[0] = mTargetCell[0]; - mPreviousTargetCell[1] = mTargetCell[1]; + mPrevTargetRank = mTargetRank; } } @@ -764,7 +632,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList replaceFolderWithFinalItem(); } } else { - setupContentForNumItems(getItemCount()); + rearrangeChildren(); // The drag failed, we need to return the item to the folder mFolderIcon.onDrop(d); } @@ -865,37 +733,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return true; } - private void setupContentDimensions(int count) { - ArrayList list = getItemsInReadingOrder(); - - int countX = mContent.getCountX(); - int countY = mContent.getCountY(); - boolean done = false; - - while (!done) { - int oldCountX = countX; - int oldCountY = countY; - if (countX * countY < count) { - // Current grid is too small, expand it - if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) { - countX++; - } else if (countY < mMaxCountY) { - countY++; - } - if (countY == 0) countY++; - } else if ((countY - 1) * countX >= count && countY >= countX) { - countY = Math.max(0, countY - 1); - } else if ((countX - 1) * countY >= count) { - countX = Math.max(0, countX - 1); - } - done = countX == oldCountX && countY == oldCountY; - } - mContent.setGridSize(countX, countY); - arrangeChildren(list); - } - public boolean isFull() { - return getItemCount() >= mMaxNumItems; + return mContent.isFull(); } private void centerAboutIcon() { @@ -905,13 +744,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); int height = getFolderHeight(); - float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, mTempRect); + float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - int centerX = (int) (mTempRect.left + mTempRect.width() * scale / 2); - int centerY = (int) (mTempRect.top + mTempRect.height() * scale / 2); + int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2); + int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2); int centeredLeft = centerX - width / 2; int centeredTop = centerY - height / 2; int currentPage = mLauncher.getWorkspace().getNextPage(); @@ -964,18 +803,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mFolderIconPivotY; } - private void setupContentForNumItems(int count) { - setupContentDimensions(count); - - DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); - if (lp == null) { - lp = new DragLayer.LayoutParams(0, 0); - lp.customPosition = true; - setLayoutParams(lp); - } - centerAboutIcon(); - } - private int getContentAreaHeight() { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -983,7 +810,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList CellLayout.LANDSCAPE : CellLayout.PORTRAIT); int maxContentAreaHeight = grid.availableHeightPx - workspacePadding.top - workspacePadding.bottom - - mFolderNameHeight; + mBottomContentHeight; int height = Math.min(maxContentAreaHeight, mContent.getDesiredHeight()); return Math.max(height, MIN_CONTENT_DIMEN); @@ -994,54 +821,52 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } private int getFolderHeight() { - int height = getPaddingTop() + getPaddingBottom() - + getContentAreaHeight() + mFolderNameHeight; - return height; + return getFolderHeight(getContentAreaHeight()); + } + + private int getFolderHeight(int contentAreaHeight) { + return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mBottomContentHeight; } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); - int height = getFolderHeight(); - int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(getContentAreaWidth(), - MeasureSpec.EXACTLY); - int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(getContentAreaHeight(), - MeasureSpec.EXACTLY); - - mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight()); - mScrollView.measure(contentAreaWidthSpec, contentAreaHeightSpec); - mFolderName.measure(contentAreaWidthSpec, - MeasureSpec.makeMeasureSpec(mFolderNameHeight, MeasureSpec.EXACTLY)); - setMeasuredDimension(width, height); - } - - private void arrangeChildren(ArrayList list) { - int[] vacant = new int[2]; - if (list == null) { - list = getItemsInReadingOrder(); - } - mContent.removeAllViews(); + int contentWidth = getContentAreaWidth(); + int contentHeight = getContentAreaHeight(); - for (int i = 0; i < list.size(); i++) { - View v = list.get(i); - mContent.getVacantCell(vacant, 1, 1); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - lp.cellX = vacant[0]; - lp.cellY = vacant[1]; - ItemInfo info = (ItemInfo) v.getTag(); - if (info.cellX != vacant[0] || info.cellY != vacant[1]) { - info.cellX = vacant[0]; - info.cellY = vacant[1]; - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY); - } - boolean insert = false; - mContent.addViewToCellLayout(v, insert ? 0 : -1, (int)info.id, lp, true); - } + int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY); + int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY); + + mContent.setFixedSize(contentWidth, contentHeight); + mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec); + + // Move the bottom content below mContent + mBottomContent.measure(contentAreaWidthSpec, + MeasureSpec.makeMeasureSpec(mBottomContentHeight, MeasureSpec.EXACTLY)); + + int folderWidth = getPaddingLeft() + getPaddingRight() + contentWidth; + int folderHeight = getFolderHeight(contentHeight); + setMeasuredDimension(folderWidth, folderHeight); + } + + /** + * Rearranges the children based on their rank. + */ + public void rearrangeChildren() { + rearrangeChildren(-1); + } + + /** + * Rearranges the children based on their rank. + * @param itemCount if greater than the total children count, empty spaces are left at the end, + * otherwise it is ignored. + */ + public void rearrangeChildren(int itemCount) { + ArrayList views = getItemsInReadingOrder(); + mContent.arrangeChildren(views, Math.max(itemCount, views.size())); mItemsInvalidated = true; } public int getItemCount() { - return mContent.getShortcutsAndWidgets().getChildCount(); + return mContent.getItemCount(); } public View getItemAt(int index) { @@ -1058,7 +883,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderIcon.requestFocus(); if (mRearrangeOnClose) { - setupContentForNumItems(getItemCount()); + rearrangeChildren(); mRearrangeOnClose = false; } if (getItemCount() <= 1) { @@ -1153,9 +978,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View currentDragView; ShortcutInfo si = mCurrentDragInfo; if (mIsExternalDrag) { - si.cellX = mEmptyCell[0]; - si.cellY = mEmptyCell[1]; - + currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank); // Actually move the item in the database if it was an external drag. Call this // before creating the view, so that ShortcutInfo is updated appropriately. LauncherModel.addOrMoveItemInDatabase( @@ -1166,14 +989,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList updateItemLocationsInDatabaseBatch(); } mIsExternalDrag = false; - - currentDragView = createAndAddShortcut(si); } else { currentDragView = mCurrentDragView; - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) currentDragView.getLayoutParams(); - si.cellX = lp.cellX = mEmptyCell[0]; - si.cellX = lp.cellY = mEmptyCell[1]; - mContent.addViewToCellLayout(currentDragView, -1, (int) si.id, lp, true); + mContent.addViewForRank(currentDragView, si, mEmptyCellRank); } if (d.dragView.hasDrawn()) { @@ -1192,7 +1010,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList currentDragView.setVisibility(VISIBLE); } mItemsInvalidated = true; - setupContentDimensions(getItemCount()); + rearrangeChildren(); // Temporarily suppress the listener, as we did all the work already here. mSuppressOnAdd = true; @@ -1219,12 +1037,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // If the item was dropped onto this open folder, we have done the work associated // with adding the item to the folder, as indicated by mSuppressOnAdd being set if (mSuppressOnAdd) return; - if (!findAndSetEmptyCells(item)) { - // The current layout is full, can we expand it? - setupContentForNumItems(getItemCount() + 1); - findAndSetEmptyCells(item); - } - createAndAddShortcut(item); + mContent.createAndAddShortcutToEnd(item); LauncherModel.addOrMoveItemInDatabase( mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); } @@ -1239,7 +1052,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (mState == STATE_ANIMATING) { mRearrangeOnClose = true; } else { - setupContentForNumItems(getItemCount()); + rearrangeChildren(); } if (getItemCount() <= 1) { replaceFolderWithFinalItem(); diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java new file mode 100644 index 000000000..66f5224bb --- /dev/null +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -0,0 +1,285 @@ +package com.android.launcher3; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; + +import java.util.ArrayList; + +public class FolderCellLayout extends CellLayout { + + private static final int REORDER_ANIMATION_DURATION = 230; + private static final int START_VIEW_REORDER_DELAY = 30; + private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; + + private static final int[] sTempPosArray = new int[2]; + + private final FolderKeyEventListener mKeyListener = new FolderKeyEventListener(); + private final LayoutInflater mInflater; + private final IconCache mIconCache; + + private final int mMaxCountX; + private final int mMaxCountY; + private final int mMaxNumItems; + + // Indicates the last number of items used to set up the grid size + private int mAllocatedContentSize; + + private Folder mFolder; + private FocusIndicatorView mFocusIndicatorView; + + public FolderCellLayout(Context context) { + this(context, null); + } + + public FolderCellLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FolderCellLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + mMaxCountX = (int) grid.numColumns; + mMaxCountY = (int) grid.numRows; + mMaxNumItems = mMaxCountX * mMaxCountY; + + mInflater = LayoutInflater.from(context); + mIconCache = app.getIconCache(); + } + + public void setFolder(Folder folder) { + mFolder = folder; + mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); + } + + /** + * Sets up the grid size such that {@param count} items can fit in the grid. + * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while + * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. + */ + private void setupContentDimensions(int count) { + mAllocatedContentSize = count; + int countX = getCountX(); + int countY = getCountY(); + boolean done = false; + + while (!done) { + int oldCountX = countX; + int oldCountY = countY; + if (countX * countY < count) { + // Current grid is too small, expand it + if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) { + countX++; + } else if (countY < mMaxCountY) { + countY++; + } + if (countY == 0) countY++; + } else if ((countY - 1) * countX >= count && countY >= countX) { + countY = Math.max(0, countY - 1); + } else if ((countX - 1) * countY >= count) { + countX = Math.max(0, countX - 1); + } + done = countX == oldCountX && countY == oldCountY; + } + setGridSize(countX, countY); + } + + /** + * Binds items to the layout. + * @return list of items that could not be bound, probably because we hit the max size limit. + */ + public ArrayList bindItems(ArrayList items) { + ArrayList extra = new ArrayList(); + setupContentDimensions(Math.min(items.size(), mMaxNumItems)); + + int countX = getCountX(); + int rank = 0; + for (ShortcutInfo item : items) { + if (rank >= mMaxNumItems) { + extra.add(item); + continue; + } + + item.rank = rank; + item.cellX = rank % countX; + item.cellY = rank / countX; + addNewView(item); + rank++; + } + return extra; + } + + /** + * Create space for a new item at the end, and returns the rank for that item. + * Resizes the content if necessary. + */ + public int allocateNewLastItemRank() { + int rank = getItemCount(); + mFolder.rearrangeChildren(rank + 1); + return rank; + } + + /** + * Adds the new item to the end of the grid. Resizes the content if necessary. + */ + public View createAndAddShortcutToEnd(ShortcutInfo item) { + int rank = allocateNewLastItemRank(); + return createAndAddViewForRank(item, rank); + } + + public View createAndAddViewForRank(ShortcutInfo item, int rank) { + updateItemXY(item, rank); + return addNewView(item); + } + + /** + * Adds the {@param view} to the layout based on {@param rank} and updated the position + * related attributes. It assumes that {@param item} is already attached to the view. + */ + public void addViewForRank(View view, ShortcutInfo item, int rank) { + updateItemXY(item, rank); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); + lp.cellX = item.cellX; + lp.cellY = item.cellY; + addViewToCellLayout(view, -1, (int) item.id, lp, true); + } + + /** + * Updates the item cellX and cellY position + */ + private void updateItemXY(ShortcutInfo item, int rank) { + item.rank = rank; + int countX = getCountX(); + item.cellX = rank % countX; + item.cellY = rank / countX; + } + + private View addNewView(ShortcutInfo item) { + final BubbleTextView textView = (BubbleTextView) mInflater.inflate( + R.layout.folder_application, getShortcutsAndWidgets(), false); + textView.applyFromShortcutInfo(item, mIconCache, false); + textView.setOnClickListener(mFolder); + textView.setOnLongClickListener(mFolder); + textView.setOnFocusChangeListener(mFocusIndicatorView); + textView.setOnKeyListener(mKeyListener); + + CellLayout.LayoutParams lp = new CellLayout.LayoutParams( + item.cellX, item.cellY, item.spanX, item.spanY); + addViewToCellLayout(textView, -1, (int)item.id, lp, true); + return textView; + } + + /** + * Refer {@link #findNearestArea(int, int, int, int, View, boolean, int[])} + * + * @return The rank of a vacant area that can contain this object, + * nearest the requested location. + */ + public int findNearestArea(int pixelX, int pixelY, int spanX, int spanY) { + findNearestArea(pixelX, pixelY, spanX, spanY, null, false, sTempPosArray); + if (mFolder.isLayoutRtl()) { + sTempPosArray[0] = getCountX() - sTempPosArray[0] - 1; + } + + // Convert this position to rank. + return Math.min(mAllocatedContentSize - 1, + sTempPosArray[1] * getCountX() + sTempPosArray[0]); + } + + public boolean isFull() { + return getItemCount() >= mMaxNumItems; + } + + public int getItemCount() { + return getShortcutsAndWidgets().getChildCount(); + } + + /** + * Updates position and rank of all the children in the view based. + * @param list the ordered list of children. + * @param itemCount if greater than the total children count, empty spaces are left at the end. + */ + public void arrangeChildren(ArrayList list, int itemCount) { + setupContentDimensions(itemCount); + removeAllViews(); + + int newX, newY; + int rank = 0; + int countX = getCountX(); + for (View v : list) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); + newX = rank % countX; + newY = rank / countX; + ItemInfo info = (ItemInfo) v.getTag(); + if (info.cellX != newX || info.cellY != newY || info.rank != rank) { + info.cellX = newX; + info.cellY = newY; + info.rank = rank; + LauncherModel.addOrMoveItemInDatabase(getContext(), info, + mFolder.mInfo.id, 0, info.cellX, info.cellY); + } + lp.cellX = info.cellX; + lp.cellY = info.cellY; + rank ++; + addViewToCellLayout(v, -1, (int)info.id, lp, true); + } + } + + /** + * Reorders the items such that the {@param empty} spot moves to {@param target} + */ + public void realTimeReorder(int empty, int target) { + boolean wrap; + int startX; + int endX; + int startY; + int delay = 0; + float delayAmount = START_VIEW_REORDER_DELAY; + + int countX = getCountX(); + int emptyX = empty % getCountX(); + int emptyY = empty / countX; + + int targetX = target % countX; + int targetY = target / countX; + + if (target > empty) { + wrap = emptyX == countX - 1; + startY = wrap ? emptyY + 1 : emptyY; + for (int y = startY; y <= targetY; y++) { + startX = y == emptyY ? emptyX + 1 : 0; + endX = y < targetY ? countX - 1 : targetX; + for (int x = startX; x <= endX; x++) { + View v = getChildAt(x,y); + if (animateChildToPosition(v, emptyX, emptyY, + REORDER_ANIMATION_DURATION, delay, true, true)) { + emptyX = x; + emptyY = y; + delay += delayAmount; + delayAmount *= VIEW_REORDER_DELAY_FACTOR; + } + } + } + } else { + wrap = emptyX == 0; + startY = wrap ? emptyY - 1 : emptyY; + for (int y = startY; y >= targetY; y--) { + startX = y == emptyY ? emptyX - 1 : countX - 1; + endX = y > targetY ? 0 : targetX; + for (int x = startX; x >= endX; x--) { + View v = getChildAt(x,y); + if (animateChildToPosition(v, emptyX, emptyY, + REORDER_ANIMATION_DURATION, delay, true, true)) { + emptyX = x; + emptyY = y; + delay += delayAmount; + delayAmount *= VIEW_REORDER_DELAY_FACTOR; + } + } + } + } + } +} -- cgit v1.2.3 From ac721f8d5f869aa8a11cd247fe3ad5a7b3dd47cd Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 4 Mar 2015 16:33:56 -0800 Subject: Fix the broken build from recent focus navigation handling code (compilation error). Change-Id: I9beb63a88f8c7c247f397d0495501dbc0f637456 --- src/com/android/launcher3/FocusHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index ebb319b0b..c16e45b31 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -326,7 +326,7 @@ public class FocusHelper { parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); if (parent != null) { iconLayout = (CellLayout) parent.getParent(); - matrix = FocusLogic.createSparseMatrix(iconLayout, orientation, + matrix = FocusLogic.createSparseMatrix(iconLayout, iconLayout.getCountX(), row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, FocusLogic.PIVOT, pageIndex - 1, pageCount); @@ -353,7 +353,7 @@ public class FocusHelper { parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); if (parent != null) { iconLayout = (CellLayout) parent.getParent(); - matrix = FocusLogic.createSparseMatrix(iconLayout, orientation, -1, row); + matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, FocusLogic.PIVOT, pageIndex, pageCount); newIcon = parent.getChildAt(newIconIndex); -- cgit v1.2.3 From bc753359f8b08e0813016eebc8392b83a4d2bd6e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 09:40:44 -0800 Subject: Extracting a common interface out of FolderCellLayout Change-Id: Ia94a75ac232b8b425c8befdf2e4f064678531505 --- src/com/android/launcher3/Folder.java | 141 ++++++++++++++++-------- src/com/android/launcher3/FolderCellLayout.java | 86 +++++++++------ 2 files changed, 144 insertions(+), 83 deletions(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 80fc32880..4414672cc 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -48,6 +48,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.Workspace.ItemOperator; import java.util.ArrayList; import java.util.Collections; @@ -95,7 +96,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private FolderIcon mFolderIcon; - private FolderCellLayout mContent; + private FolderContent mContent; private View mContentWrapper; FolderEditText mFolderName; @@ -160,17 +161,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList protected void onFinishInflate() { super.onFinishInflate(); mContentWrapper = findViewById(R.id.folder_content_wrapper); - mContent = (FolderCellLayout) findViewById(R.id.folder_content); + mContent = (FolderContent) findViewById(R.id.folder_content); mContent.setFolder(this); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - mContent.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); - mContent.setGridSize(0, 0); - mContent.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); - mContent.setInvertIfRtl(true); - mFolderName = (FolderEditText) findViewById(R.id.folder_name); mFolderName.setFolder(this); mFolderName.setOnFocusChangeListener(this); @@ -435,8 +428,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList reveal.setDuration(mMaterialExpandDuration); reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - mContent.setAlpha(0f); - Animator iconsAlpha = LauncherAnimUtils.ofFloat(mContent, "alpha", 0f, 1f); + mContentWrapper.setAlpha(0f); + Animator iconsAlpha = LauncherAnimUtils.ofFloat(mContentWrapper, "alpha", 0f, 1f); iconsAlpha.setDuration(mMaterialExpandDuration); iconsAlpha.setStartDelay(mMaterialExpandStagger); iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); @@ -459,11 +452,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList openFolderAnim = anim; - mContent.setLayerType(LAYER_TYPE_HARDWARE, null); + mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null); onCompleteRunnable = new Runnable() { @Override public void run() { - mContent.setLayerType(LAYER_TYPE_NONE, null); + mContentWrapper.setLayerType(LAYER_TYPE_NONE, null); } }; } @@ -471,8 +464,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public void onAnimationStart(Animator animation) { sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - String.format(getContext().getString(R.string.folder_opened), - mContent.getCountX(), mContent.getCountY())); + mContent.getAccessibilityDescription()); mState = STATE_ANIMATING; } @Override @@ -483,7 +475,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList onCompleteRunnable.run(); } - setFocusOnFirstChild(); + mContent.setFocusOnFirstChild(); } }); openFolderAnim.start(); @@ -512,13 +504,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } - private void setFocusOnFirstChild() { - View firstChild = mContent.getChildAt(0, 0); - if (firstChild != null) { - firstChild.requestFocus(); - } - } - public void animateClosed() { if (!(getParent() instanceof DragLayer)) return; PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0); @@ -576,7 +561,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList r[0] -= getPaddingLeft(); r[1] -= getPaddingTop(); - mTargetRank = mContent.findNearestArea((int) r[0], (int) r[1], 1, 1); + mTargetRank = mContent.findNearestArea((int) r[0], (int) r[1]); if (mTargetRank != mPrevTargetRank) { mReorderAlarm.cancelAlarm(); mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); @@ -869,10 +854,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mContent.getItemCount(); } - public View getItemAt(int index) { - return mContent.getShortcutsAndWidgets().getChildAt(index); - } - private void onCloseComplete() { DragLayer parent = (DragLayer) getParent(); if (parent != null) { @@ -933,7 +914,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } }; - View finalChild = getItemAt(0); + View finalChild = mContent.getLastItem(); if (finalChild != null) { mFolderIcon.performDestroyAnimation(finalChild, onCompleteRunnable); } else { @@ -949,8 +930,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // This method keeps track of the last item in the folder for the purposes // of keyboard focus private void updateTextViewFocus() { - View lastChild = getItemAt(getItemCount() - 1); - getItemAt(getItemCount() - 1); + View lastChild = mContent.getLastItem(); if (lastChild != null) { mFolderName.setNextFocusDownId(lastChild.getId()); mFolderName.setNextFocusRightId(lastChild.getId()); @@ -1037,7 +1017,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // If the item was dropped onto this open folder, we have done the work associated // with adding the item to the folder, as indicated by mSuppressOnAdd being set if (mSuppressOnAdd) return; - mContent.createAndAddShortcutToEnd(item); + mContent.createAndAddViewForRank(item, mContent.allocateNewLastItemRank()); LauncherModel.addOrMoveItemInDatabase( mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); } @@ -1059,16 +1039,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } - private View getViewForInfo(ShortcutInfo item) { - for (int j = 0; j < mContent.getCountY(); j++) { - for (int i = 0; i < mContent.getCountX(); i++) { - View v = mContent.getChildAt(i, j); - if (v.getTag() == item) { - return v; - } + private View getViewForInfo(final ShortcutInfo item) { + return mContent.iterateOverItems(new ItemOperator() { + + @Override + public boolean evaluate(ItemInfo info, View view, View parent) { + return info == item; } - } - return null; + }); } public void onItemsChanged() { @@ -1081,14 +1059,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public ArrayList getItemsInReadingOrder() { if (mItemsInvalidated) { mItemsInReadingOrder.clear(); - for (int j = 0; j < mContent.getCountY(); j++) { - for (int i = 0; i < mContent.getCountX(); i++) { - View v = mContent.getChildAt(i, j); - if (v != null) { - mItemsInReadingOrder.add(v); - } + mContent.iterateOverItems(new ItemOperator() { + + @Override + public boolean evaluate(ItemInfo info, View view, View parent) { + mItemsInReadingOrder.add(view); + return false; } - } + }); mItemsInvalidated = false; } return mItemsInReadingOrder; @@ -1108,4 +1086,69 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void getHitRectRelativeToDragLayer(Rect outRect) { getHitRect(outRect); } + + public static interface FolderContent { + void setFolder(Folder f); + + void removeView(View v); + + boolean isFull(); + int getItemCount(); + + int getDesiredWidth(); + int getDesiredHeight(); + void setFixedSize(int width, int height); + + /** + * Iterates over all its items in a reading order. + * @return the view for which the operator returned true. + */ + View iterateOverItems(ItemOperator op); + View getLastItem(); + + String getAccessibilityDescription(); + + /** + * Binds items to the layout. + * @return list of items that could not be bound, probably because we hit the max size limit. + */ + ArrayList bindItems(ArrayList children); + + /** + * Create space for a new item at the end, and returns the rank for that item. + * Resizes the content if necessary. + */ + int allocateNewLastItemRank(); + + View createAndAddViewForRank(ShortcutInfo item, int rank); + + /** + * Adds the {@param view} to the layout based on {@param rank} and updated the position + * related attributes. It assumes that {@param item} is already attached to the view. + */ + void addViewForRank(View view, ShortcutInfo item, int rank); + + /** + * Reorders the items such that the {@param empty} spot moves to {@param target} + */ + void realTimeReorder(int empty, int target); + + /** + * @return the rank of the cell nearest to the provided pixel position. + */ + int findNearestArea(int pixelX, int pixelY); + + /** + * Updates position and rank of all the children in the view based. + * @param list the ordered list of children. + * @param itemCount if greater than the total children count, empty spaces are left + * at the end. + */ + void arrangeChildren(ArrayList list, int itemCount); + + /** + * Sets the focus on the first visible child. + */ + void setFocusOnFirstChild(); + } } diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java index 66f5224bb..21f2a1112 100644 --- a/src/com/android/launcher3/FolderCellLayout.java +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -5,9 +5,11 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import com.android.launcher3.Workspace.ItemOperator; + import java.util.ArrayList; -public class FolderCellLayout extends CellLayout { +public class FolderCellLayout extends CellLayout implements Folder.FolderContent { private static final int REORDER_ANIMATION_DURATION = 230; private static final int START_VIEW_REORDER_DELAY = 30; @@ -48,8 +50,13 @@ public class FolderCellLayout extends CellLayout { mInflater = LayoutInflater.from(context); mIconCache = app.getIconCache(); + + setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); + getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); + setInvertIfRtl(true); } + @Override public void setFolder(Folder folder) { mFolder = folder; mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); @@ -87,10 +94,7 @@ public class FolderCellLayout extends CellLayout { setGridSize(countX, countY); } - /** - * Binds items to the layout. - * @return list of items that could not be bound, probably because we hit the max size limit. - */ + @Override public ArrayList bindItems(ArrayList items) { ArrayList extra = new ArrayList(); setupContentDimensions(Math.min(items.size(), mMaxNumItems)); @@ -112,33 +116,20 @@ public class FolderCellLayout extends CellLayout { return extra; } - /** - * Create space for a new item at the end, and returns the rank for that item. - * Resizes the content if necessary. - */ + @Override public int allocateNewLastItemRank() { int rank = getItemCount(); mFolder.rearrangeChildren(rank + 1); return rank; } - /** - * Adds the new item to the end of the grid. Resizes the content if necessary. - */ - public View createAndAddShortcutToEnd(ShortcutInfo item) { - int rank = allocateNewLastItemRank(); - return createAndAddViewForRank(item, rank); - } - + @Override public View createAndAddViewForRank(ShortcutInfo item, int rank) { updateItemXY(item, rank); return addNewView(item); } - /** - * Adds the {@param view} to the layout based on {@param rank} and updated the position - * related attributes. It assumes that {@param item} is already attached to the view. - */ + @Override public void addViewForRank(View view, ShortcutInfo item, int rank) { updateItemXY(item, rank); CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); @@ -174,12 +165,10 @@ public class FolderCellLayout extends CellLayout { /** * Refer {@link #findNearestArea(int, int, int, int, View, boolean, int[])} - * - * @return The rank of a vacant area that can contain this object, - * nearest the requested location. */ - public int findNearestArea(int pixelX, int pixelY, int spanX, int spanY) { - findNearestArea(pixelX, pixelY, spanX, spanY, null, false, sTempPosArray); + @Override + public int findNearestArea(int pixelX, int pixelY) { + findNearestArea(pixelX, pixelY, 1, 1, null, false, sTempPosArray); if (mFolder.isLayoutRtl()) { sTempPosArray[0] = getCountX() - sTempPosArray[0] - 1; } @@ -189,19 +178,17 @@ public class FolderCellLayout extends CellLayout { sTempPosArray[1] * getCountX() + sTempPosArray[0]); } + @Override public boolean isFull() { return getItemCount() >= mMaxNumItems; } + @Override public int getItemCount() { return getShortcutsAndWidgets().getChildCount(); } - /** - * Updates position and rank of all the children in the view based. - * @param list the ordered list of children. - * @param itemCount if greater than the total children count, empty spaces are left at the end. - */ + @Override public void arrangeChildren(ArrayList list, int itemCount) { setupContentDimensions(itemCount); removeAllViews(); @@ -228,9 +215,40 @@ public class FolderCellLayout extends CellLayout { } } - /** - * Reorders the items such that the {@param empty} spot moves to {@param target} - */ + @Override + public View iterateOverItems(ItemOperator op) { + for (int j = 0; j < getCountY(); j++) { + for (int i = 0; i < getCountX(); i++) { + View v = getChildAt(i, j); + if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) { + return v; + } + } + } + return null; + } + + @Override + public String getAccessibilityDescription() { + return String.format(getContext().getString(R.string.folder_opened), + getCountX(), getCountY()); + } + + @Override + public void setFocusOnFirstChild() { + View firstChild = getChildAt(0, 0); + if (firstChild != null) { + firstChild.requestFocus(); + } + } + + @Override + public View getLastItem() { + int total = getShortcutsAndWidgets().getChildCount(); + return getShortcutsAndWidgets().getChildAt(total % getCountX(), total / getCountX()); + } + + @Override public void realTimeReorder(int empty, int target) { boolean wrap; int startX; -- cgit v1.2.3 From 95deb3a57cfebe51e5b8ea74e0a870123d1347e9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 10:27:01 -0800 Subject: Fixing icon disappearing on dropping to workspace > It was created in ag/645900 Change-Id: I31ca5bf9763292517fa5ee3ccd867897bca45bad --- src/com/android/launcher3/Workspace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 60457fd7c..3d29b6fec 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4169,7 +4169,7 @@ public class Workspace extends SmoothPagedView boolean beingCalledAfterUninstall = mDeferredAction != null; if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) { - if (mDragInfo != null) { + if (target != this && mDragInfo != null) { removeWorkspaceItem(mDragInfo.cell); } } else if (mDragInfo != null) { -- cgit v1.2.3 From d81992b6e70e05a3c208e02d019e606b7cb8a0b7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 14:00:18 -0800 Subject: Setting hotseat padding to the container for symmetry Change-Id: I953aa4e8d729db88dae29f51af28c6cf01891bd6 --- src/com/android/launcher3/DeviceProfile.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index b97f0f2f7..7d02e10b5 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -766,8 +766,7 @@ public class DeviceProfile { lp.gravity = Gravity.BOTTOM; lp.width = LayoutParams.MATCH_PARENT; lp.height = hotseatBarHeightPx; - hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0, - 2 * edgeMarginPx, 0); + hotseat.setPadding(2 * edgeMarginPx, 0, 2 * edgeMarginPx, 0); } hotseat.setLayoutParams(lp); -- cgit v1.2.3 From 82e861d71aaefcdec47d78bf564d755d2910ecb8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 14:56:29 -0800 Subject: Using unique view id for each item in folder Change-Id: I413de3db94dbab54a9c1e5799286ac78a1ae5102 --- src/com/android/launcher3/FolderCellLayout.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java index 21f2a1112..e6e9199e8 100644 --- a/src/com/android/launcher3/FolderCellLayout.java +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -135,7 +135,7 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); lp.cellX = item.cellX; lp.cellY = item.cellY; - addViewToCellLayout(view, -1, (int) item.id, lp, true); + addViewToCellLayout(view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); } /** @@ -159,7 +159,7 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent CellLayout.LayoutParams lp = new CellLayout.LayoutParams( item.cellX, item.cellY, item.spanX, item.spanY); - addViewToCellLayout(textView, -1, (int)item.id, lp, true); + addViewToCellLayout(textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); return textView; } @@ -211,7 +211,7 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent lp.cellX = info.cellX; lp.cellY = info.cellY; rank ++; - addViewToCellLayout(v, -1, (int)info.id, lp, true); + addViewToCellLayout(v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); } } @@ -244,8 +244,8 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent @Override public View getLastItem() { - int total = getShortcutsAndWidgets().getChildCount(); - return getShortcutsAndWidgets().getChildAt(total % getCountX(), total / getCountX()); + int lastRank = getShortcutsAndWidgets().getChildCount() - 1; + return getShortcutsAndWidgets().getChildAt(lastRank % getCountX(), lastRank / getCountX()); } @Override -- cgit v1.2.3 From a39b82e2eba2580a210e34ce1a842589529348bf Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 13:43:28 -0800 Subject: Using left and top instead of getLocationInWindow for getting target view position Change-Id: I1c295b8fd8be46ed9f0b12d7019572d9adc4af54 --- src/com/android/launcher3/FocusIndicatorView.java | 51 +++++++++++++---------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java index 7d4664abb..53dc2e22b 100644 --- a/src/com/android/launcher3/FocusIndicatorView.java +++ b/src/com/android/launcher3/FocusIndicatorView.java @@ -23,7 +23,6 @@ import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Pair; import android.view.View; -import android.view.ViewParent; public class FocusIndicatorView extends View implements View.OnFocusChangeListener { @@ -32,9 +31,6 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen private static final float MIN_VISIBLE_ALPHA = 0.2f; private static final long ANIM_DURATION = 150; - private static final int[] sTempPos = new int[2]; - private static final int[] sTempShift = new int[2]; - private final int[] mIndicatorPos = new int[2]; private final int[] mTargetViewPos = new int[2]; @@ -44,6 +40,8 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen private View mLastFocusedView; private boolean mInitiated; + private View mCommonParent; + private Pair mPendingCall; public FocusIndicatorView(Context context) { @@ -80,7 +78,9 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen } if (!mInitiated) { - getLocationRelativeToParentPagedView(this, mIndicatorPos); + // The parent view should always the a parent of the target view. + mCommonParent = (View) this.getParent(); + computeLocationRelativeToParent(this, mCommonParent, mIndicatorPos); mInitiated = true; } @@ -93,7 +93,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen nextState.scaleX = v.getScaleX() * v.getWidth() / indicatorWidth; nextState.scaleY = v.getScaleY() * v.getHeight() / indicatorHeight; - getLocationRelativeToParentPagedView(v, mTargetViewPos); + computeLocationRelativeToParent(v, mCommonParent, mTargetViewPos); nextState.x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - nextState.scaleX) * indicatorWidth / 2; nextState.y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - nextState.scaleY) * indicatorHeight / 2; @@ -150,27 +150,32 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen } /** - * Gets the location of a view relative in the window, off-setting any shift due to - * page view scroll + * Computes the location of a view relative to {@link #mCommonParent}, off-setting + * any shift due to page view scroll. + * @param pos an array of two integers in which to hold the coordinates */ - private static void getLocationRelativeToParentPagedView(View v, int[] pos) { - getPagedViewScrollShift(v, sTempShift); - v.getLocationInWindow(sTempPos); - pos[0] = sTempPos[0] + sTempShift[0]; - pos[1] = sTempPos[1] + sTempShift[1]; + private static void computeLocationRelativeToParent(View v, View parent, int[] pos) { + pos[0] = pos[1] = 0; + computeLocationRelativeToParentHelper(v, parent, pos); + + // If a view is scaled, its position will also shift accordingly. For optimization, only + // consider this for the last node. + pos[0] += (1 - v.getScaleX()) * v.getWidth() / 2; + pos[1] += (1 - v.getScaleY()) * v.getHeight() / 2; } - private static void getPagedViewScrollShift(View child, int[] shift) { - ViewParent parent = child.getParent(); + private static void computeLocationRelativeToParentHelper(View child, + View commonParent, int[] shift) { + View parent = (View) child.getParent(); if (parent instanceof PagedView) { - View parentView = (View) parent; - child.getLocationInWindow(sTempPos); - shift[0] = parentView.getPaddingLeft() - sTempPos[0]; - shift[1] = -(int) child.getTranslationY(); - } else if (parent instanceof View) { - getPagedViewScrollShift((View) parent, shift); - } else { - shift[0] = shift[1] = 0; + child = ((PagedView) parent).getPageAt(0); + } + + shift[0] += child.getLeft(); + shift[1] += child.getTop(); + + if (parent != commonParent) { + computeLocationRelativeToParentHelper(parent, commonParent, shift); } } -- cgit v1.2.3 From ee995106d8a50b691e65988f5b3df5d2d83ca69a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 16:27:37 -0800 Subject: Some parameter cleanup Change-Id: Iedc7c97bd9c513ff046f7bf64a6e13f1fc8e8b54 --- src/com/android/launcher3/FocusIndicatorView.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java index 53dc2e22b..af3b97634 100644 --- a/src/com/android/launcher3/FocusIndicatorView.java +++ b/src/com/android/launcher3/FocusIndicatorView.java @@ -40,8 +40,6 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen private View mLastFocusedView; private boolean mInitiated; - private View mCommonParent; - private Pair mPendingCall; public FocusIndicatorView(Context context) { @@ -79,8 +77,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen if (!mInitiated) { // The parent view should always the a parent of the target view. - mCommonParent = (View) this.getParent(); - computeLocationRelativeToParent(this, mCommonParent, mIndicatorPos); + computeLocationRelativeToParent(this, (View) getParent(), mIndicatorPos); mInitiated = true; } @@ -93,7 +90,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen nextState.scaleX = v.getScaleX() * v.getWidth() / indicatorWidth; nextState.scaleY = v.getScaleY() * v.getHeight() / indicatorHeight; - computeLocationRelativeToParent(v, mCommonParent, mTargetViewPos); + computeLocationRelativeToParent(v, (View) getParent(), mTargetViewPos); nextState.x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - nextState.scaleX) * indicatorWidth / 2; nextState.y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - nextState.scaleY) * indicatorHeight / 2; -- cgit v1.2.3 From 1c0e633bd59e0f14f799cbb6aee33266032998fa Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 16:56:12 -0800 Subject: Handling getLastItem when the folder is closed Change-Id: I6afebd7c0a92edb25e3658e7f860244d7457c852 --- src/com/android/launcher3/FolderCellLayout.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java index e6e9199e8..b354ec74e 100644 --- a/src/com/android/launcher3/FolderCellLayout.java +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -245,7 +245,13 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent @Override public View getLastItem() { int lastRank = getShortcutsAndWidgets().getChildCount() - 1; - return getShortcutsAndWidgets().getChildAt(lastRank % getCountX(), lastRank / getCountX()); + // count can be zero if the folder is not yet laid out. + int count = getCountX(); + if (count > 0) { + return getShortcutsAndWidgets().getChildAt(lastRank % count, lastRank / count); + } else { + return getShortcutsAndWidgets().getChildAt(lastRank); + } } @Override -- cgit v1.2.3 From c9735cff2e558aa3f3810e49c15ef13049b9429c Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 23 Jan 2015 16:11:55 -0800 Subject: Enabling accessible drag and drop -> Using the context menu, and a new two stage system, this allows users to curate icons and widgets on the workspace -> Move icons / widgets to any empty cell on any existing screen, or create a new screen (appended to the right, as with regular drag and drop) -> Move icons into existing folders -> Create folders by moving an icon onto another icon -> Also added confirmations for these and some existing accessibility actions Limitations: -> Currently, no support for drag and drop in folders -> Considering moving the drag view so it doesn't occlude any content (in particular, when user changes pages) -> In this mode, accessibility framework seems to have problems with the next / prev operations Bug: 18482913 Change-Id: I19b0be9dc8bfa766d430408c8ad9303c716b89b2 --- Android.mk | 2 + res/values/config.xml | 1 + res/values/strings.xml | 28 ++ src/com/android/launcher3/CellLayout.java | 306 ++++++++++++++++++++- src/com/android/launcher3/DragController.java | 56 +++- src/com/android/launcher3/DropTarget.java | 3 + src/com/android/launcher3/Launcher.java | 6 +- .../launcher3/LauncherAccessibilityDelegate.java | 107 ++++++- src/com/android/launcher3/LauncherAppState.java | 4 +- src/com/android/launcher3/SearchDropTargetBar.java | 22 +- src/com/android/launcher3/Workspace.java | 63 ++++- 11 files changed, 556 insertions(+), 42 deletions(-) diff --git a/Android.mk b/Android.mk index 632dd0961..5267469b3 100644 --- a/Android.mk +++ b/Android.mk @@ -23,6 +23,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 + LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, WallpaperPicker/src) \ $(call all-renderscript-files-under, src) \ diff --git a/res/values/config.xml b/res/values/config.xml index cbec51216..1acace6f9 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -104,4 +104,5 @@ + diff --git a/res/values/strings.xml b/res/values/strings.xml index 8b7e6c199..74b88148c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -305,4 +305,32 @@ s --> Add To Workspace + + + Item added to workspace + + + Item removed from workspace + + + Move Item + + + Move to empty cell %1$s, %2$s + + + Item moved + + + Add to folder: %1$s + + + Item added to folder + + + Create folder with: %1$s + + + Folder created + diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index a3500aa82..c57090d7c 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -22,6 +22,7 @@ import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -33,25 +34,34 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; import android.os.Parcelable; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.widget.ExploreByTouchHelper; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.MotionEvent; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; import android.view.animation.LayoutAnimationController; import com.android.launcher3.FolderIcon.FolderRingAnimator; +import com.android.launcher3.LauncherAccessibilityDelegate.DragType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Stack; public class CellLayout extends ViewGroup { @@ -169,6 +179,14 @@ public class CellLayout extends ViewGroup { private final static Paint sPaint = new Paint(); + // Related to accessible drag and drop + DragAndDropAccessibilityDelegate mTouchHelper = new DragAndDropAccessibilityDelegate(this); + private boolean mUseTouchHelper = false; + OnClickListener mOldClickListener = null; + OnClickListener mOldWorkspaceListener = null; + private int mDownX = 0; + private int mDownY = 0; + public CellLayout(Context context) { this(context, null); } @@ -294,6 +312,282 @@ public class CellLayout extends ViewGroup { addView(mShortcutsAndWidgets); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void enableAccessibleDrag(boolean enable) { + mUseTouchHelper = enable; + if (!enable) { + ViewCompat.setAccessibilityDelegate(this, null); + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + setOnClickListener(mLauncher); + } else { + ViewCompat.setAccessibilityDelegate(this, mTouchHelper); + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + setOnClickListener(mTouchHelper); + } + + // Invalidate the accessibility hierarchy + if (getParent() != null) { + getParent().notifySubtreeAccessibilityStateChanged( + this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); + } + } + + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Always attempt to dispatch hover events to accessibility first. + if (mUseTouchHelper && mTouchHelper.dispatchHoverEvent(event)) { + return true; + } + return super.dispatchHoverEvent(event); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mDownX = (int) event.getX(); + mDownY = (int) event.getY(); + } + return super.dispatchTouchEvent(event); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mUseTouchHelper || + (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) { + return true; + } + return false; + } + + class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper implements OnClickListener { + private final Rect mTempRect = new Rect(); + + public DragAndDropAccessibilityDelegate(View forView) { + super(forView); + } + + private int getViewIdAt(float x, float y) { + if (x < 0 || y < 0 || x > getMeasuredWidth() || y > getMeasuredHeight()) { + return ExploreByTouchHelper.INVALID_ID; + } + + // Map coords to cell + int cellX = (int) Math.floor(x / (mCellWidth + mWidthGap)); + int cellY = (int) Math.floor(y / (mCellHeight + mHeightGap)); + + // Map cell to id + int id = cellX * mCountY + cellY; + return id; + } + + @Override + protected int getVirtualViewAt(float x, float y) { + return nearestDropLocation(getViewIdAt(x, y)); + } + + protected int nearestDropLocation(int id) { + int count = mCountX * mCountY; + for (int delta = 0; delta < count; delta++) { + if (id + delta <= (count - 1)) { + int target = intersectsValidDropTarget(id + delta); + if (target >= 0) { + return target; + } + } else if (id - delta >= 0) { + int target = intersectsValidDropTarget(id - delta); + if (target >= 0) { + return target; + } + } + } + return ExploreByTouchHelper.INVALID_ID; + } + + /** + * Find the virtual view id corresponding to the top left corner of any drop region by which + * the passed id is contained. For an icon, this is simply + * + * @param id the id we're interested examining (ie. does it fit there?) + * @return the view id of the top left corner of a valid drop region or -1 if there is no + * such valid region. For the icon, this can just be -1 or id. + */ + protected int intersectsValidDropTarget(int id) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); + + int y = id % mCountY; + int x = id / mCountY; + + if (dragInfo.dragType == DragType.WIDGET) { + // For a widget, every cell must be vacant. In addition, we will return any valid + // drop target by which the passed id is contained. + boolean fits = false; + + // These represent the amount that we can back off if we hit a problem. They + // get consumed as we move up and to the right, trying new regions. + int spanX = dragInfo.info.spanX; + int spanY = dragInfo.info.spanY; + + for (int m = 0; m < spanX; m++) { + for (int n = 0; n < spanY; n++) { + + fits = true; + int x0 = x - m; + int y0 = y - n; + + if (x0 < 0 || y0 < 0) continue; + + for (int i = x0; i < x0 + spanX; i++) { + if (!fits) break; + for (int j = y0; j < y0 + spanY; j++) { + if (i >= mCountX || j >= mCountY || mOccupied[i][j]) { + fits = false; + break; + } + } + } + if (fits) { + return x0 * mCountY + y0; + } + } + } + return -1; + } else { + // For an icon, we simply check the view directly below + View child = getChildAt(x, y); + if (child == null || child == dragInfo.item) { + // Empty cell. Good for an icon or folder. + return id; + } else if (dragInfo.dragType != DragType.FOLDER) { + // For icons, we can consider cells that have another icon or a folder. + ItemInfo info = (ItemInfo) child.getTag(); + if (info instanceof AppInfo || info instanceof FolderInfo || + info instanceof ShortcutInfo) { + return id; + } + } + return -1; + } + } + + @Override + protected void getVisibleVirtualViews(List virtualViews) { + // We create a virtual view for each cell of the grid + // The cell ids correspond to cells in reading order. + int nCells = mCountX * mCountY; + + for (int i = 0; i < nCells; i++) { + if (intersectsValidDropTarget(i) >= 0) { + virtualViews.add(i); + } + } + } + + @Override + protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) { + if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { + String confirmation = getConfirmationForIconDrop(viewId); + LauncherAppState.getInstance().getAccessibilityDelegate() + .handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); + return true; + } + return false; + } + + @Override + public void onClick(View arg0) { + int viewId = getViewIdAt(mDownX, mDownY); + + String confirmation = getConfirmationForIconDrop(viewId); + LauncherAppState.getInstance().getAccessibilityDelegate() + .handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); + } + + @Override + protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) { + if (id == ExploreByTouchHelper.INVALID_ID) { + throw new IllegalArgumentException("Invalid virtual view id"); + } + // We're required to set something here. + event.setContentDescription(""); + } + + @Override + protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) { + if (id == ExploreByTouchHelper.INVALID_ID) { + throw new IllegalArgumentException("Invalid virtual view id"); + } + + node.setContentDescription(getLocationDescriptionForIconDrop(id)); + node.setBoundsInParent(getItemBounds(id)); + + node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); + node.setClickable(true); + node.setFocusable(true); + } + + private String getLocationDescriptionForIconDrop(int id) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); + + int y = id % mCountY; + int x = id / mCountY; + + Resources res = getContext().getResources(); + View child = getChildAt(x, y); + if (child == null || child == dragInfo.item) { + return res.getString(R.string.move_to_empty_cell, x, y); + } else { + ItemInfo info = (ItemInfo) child.getTag(); + if (info instanceof AppInfo || info instanceof ShortcutInfo) { + return res.getString(R.string.create_folder_with, info.title); + } else if (info instanceof FolderInfo) { + return res.getString(R.string.add_to_folder, info.title); + } + } + return ""; + } + + private String getConfirmationForIconDrop(int id) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); + + int y = id % mCountY; + int x = id / mCountY; + + Resources res = getContext().getResources(); + View child = getChildAt(x, y); + if (child == null || child == dragInfo.item) { + return res.getString(R.string.item_moved); + } else { + ItemInfo info = (ItemInfo) child.getTag(); + if (info instanceof AppInfo || info instanceof ShortcutInfo) { + return res.getString(R.string.folder_created); + + } else if (info instanceof FolderInfo) { + return res.getString(R.string.added_to_folder); + } + } + return ""; + } + + private Rect getItemBounds(int id) { + int cellY = id % mCountY; + int cellX = id / mCountY; + int x = getPaddingLeft() + (int) (cellX * (mCellWidth + mWidthGap)); + int y = getPaddingTop() + (int) (cellY * (mCellHeight + mHeightGap)); + + Rect bounds = mTempRect; + bounds.set(x, y, x + mCellWidth, y + mCellHeight); + return bounds; + } + } + public void enableHardwareLayer(boolean hasLayer) { mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint); } @@ -679,18 +973,6 @@ public class CellLayout extends ViewGroup { mShortcutsAndWidgets.removeViewsInLayout(start, count); } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - // First we clear the tag to ensure that on every touch down we start with a fresh slate, - // even in the case where we return early. Not clearing here was causing bugs whereby on - // long-press we'd end up picking up an item from a previous drag operation. - if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) { - return true; - } - - return false; - } - /** * Given a point, return the cell that strictly encloses that point * @param x X coordinate of the point diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 480dce999..09c59a057 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -73,6 +73,9 @@ public class DragController { /** Whether or not we're dragging. */ private boolean mDragging; + /** Whether or not this is an accessible drag operation */ + private boolean mIsAccessibleDrag; + /** X coordinate of the down event. */ private int mMotionDownX; @@ -182,7 +185,7 @@ public class DragController { (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2); startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null, - null, initialDragViewScale); + null, initialDragViewScale, false); if (dragAction == DRAG_ACTION_MOVE) { v.setVisibility(View.GONE); @@ -202,10 +205,11 @@ public class DragController { * {@link #DRAG_ACTION_COPY} * @param dragRegion Coordinates within the bitmap b for the position of item being dragged. * Makes dragging feel more precise, e.g. you can clip out a transparent border + * @param accessible whether this drag should occur in accessibility mode */ public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY, DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion, - float initialDragViewScale) { + float initialDragViewScale, boolean accessible) { if (PROFILE_DRAWING_DURING_DRAG) { android.os.Debug.startMethodTracing("Launcher"); } @@ -228,12 +232,21 @@ public class DragController { final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; mDragging = true; + mIsAccessibleDrag = accessible; mDragObject = new DropTarget.DragObject(); mDragObject.dragComplete = false; - mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft); - mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop); + if (mIsAccessibleDrag) { + // For an accessible drag, we assume the view is being dragged from the center. + mDragObject.xOffset = b.getWidth() / 2; + mDragObject.yOffset = b.getHeight() / 2; + mDragObject.accessibleDrag = true; + } else { + mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft); + mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop); + } + mDragObject.dragSource = source; mDragObject.dragInfo = dragInfo; @@ -349,6 +362,7 @@ public class DragController { private void endDrag() { if (mDragging) { mDragging = false; + mIsAccessibleDrag = false; clearScrollRunnable(); boolean isDeferred = false; if (mDragObject.dragView != null) { @@ -421,6 +435,10 @@ public class DragController { + mDragging); } + if (mIsAccessibleDrag) { + return false; + } + // Update the velocity tracker acquireVelocityTrackerAndAddMovement(ev); @@ -560,7 +578,7 @@ public class DragController { * Call this from a drag source view. */ public boolean onTouchEvent(MotionEvent ev) { - if (!mDragging) { + if (!mDragging || mIsAccessibleDrag) { return false; } @@ -616,6 +634,34 @@ public class DragController { return true; } + /** + * Since accessible drag and drop won't cause the same sequence of touch events, we manually + * inject the appropriate state. + */ + public void prepareAccessibleDrag(int x, int y) { + mMotionDownX = x; + mMotionDownY = y; + mLastDropTarget = null; + } + + /** + * As above, since accessible drag and drop won't cause the same sequence of touch events, + * we manually ensure appropriate drag and drop events get emulated for accessible drag. + */ + public void completeAccessibleDrag(int[] location) { + final int[] coordinates = mCoordinatesTemp; + + // We make sure that we prime the target for drop. + DropTarget dropTarget = findDropTarget(location[0], location[1], coordinates); + mDragObject.x = coordinates[0]; + mDragObject.y = coordinates[1]; + checkTouchMove(dropTarget); + + // Perform the drop + drop(location[0], location[1]); + endDrag(); + } + /** * Determines whether the user flung the current item to delete it. * diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 7ede42751..94ae82b82 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -54,6 +54,9 @@ public interface DropTarget { /** Where the drag originated */ public DragSource dragSource = null; + /** The object is part of an accessible drag operation */ + public boolean accessibleDrag; + /** Post drag animation runnable */ public Runnable postAnimationRunnable = null; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1ad8b274d..e58d0dab4 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2447,6 +2447,10 @@ public class Launcher extends Activity return; } + if (LauncherAppState.getInstance().getAccessibilityDelegate().onBackPressed()) { + return; + } + if (isAllAppsVisible()) { if (mAppsCustomizeContent.getContentType() == AppsCustomizePagedView.ContentType.Applications) { @@ -3165,7 +3169,7 @@ public class Launcher extends Activity View itemUnderLongClick = null; if (v.getTag() instanceof ItemInfo) { ItemInfo info = (ItemInfo) v.getTag(); - longClickCellInfo = new CellLayout.CellInfo(v, info);; + longClickCellInfo = new CellLayout.CellInfo(v, info); itemUnderLongClick = longClickCellInfo.cell; resetAddInfo(); } diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index c9e277e4c..0ae1c0e90 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -1,11 +1,16 @@ package com.android.launcher3; import android.annotation.TargetApi; +import android.content.res.Resources; +import android.graphics.Rect; import android.os.Build; import android.os.Bundle; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.util.SparseArray; import android.view.View; import android.view.View.AccessibilityDelegate; +import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -20,6 +25,21 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { public static final int INFO = R.id.action_info; public static final int UNINSTALL = R.id.action_uninstall; public static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; + public static final int MOVE = R.id.action_move; + + enum DragType { + ICON, + FOLDER, + WIDGET + } + + public static class DragInfo { + DragType dragType; + ItemInfo info; + View item; + } + + private DragInfo mDragInfo = null; private final SparseArray mActions = new SparseArray(); @@ -36,6 +56,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { launcher.getText(R.string.delete_target_uninstall_label))); mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE, launcher.getText(R.string.action_add_to_workspace))); + mActions.put(MOVE, new AccessibilityAction(MOVE, + launcher.getText(R.string.action_move))); + } @Override @@ -49,6 +72,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { || (item instanceof FolderInfo)) { // Workspace shortcut / widget info.addAction(mActions.get(REMOVE)); + info.addAction(mActions.get(MOVE)); } else if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { // App or Widget from customization tray if (item instanceof AppInfo) { @@ -69,14 +93,21 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { } public boolean performAction(View host, ItemInfo item, int action) { + Resources res = mLauncher.getResources(); if (action == REMOVE) { - return DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host); + if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) { + announceConfirmation(R.string.item_removed_from_workspace); + return true; + } + return false; } else if (action == INFO) { InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); return true; } else if (action == UNINSTALL) { DeleteDropTarget.uninstallApp(mLauncher, (AppInfo) item); return true; + } else if (action == MOVE) { + beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { final int preferredPage = mLauncher.getWorkspace().getCurrentPage(); final ScreenPosProvider screenProvider = new ScreenPosProvider() { @@ -90,20 +121,92 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { final ArrayList addShortcuts = new ArrayList(); addShortcuts.add(((AppInfo) item).makeShortcut()); mLauncher.showWorkspace(true, new Runnable() { - @Override public void run() { mLauncher.getModel().addAndBindAddedWorkspaceApps( mLauncher, addShortcuts, screenProvider, 0, true); + announceConfirmation(R.string.item_added_to_workspace); } }); return true; } else if (item instanceof PendingAddItemInfo) { mLauncher.getModel().addAndBindPendingItem( mLauncher, (PendingAddItemInfo) item, screenProvider, 0); + announceConfirmation(R.string.item_added_to_workspace); return true; } } return false; } + + private void announceConfirmation(int resId) { + announceConfirmation(mLauncher.getResources().getString(resId)); + } + + private void announceConfirmation(String confirmation) { + mLauncher.getDragLayer().announceForAccessibility(confirmation); + + } + + public boolean isInAccessibleDrag() { + return mDragInfo != null; + } + + public DragInfo getDragInfo() { + return mDragInfo; + } + + public void handleAccessibleDrop(CellLayout targetContainer, Rect dropLocation, + String confirmation) { + if (!isInAccessibleDrag()) return; + + int[] loc = new int[2]; + loc[0] = dropLocation.centerX(); + loc[1] = dropLocation.centerY(); + + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(targetContainer, loc); + mLauncher.getDragController().completeAccessibleDrag(loc); + + endAccessibleDrag(); + announceConfirmation(confirmation); + } + + public void beginAccessibleDrag(View item, ItemInfo info) { + mDragInfo = new DragInfo(); + mDragInfo.info = info; + mDragInfo.item = item; + mDragInfo.dragType = DragType.ICON; + if (info instanceof FolderInfo) { + mDragInfo.dragType = DragType.FOLDER; + } else if (info instanceof LauncherAppWidgetInfo) { + mDragInfo.dragType = DragType.WIDGET; + } + + CellLayout.CellInfo cellInfo = new CellLayout.CellInfo(item, info); + + Rect pos = new Rect(); + mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos); + + mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY()); + mLauncher.getWorkspace().enableAccessibleDrag(true); + mLauncher.getWorkspace().startDrag(cellInfo, true); + } + + public boolean onBackPressed() { + if (isInAccessibleDrag()) { + cancelAccessibleDrag(); + return true; + } + return false; + } + + private void cancelAccessibleDrag() { + mLauncher.getDragController().cancelDrag(); + endAccessibleDrag(); + } + + private void endAccessibleDrag() { + mDragInfo = null; + mLauncher.getWorkspace().enableAccessibleDrag(false); + } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 8e6557f73..d8896ccd2 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -62,7 +62,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private static LauncherAppState INSTANCE; private DynamicGrid mDynamicGrid; - private AccessibilityDelegate mAccessibilityDelegate; + private LauncherAccessibilityDelegate mAccessibilityDelegate; public static LauncherAppState getInstance() { if (INSTANCE == null) { @@ -168,7 +168,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mModel; } - AccessibilityDelegate getAccessibilityDelegate() { + LauncherAccessibilityDelegate getAccessibilityDelegate() { return mAccessibilityDelegate; } diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index 99c2e0859..cc17820ff 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -197,6 +197,10 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D */ @Override public void onDragStart(DragSource source, Object info, int dragAction) { + showDeleteTarget(); + } + + public void showDeleteTarget() { // Animate out the QSB search bar, and animate in the drop target bar prepareStartAnimation(mDropTargetBar); mDropTargetBarAnim.start(); @@ -206,6 +210,16 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } } + public void hideDeleteTarget() { + // Restore the QSB search bar, and animate out the drop target bar + prepareStartAnimation(mDropTargetBar); + mDropTargetBarAnim.reverse(); + if (!mIsSearchBarHidden) { + prepareStartAnimation(mQSBSearchBar); + mQSBSearchBarAnim.reverse(); + } + } + public void deferOnDragEnd() { mDeferOnDragEnd = true; } @@ -213,13 +227,7 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D @Override public void onDragEnd() { if (!mDeferOnDragEnd) { - // Restore the QSB search bar, and animate out the drop target bar - prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.reverse(); - if (!mIsSearchBarHidden) { - prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); - } + hideDeleteTarget(); } else { mDeferOnDragEnd = false; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b9c1f4d3d..125a2ed50 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -26,6 +26,7 @@ import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; import android.app.WallpaperManager; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; @@ -44,6 +45,7 @@ import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; @@ -572,6 +574,10 @@ public class Workspace extends SmoothPagedView mWorkspaceScreens.put(screenId, newScreen); mScreenOrder.add(insertIndex, screenId); addView(newScreen, insertIndex); + + if (LauncherAppState.getInstance().getAccessibilityDelegate().isInAccessibleDrag()) { + newScreen.enableAccessibleDrag(true); + } return screenId; } @@ -1621,7 +1627,6 @@ public class Workspace extends SmoothPagedView float scrollProgress = getScrollProgress(screenCenter, child, i); float alpha = 1 - Math.abs(scrollProgress); child.getShortcutsAndWidgets().setAlpha(alpha); - //child.setBackgroundAlphaMultiplier(1 - alpha); } } } @@ -1634,6 +1639,23 @@ public class Workspace extends SmoothPagedView } } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void enableAccessibleDrag(boolean enable) { + for (int i = 0; i < getChildCount(); i++) { + CellLayout child = (CellLayout) getChildAt(i); + child.enableAccessibleDrag(enable); + } + + if (enable) { + // We need to allow our individual children to become click handlers in this case + setOnClickListener(null); + } else { + // Reset our click listener + setOnClickListener(mLauncher); + } + mLauncher.getHotseat().getLayout().enableAccessibleDrag(enable); + } + public boolean hasCustomContent() { return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID); } @@ -2184,7 +2206,7 @@ public class Workspace extends SmoothPagedView private void updateAccessibilityFlags() { int accessible = mState == State.NORMAL ? - IMPORTANT_FOR_ACCESSIBILITY_YES : + IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; setImportantForAccessibility(accessible); } @@ -2674,7 +2696,11 @@ public class Workspace extends SmoothPagedView return b; } - void startDrag(CellLayout.CellInfo cellInfo) { + public void startDrag(CellLayout.CellInfo cellInfo) { + startDrag(cellInfo, false); + } + + public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) { View child = cellInfo.cell; // Make sure the drag was started by a long press as opposed to a long click. @@ -2687,10 +2713,14 @@ public class Workspace extends SmoothPagedView CellLayout layout = (CellLayout) child.getParent().getParent(); layout.prepareChildForDrag(child); - beginDragShared(child, this); + beginDragShared(child, this, accessible); } public void beginDragShared(View child, DragSource source) { + beginDragShared(child, source, false); + } + + public void beginDragShared(View child, DragSource source, boolean accessible) { child.clearFocus(); child.setPressed(false); @@ -2744,7 +2774,7 @@ public class Workspace extends SmoothPagedView } DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), - DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale); + DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); if (child.getParent() instanceof ShortcutAndWidgetContainer) { @@ -2794,7 +2824,7 @@ public class Workspace extends SmoothPagedView // Start the drag DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), - DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale); + DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, false); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); // Recycle temporary bitmaps @@ -3149,7 +3179,8 @@ public class Workspace extends SmoothPagedView final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo(); - if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { + if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE + && !d.accessibleDrag) { final Runnable addResizeFrame = new Runnable() { public void run() { DragLayer dragLayer = mLauncher.getDragLayer(); @@ -3638,7 +3669,7 @@ public class Workspace extends SmoothPagedView mTargetCell[1]); manageFolderFeedback(info, mDragTargetLayout, mTargetCell, - targetCellDistance, dragOverView); + targetCellDistance, dragOverView, d.accessibleDrag); boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX, @@ -3676,15 +3707,21 @@ public class Workspace extends SmoothPagedView } private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout, - int[] targetCell, float distance, View dragOverView) { + int[] targetCell, float distance, View dragOverView, boolean accessibleDrag) { boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance, false); - if (mDragMode == DRAG_MODE_NONE && userFolderPending && !mFolderCreationAlarm.alarmPending()) { - mFolderCreationAlarm.setOnAlarmListener(new - FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1])); - mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT); + + FolderCreationAlarmListener listener = new + FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]); + + if (!accessibleDrag) { + mFolderCreationAlarm.setOnAlarmListener(listener); + mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT); + } else { + listener.onAlarm(mFolderCreationAlarm); + } return; } -- cgit v1.2.3 From 290800b5b7d575fd709f244f54a5fa5b63b58876 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Mar 2015 11:33:33 -0800 Subject: Adding a scrollable folder content implementation > Size is restricted to 3x3 for now > Drag-drop across page s not implemented yet > A-Z sorting is not implemented yet Change-Id: I84328caa6ad910d1edeeac6f3a7fb61b7292ea7e --- .../ic_pageindicator_current_dark.png | Bin 0 -> 1168 bytes .../ic_pageindicator_default_dark.png | Bin 0 -> 927 bytes res/layout/user_folder.xml | 1 - res/layout/user_folder_scroll.xml | 78 +++ .../android/launcher3/AppsCustomizePagedView.java | 14 +- src/com/android/launcher3/FocusHelper.java | 45 +- src/com/android/launcher3/Folder.java | 39 +- src/com/android/launcher3/FolderCellLayout.java | 21 + src/com/android/launcher3/FolderPagedView.java | 581 +++++++++++++++++++++ 9 files changed, 742 insertions(+), 37 deletions(-) create mode 100644 res/drawable-xxhdpi/ic_pageindicator_current_dark.png create mode 100644 res/drawable-xxhdpi/ic_pageindicator_default_dark.png create mode 100644 res/layout/user_folder_scroll.xml create mode 100644 src/com/android/launcher3/FolderPagedView.java diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_dark.png b/res/drawable-xxhdpi/ic_pageindicator_current_dark.png new file mode 100644 index 000000000..d5c4c8d4e Binary files /dev/null and b/res/drawable-xxhdpi/ic_pageindicator_current_dark.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_dark.png b/res/drawable-xxhdpi/ic_pageindicator_default_dark.png new file mode 100644 index 000000000..79d307b07 Binary files /dev/null and b/res/drawable-xxhdpi/ic_pageindicator_default_dark.png differ diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index 74cdf11dc..7a4d5e881 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -46,7 +46,6 @@ android:id="@+id/folder_name" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_centerHorizontal="true" android:background="#00000000" android:fontFamily="sans-serif-condensed" android:gravity="center_horizontal" diff --git a/res/layout/user_folder_scroll.xml b/res/layout/user_folder_scroll.xml new file mode 100644 index 000000000..421e426cb --- /dev/null +++ b/res/layout/user_folder_scroll.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index c1aa19ae7..9f8d499eb 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -36,7 +36,6 @@ import android.os.Process; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -45,6 +44,7 @@ import android.widget.ImageView; import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.FocusHelper.PagedViewKeyListener; import com.android.launcher3.compat.AppWidgetManagerCompat; import java.util.ArrayList; @@ -139,7 +139,7 @@ class AppsCustomizeAsyncTask extends AsyncTask mRunningTasks; private static final int sPageSleepDelay = 200; + private final PagedViewKeyListener mKeyListener = new PagedViewKeyListener(); + private Runnable mInflateWidgetRunnable = null; private Runnable mBindWidgetRunnable = null; static final int WIDGET_NO_CLEANUP_REQUIRED = -1; @@ -449,10 +451,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mWidgetInstructionToast.show(); } - public boolean onKey(View v, int keyCode, KeyEvent event) { - return FocusHelper.handleAppsCustomizeKeyEvent(v, keyCode, event); - } - /* * PagedViewWithDraggableItems implementation */ @@ -959,7 +957,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen icon.setOnClickListener(mLauncher); icon.setOnLongClickListener(this); icon.setOnTouchListener(this); - icon.setOnKeyListener(this); + icon.setOnKeyListener(mKeyListener); icon.setOnFocusChangeListener(layout.mFocusHandlerView); int index = i - startIndex; @@ -1141,7 +1139,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen widget.setOnClickListener(this); widget.setOnLongClickListener(this); widget.setOnTouchListener(this); - widget.setOnKeyListener(this); + widget.setOnKeyListener(mKeyListener); // Layout each widget int ix = i % mWidgetCountX; diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 737f6cca7..b090a7c3f 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -23,6 +23,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.FocusHelper.PagedViewKeyListener; import com.android.launcher3.util.FocusLogic; /** @@ -64,10 +65,33 @@ public class FocusHelper { // Key code handling methods. // + /** + * A keyboard listener for scrollable folders + */ + public static class PagedFolderKeyEventListener extends PagedViewKeyListener { + + private final Folder mFolder; + + public PagedFolderKeyEventListener(Folder folder) { + mFolder = folder; + } + + @Override + public void handleNoopKey(int keyCode, View v) { + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + mFolder.mFolderName.requestFocus(); + playSoundEffect(keyCode, v); + } + } + } + /** * Handles key events in the all apps screen. */ - static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) { + public static class PagedViewKeyListener implements View.OnKeyListener { + + @Override + public boolean onKey(View v, int keyCode, KeyEvent e) { boolean consume = FocusLogic.shouldConsume(keyCode); if (e.getAction() == KeyEvent.ACTION_UP) { return consume; @@ -87,15 +111,14 @@ public class FocusHelper { parentLayout = (ViewGroup) itemContainer.getParent(); countX = ((CellLayout) parentLayout).getCountX(); countY = ((CellLayout) parentLayout).getCountY(); - } else if (v.getParent() instanceof ViewGroup) { - //TODO(hyunyoungs): figure out when this needs to be called. - itemContainer = parentLayout = (ViewGroup) v.getParent(); - countX = ((PagedViewGridLayout) parentLayout).getCellCountX(); - countY = ((PagedViewGridLayout) parentLayout).getCellCountY(); } else { - throw new IllegalStateException( - "Parent of the focused item inside all apps screen is not a supported type."); + if (LauncherAppState.isDogfoodBuild()) { + throw new IllegalStateException("Parent of the focused item is not supported."); + } else { + return false; + } } + final int iconIndex = itemContainer.indexOfChild(v); final PagedView container = (PagedView) parentLayout.getParent(); final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout)); @@ -109,6 +132,7 @@ public class FocusHelper { int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, iconIndex, pageIndex, pageCount); if (newIconIndex == FocusLogic.NOOP) { + handleNoopKey(keyCode, v); return consume; } switch (newIconIndex) { @@ -163,10 +187,15 @@ public class FocusHelper { if (child != null) { child.requestFocus(); playSoundEffect(keyCode, v); + } else { + handleNoopKey(keyCode, v); } return consume; } + public void handleNoopKey(int keyCode, View v) { } + } + /** * Handles key events in the workspace hot seat (bottom of the screen). *

Currently we don't special case for the phone UI in different orientations, even though diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 4414672cc..0a6557907 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -66,6 +66,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * results in CellLayout being measured as UNSPECIFIED, which it does not support. */ private static final int MIN_CONTENT_DIMEN = 5; + private static final boolean ALLOW_FOLDER_SCROLL = true; static final int STATE_NONE = -1; static final int STATE_SMALL = 0; @@ -100,8 +101,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private View mContentWrapper; FolderEditText mFolderName; - private View mBottomContent; - private int mBottomContentHeight; + private View mFooter; + private int mFooterHeight; // Cell ranks used for drag and drop private int mTargetRank, mPrevTargetRank, mEmptyCellRank; @@ -175,13 +176,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setInputType(mFolderName.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); - // We only have the folder name at the bottom for now - mBottomContent = mFolderName; - // We find out how tall the bottom content wants to be (it is set to wrap_content), so that + mFooter = ALLOW_FOLDER_SCROLL ? findViewById(R.id.folder_footer) : mFolderName; + // We find out how tall footer wants to be (it is set to wrap_content), so that // we can allocate the appropriate amount of space for it. int measureSpec = MeasureSpec.UNSPECIFIED; - mBottomContent.measure(measureSpec, measureSpec); - mBottomContentHeight = mBottomContent.getMeasuredHeight(); + mFooter.measure(measureSpec, measureSpec); + mFooterHeight = mFooter.getMeasuredHeight(); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -225,7 +225,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mEmptyCellRank = item.rank; mCurrentDragView = v; - mContent.removeView(mCurrentDragView); + mContent.removeItem(mCurrentDragView); mInfo.remove(mCurrentDragInfo); mDragInProgress = true; mItemAddedBackToSelfViaIcon = false; @@ -359,7 +359,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * @return A new UserFolder. */ static Folder fromXml(Context context) { - return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null); + return (Folder) LayoutInflater.from(context).inflate( + ALLOW_FOLDER_SCROLL ? R.layout.user_folder_scroll : R.layout.user_folder, null); } /** @@ -434,8 +435,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList iconsAlpha.setStartDelay(mMaterialExpandStagger); iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - mBottomContent.setAlpha(0f); - Animator textAlpha = LauncherAnimUtils.ofFloat(mBottomContent, "alpha", 0f, 1f); + mFooter.setAlpha(0f); + Animator textAlpha = LauncherAnimUtils.ofFloat(mFooter, "alpha", 0f, 1f); textAlpha.setDuration(mMaterialExpandDuration); textAlpha.setStartDelay(mMaterialExpandStagger); textAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); @@ -795,7 +796,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList CellLayout.LANDSCAPE : CellLayout.PORTRAIT); int maxContentAreaHeight = grid.availableHeightPx - workspacePadding.top - workspacePadding.bottom - - mBottomContentHeight; + mFooterHeight; int height = Math.min(maxContentAreaHeight, mContent.getDesiredHeight()); return Math.max(height, MIN_CONTENT_DIMEN); @@ -810,7 +811,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } private int getFolderHeight(int contentAreaHeight) { - return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mBottomContentHeight; + return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mFooterHeight; } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -822,10 +823,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setFixedSize(contentWidth, contentHeight); mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec); - - // Move the bottom content below mContent - mBottomContent.measure(contentAreaWidthSpec, - MeasureSpec.makeMeasureSpec(mBottomContentHeight, MeasureSpec.EXACTLY)); + mFooter.measure(contentAreaWidthSpec, + MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY)); int folderWidth = getPaddingLeft() + getPaddingRight() + contentWidth; int folderHeight = getFolderHeight(contentHeight); @@ -929,7 +928,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // This method keeps track of the last item in the folder for the purposes // of keyboard focus - private void updateTextViewFocus() { + public void updateTextViewFocus() { View lastChild = mContent.getLastItem(); if (lastChild != null) { mFolderName.setNextFocusDownId(lastChild.getId()); @@ -1028,7 +1027,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // the work associated with removing the item, so we don't have to do anything here. if (item == mCurrentDragInfo) return; View v = getViewForInfo(item); - mContent.removeView(v); + mContent.removeItem(v); if (mState == STATE_ANIMATING) { mRearrangeOnClose = true; } else { @@ -1090,7 +1089,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public static interface FolderContent { void setFolder(Folder f); - void removeView(View v); + void removeItem(View v); boolean isFull(); int getItemCount(); diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java index b354ec74e..1566912b4 100644 --- a/src/com/android/launcher3/FolderCellLayout.java +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -1,3 +1,19 @@ +/** + * 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.Context; @@ -138,6 +154,11 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent addViewToCellLayout(view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); } + @Override + public void removeItem(View v) { + removeView(v); + } + /** * Updates the item cellX and cellY position */ diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java new file mode 100644 index 000000000..60c94e0ed --- /dev/null +++ b/src/com/android/launcher3/FolderPagedView.java @@ -0,0 +1,581 @@ +/** + * 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.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; + +import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; +import com.android.launcher3.PageIndicator.PageMarkerResources; +import com.android.launcher3.Workspace.ItemOperator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +public class FolderPagedView extends PagedView implements Folder.FolderContent { + + private static final String TAG = "FolderPagedView"; + + private static final int REORDER_ANIMATION_DURATION = 230; + private static final int[] sTempPosArray = new int[2]; + + // TODO: Remove this restriction + private static final int MAX_ITEMS_PER_PAGE = 3; + + private final LayoutInflater mInflater; + private final IconCache mIconCache; + private final HashMap mPageChangingViews = new HashMap<>(); + + private final CellLayout mFirstPage; + + final int mMaxCountX; + final int mMaxCountY; + final int mMaxItemsPerPage; + + private int mAllocatedContentSize; + private int mGridCountX; + private int mGridCountY; + + private Folder mFolder; + private FocusIndicatorView mFocusIndicatorView; + private PagedFolderKeyEventListener mKeyListener; + + public FolderPagedView(Context context, AttributeSet attrs) { + super(context, attrs); + LauncherAppState app = LauncherAppState.getInstance(); + + mFirstPage = newCellLayout(); + addFullScreenPage(mFirstPage); + setCurrentPage(0); + setDataIsReady(); + + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE); + mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE); + mMaxItemsPerPage = mMaxCountX * mMaxCountY; + + mInflater = LayoutInflater.from(context); + mIconCache = app.getIconCache(); + } + + @Override + public void setFolder(Folder folder) { + mFolder = folder; + mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); + mKeyListener = new PagedFolderKeyEventListener(folder); + } + + /** + * Sets up the grid size such that {@param count} items can fit in the grid. + * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while + * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. + */ + private void setupContentDimensions(int count) { + mAllocatedContentSize = count; + boolean done; + if (count >= mMaxItemsPerPage) { + mGridCountX = mMaxCountX; + mGridCountY = mMaxCountY; + done = true; + } else { + mGridCountX = mFirstPage.getCountX(); + mGridCountY = mFirstPage.getCountY(); + done = false; + } + + while (!done) { + int oldCountX = mGridCountX; + int oldCountY = mGridCountY; + if (mGridCountX * mGridCountY < count) { + // Current grid is too small, expand it + if ((mGridCountX <= mGridCountY || mGridCountY == mMaxCountY) && mGridCountX < mMaxCountX) { + mGridCountX++; + } else if (mGridCountY < mMaxCountY) { + mGridCountY++; + } + if (mGridCountY == 0) mGridCountY++; + } else if ((mGridCountY - 1) * mGridCountX >= count && mGridCountY >= mGridCountX) { + mGridCountY = Math.max(0, mGridCountY - 1); + } else if ((mGridCountX - 1) * mGridCountY >= count) { + mGridCountX = Math.max(0, mGridCountX - 1); + } + done = mGridCountX == oldCountX && mGridCountY == oldCountY; + } + + setGridSize(mGridCountX, mGridCountY); + } + + public void setGridSize(int countX, int countY) { + mGridCountX = countX; + mGridCountY = countY; + mFirstPage.setGridSize(mGridCountX, mGridCountY); + for (int i = getPageCount() - 1; i > 0; i--) { + getPageAt(i).setGridSize(mGridCountX, mGridCountY); + } + } + + @Override + public ArrayList bindItems(ArrayList items) { + final int count = items.size(); + + if (getPageCount() > 1) { + Log.d(TAG, "Binding items to an non-empty view"); + removeAllViews(); + addView(mFirstPage); + mFirstPage.removeAllViews(); + } + + setupContentDimensions(count); + CellLayout page = mFirstPage; + int pagePosition = 0; + int rank = 0; + + for (ShortcutInfo item : items) { + if (pagePosition >= mMaxItemsPerPage) { + // This page is full, add a new page. + pagePosition = 0; + page = newCellLayout(); + addFullScreenPage(page); + } + + item.cellX = pagePosition % mGridCountX; + item.cellY = pagePosition / mGridCountX; + item.rank = rank; + addNewView(item, page); + + rank++; + pagePosition++; + } + return new ArrayList(); + } + + /** + * Create space for a new item at the end, and returns the rank for that item. + * Also sets the current page to the last page. + */ + @Override + public int allocateNewLastItemRank() { + int rank = getItemCount(); + int total = rank + 1; + if (rank < mMaxItemsPerPage) { + // Rearrange the items as the grid size might change. + mFolder.rearrangeChildren(total); + } else { + setupContentDimensions(total); + } + + // Add a new page if last page is full + if (getPageAt(getChildCount() - 1).getShortcutsAndWidgets().getChildCount() + >= mMaxItemsPerPage) { + addFullScreenPage(newCellLayout()); + } + setCurrentPage(getChildCount() - 1); + return rank; + } + + @Override + public View createAndAddViewForRank(ShortcutInfo item, int rank) { + int pageNo = updateItemXY(item, rank); + CellLayout page = getPageAt(pageNo); + return addNewView(item, page); + } + + @Override + public void addViewForRank(View view, ShortcutInfo item, int rank) { + int pageNo = updateItemXY(item, rank); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); + lp.cellX = item.cellX; + lp.cellY = item.cellY; + getPageAt(pageNo).addViewToCellLayout( + view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); + } + + /** + * Updates the item cellX and cellY position and return the page number for that item. + */ + private int updateItemXY(ShortcutInfo item, int rank) { + item.rank = rank; + + int pagePos = item.rank % mMaxItemsPerPage; + item.cellX = pagePos % mGridCountX; + item.cellY = pagePos / mGridCountX; + + return item.rank / mMaxItemsPerPage; + } + + private View addNewView(ShortcutInfo item, CellLayout target) { + final BubbleTextView textView = (BubbleTextView) mInflater.inflate( + R.layout.folder_application, target.getShortcutsAndWidgets(), false); + textView.applyFromShortcutInfo(item, mIconCache, false); + textView.setOnClickListener(mFolder); + textView.setOnLongClickListener(mFolder); + textView.setOnFocusChangeListener(mFocusIndicatorView); + textView.setOnKeyListener(mKeyListener); + + CellLayout.LayoutParams lp = new CellLayout.LayoutParams( + item.cellX, item.cellY, item.spanX, item.spanY); + target.addViewToCellLayout( + textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); + return textView; + } + + @Override + public CellLayout getPageAt(int index) { + return (CellLayout) getChildAt(index); + } + + public void removeCellLayoutView(View view) { + for (int i = getChildCount() - 1; i >= 0; i --) { + getPageAt(i).removeView(view); + } + } + + public CellLayout getCurrentCellLayout() { + return getPageAt(getNextPage()); + } + + @Override + public void addFullScreenPage(View page) { + LayoutParams lp = generateDefaultLayoutParams(); + lp.isFullScreenPage = true; + super.addView(page, -1, lp); + } + + private CellLayout newCellLayout() { + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + + CellLayout layout = new CellLayout(getContext()); + layout.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); + layout.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); + layout.setInvertIfRtl(true); + + if (mFirstPage != null) { + layout.setGridSize(mFirstPage.getCountX(), mFirstPage.getCountY()); + } + + return layout; + } + + @Override + public void setFixedSize(int width, int height) { + for (int i = getChildCount() - 1; i >= 0; i --) { + ((CellLayout) getChildAt(i)).setFixedSize(width, height); + } + } + + @Override + public void removeItem(View v) { + for (int i = getChildCount() - 1; i >= 0; i --) { + getPageAt(i).removeView(v); + } + } + + /** + * Updates position and rank of all the children in the view. + * It essentially removes all views from all the pages and then adds them again in appropriate + * page. + * + * @param list the ordered list of children. + * @param itemCount if greater than the total children count, empty spaces are left + * at the end, otherwise it is ignored. + * + */ + @Override + public void arrangeChildren(ArrayList list, int itemCount) { + ArrayList pages = new ArrayList(); + for (int i = 0; i < getChildCount(); i++) { + CellLayout page = (CellLayout) getChildAt(i); + page.removeAllViews(); + pages.add(page); + } + setupContentDimensions(itemCount); + + Iterator pageItr = pages.iterator(); + CellLayout currentPage = null; + + int position = 0; + int newX, newY, rank; + + rank = 0; + for (View v : list) { + if (currentPage == null || position >= mMaxItemsPerPage) { + // Next page + if (pageItr.hasNext()) { + currentPage = pageItr.next(); + } else { + currentPage = newCellLayout(); + addFullScreenPage(currentPage); + } + position = 0; + } + + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); + newX = position % mGridCountX; + newY = position / mGridCountX; + ItemInfo info = (ItemInfo) v.getTag(); + if (info.cellX != newX || info.cellY != newY || info.rank != rank) { + info.cellX = newX; + info.cellY = newY; + info.rank = rank; + LauncherModel.addOrMoveItemInDatabase(getContext(), info, + mFolder.mInfo.id, 0, info.cellX, info.cellY); + } + lp.cellX = info.cellX; + lp.cellY = info.cellY; + rank ++; + position++; + currentPage.addViewToCellLayout( + v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); + } + + boolean removed = false; + while (pageItr.hasNext()) { + CellLayout layout = pageItr.next(); + if (layout != mFirstPage) { + removeView(layout); + removed = true; + } + } + if (removed) { + setCurrentPage(0); + } + } + + @Override + protected void loadAssociatedPages(int page, boolean immediateAndOnly) { } + + @Override + public void syncPages() { } + + @Override + public void syncPageItems(int page, boolean immediate) { } + + public int getDesiredWidth() { + return mFirstPage.getDesiredWidth(); + } + + public int getDesiredHeight() { + return mFirstPage.getDesiredHeight(); + } + + @Override + public int getItemCount() { + int lastPage = getChildCount() - 1; + return getPageAt(lastPage).getShortcutsAndWidgets().getChildCount() + + lastPage * mMaxItemsPerPage; + } + + @Override + public int findNearestArea(int pixelX, int pixelY) { + int pageIndex = getNextPage(); + CellLayout page = getPageAt(pageIndex); + page.findNearestArea(pixelX, pixelY, 1, 1, null, false, sTempPosArray); + if (mFolder.isLayoutRtl()) { + sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1; + } + return Math.min(mAllocatedContentSize - 1, + pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]); + } + + @Override + protected PageMarkerResources getPageIndicatorMarker(int pageIndex) { + return new PageMarkerResources(R.drawable.ic_pageindicator_current_dark, R.drawable.ic_pageindicator_default_dark); + } + + @Override + public boolean isFull() { + return false; + } + + @Override + public View getLastItem() { + if (getChildCount() < 1) { + return null; + } + ShortcutAndWidgetContainer lastContainer = getCurrentCellLayout().getShortcutsAndWidgets(); + int lastRank = lastContainer.getChildCount() - 1; + if (mGridCountX > 0) { + return lastContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX); + } else { + return lastContainer.getChildAt(lastRank); + } + } + + @Override + public View iterateOverItems(ItemOperator op) { + for (int k = 0 ; k < getChildCount(); k++) { + CellLayout page = getPageAt(k); + for (int j = 0; j < page.getCountY(); j++) { + for (int i = 0; i < page.getCountX(); i++) { + View v = page.getChildAt(i, j); + if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) { + return v; + } + } + } + } + return null; + } + + @Override + public String getAccessibilityDescription() { + return String.format(getContext().getString(R.string.folder_opened), + mGridCountX, mGridCountY); + } + + @Override + public void setFocusOnFirstChild() { + View firstChild = getCurrentCellLayout().getChildAt(0, 0); + if (firstChild != null) { + firstChild.requestFocus(); + } + } + + @Override + protected void notifyPageSwitchListener() { + super.notifyPageSwitchListener(); + if (mFolder != null) { + mFolder.updateTextViewFocus(); + } + } + + @Override + public void realTimeReorder(int empty, int target) { + int delay = 0; + float delayAmount = 30; + + // Animation only happens on the current page. + int pageToAnimate = getNextPage(); + + int pageT = target / mMaxItemsPerPage; + int pagePosT = target % mMaxItemsPerPage; + + if (pageT != pageToAnimate) { + Log.e(TAG, "Cannot animate when the target cell is invisible"); + } + int pagePosE = empty % mMaxItemsPerPage; + int pageE = empty / mMaxItemsPerPage; + + int startPos, endPos; + int moveStart, moveEnd; + int direction; + + if (target == empty) { + // No animation + return; + } else if (target > empty) { + // Items will move backwards to make room for the empty cell. + direction = 1; + + // If empty cell is in a different page, move them instantly. + if (pageE < pageToAnimate) { + moveStart = empty; + // Instantly move the first item in the current page. + moveEnd = pageToAnimate * mMaxItemsPerPage; + // Animate the 2nd item in the current page, as the first item was already moved to + // the last page. + startPos = 0; + } else { + moveStart = moveEnd = -1; + startPos = pagePosE; + } + + endPos = pagePosT; + } else { + // The items will move forward. + direction = -1; + + if (pageE > pageToAnimate) { + // Move the items immediately. + moveStart = empty; + // Instantly move the last item in the current page. + moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1; + + // Animations start with the second last item in the page + startPos = mMaxItemsPerPage - 1; + } else { + moveStart = moveEnd = -1; + startPos = pagePosE; + } + + endPos = pagePosT; + } + + // Instant moving views. + while (moveStart != moveEnd) { + int rankToMove = moveStart + direction; + int p = rankToMove / mMaxItemsPerPage; + int pagePos = rankToMove % mMaxItemsPerPage; + int x = pagePos % mGridCountX; + int y = pagePos / mGridCountX; + + final CellLayout page = getPageAt(p); + final View v = page.getChildAt(x, y); + if (v != null) { + if (pageToAnimate != p) { + page.removeView(v); + addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart); + } else { + // Do a fake animation before removing it. + final int newRank = moveStart; + final float oldTranslateX = v.getTranslationX(); + + Runnable endAction = new Runnable() { + + @Override + public void run() { + mPageChangingViews.remove(v); + v.setTranslationX(oldTranslateX); + ((CellLayout) v.getParent().getParent()).removeView(v); + addViewForRank(v, (ShortcutInfo) v.getTag(), newRank); + } + }; + v.animate() + .translationXBy(direction > 0 ? -v.getWidth() : v.getWidth()) + .setDuration(REORDER_ANIMATION_DURATION) + .setStartDelay(0) + .withEndAction(endAction); + mPageChangingViews.put(v, endAction); + } + } + moveStart = rankToMove; + } + + if ((endPos - startPos) * direction <= 0) { + // No animation + return; + } + + CellLayout page = getPageAt(pageToAnimate); + for (int i = startPos; i != endPos; i += direction) { + int nextPos = i + direction; + View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX); + if (v != null) { + ((ItemInfo) v.getTag()).rank -= direction; + } + if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX, + REORDER_ANIMATION_DURATION, delay, true, true)) { + delay += delayAmount; + delayAmount *= 0.9; + } + } + } +} -- cgit v1.2.3 From e4766230ff8115c5897ebee74c145bcae822581e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Mar 2015 13:42:03 -0700 Subject: Refactoring some logic in FolderPagedView > Using a common paths for rearranging items, creating new icon and creating new page Change-Id: I85097c7e47b42b79162a8a553a971e44dbc7af3c --- src/com/android/launcher3/FolderPagedView.java | 149 ++++++++----------------- 1 file changed, 49 insertions(+), 100 deletions(-) diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 60c94e0ed..c9e825aa3 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.util.Log; @@ -44,11 +45,9 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { private final IconCache mIconCache; private final HashMap mPageChangingViews = new HashMap<>(); - private final CellLayout mFirstPage; - - final int mMaxCountX; - final int mMaxCountY; - final int mMaxItemsPerPage; + private final int mMaxCountX; + private final int mMaxCountY; + private final int mMaxItemsPerPage; private int mAllocatedContentSize; private int mGridCountX; @@ -61,10 +60,6 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); LauncherAppState app = LauncherAppState.getInstance(); - - mFirstPage = newCellLayout(); - addFullScreenPage(mFirstPage); - setCurrentPage(0); setDataIsReady(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -96,8 +91,6 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { mGridCountY = mMaxCountY; done = true; } else { - mGridCountX = mFirstPage.getCountX(); - mGridCountY = mFirstPage.getCountY(); done = false; } @@ -120,50 +113,19 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { done = mGridCountX == oldCountX && mGridCountY == oldCountY; } - setGridSize(mGridCountX, mGridCountY); - } - - public void setGridSize(int countX, int countY) { - mGridCountX = countX; - mGridCountY = countY; - mFirstPage.setGridSize(mGridCountX, mGridCountY); - for (int i = getPageCount() - 1; i > 0; i--) { + // Update grid size + for (int i = getPageCount() - 1; i >= 0; i--) { getPageAt(i).setGridSize(mGridCountX, mGridCountY); } } @Override public ArrayList bindItems(ArrayList items) { - final int count = items.size(); - - if (getPageCount() > 1) { - Log.d(TAG, "Binding items to an non-empty view"); - removeAllViews(); - addView(mFirstPage); - mFirstPage.removeAllViews(); - } - - setupContentDimensions(count); - CellLayout page = mFirstPage; - int pagePosition = 0; - int rank = 0; - + ArrayList icons = new ArrayList(); for (ShortcutInfo item : items) { - if (pagePosition >= mMaxItemsPerPage) { - // This page is full, add a new page. - pagePosition = 0; - page = newCellLayout(); - addFullScreenPage(page); - } - - item.cellX = pagePosition % mGridCountX; - item.cellY = pagePosition / mGridCountX; - item.rank = rank; - addNewView(item, page); - - rank++; - pagePosition++; + icons.add(createNewView(item)); } + arrangeChildren(icons, icons.size(), false); return new ArrayList(); } @@ -185,7 +147,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { // Add a new page if last page is full if (getPageAt(getChildCount() - 1).getShortcutsAndWidgets().getChildCount() >= mMaxItemsPerPage) { - addFullScreenPage(newCellLayout()); + createAndAddNewPage(); } setCurrentPage(getChildCount() - 1); return rank; @@ -193,47 +155,39 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { @Override public View createAndAddViewForRank(ShortcutInfo item, int rank) { - int pageNo = updateItemXY(item, rank); - CellLayout page = getPageAt(pageNo); - return addNewView(item, page); + View icon = createNewView(item); + addViewForRank(createNewView(item), item, rank); + return icon; } @Override public void addViewForRank(View view, ShortcutInfo item, int rank) { - int pageNo = updateItemXY(item, rank); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); - lp.cellX = item.cellX; - lp.cellY = item.cellY; - getPageAt(pageNo).addViewToCellLayout( - view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); - } + int pagePos = rank % mMaxItemsPerPage; + int pageNo = rank / mMaxItemsPerPage; - /** - * Updates the item cellX and cellY position and return the page number for that item. - */ - private int updateItemXY(ShortcutInfo item, int rank) { item.rank = rank; - - int pagePos = item.rank % mMaxItemsPerPage; item.cellX = pagePos % mGridCountX; item.cellY = pagePos / mGridCountX; - return item.rank / mMaxItemsPerPage; + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); + lp.cellX = item.cellX; + lp.cellY = item.cellY; + getPageAt(pageNo).addViewToCellLayout( + view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); } - private View addNewView(ShortcutInfo item, CellLayout target) { + @SuppressLint("InflateParams") + private View createNewView(ShortcutInfo item) { final BubbleTextView textView = (BubbleTextView) mInflater.inflate( - R.layout.folder_application, target.getShortcutsAndWidgets(), false); + R.layout.folder_application, null, false); textView.applyFromShortcutInfo(item, mIconCache, false); textView.setOnClickListener(mFolder); textView.setOnLongClickListener(mFolder); textView.setOnFocusChangeListener(mFocusIndicatorView); textView.setOnKeyListener(mKeyListener); - CellLayout.LayoutParams lp = new CellLayout.LayoutParams( - item.cellX, item.cellY, item.spanX, item.spanY); - target.addViewToCellLayout( - textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); + textView.setLayoutParams(new CellLayout.LayoutParams( + item.cellX, item.cellY, item.spanX, item.spanY)); return textView; } @@ -252,26 +206,18 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { return getPageAt(getNextPage()); } - @Override - public void addFullScreenPage(View page) { - LayoutParams lp = generateDefaultLayoutParams(); - lp.isFullScreenPage = true; - super.addView(page, -1, lp); - } - - private CellLayout newCellLayout() { + private CellLayout createAndAddNewPage() { DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + CellLayout page = new CellLayout(getContext()); + page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); + page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); + page.setInvertIfRtl(true); + page.setGridSize(mGridCountX, mGridCountY); - CellLayout layout = new CellLayout(getContext()); - layout.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); - layout.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); - layout.setInvertIfRtl(true); - - if (mFirstPage != null) { - layout.setGridSize(mFirstPage.getCountX(), mFirstPage.getCountY()); - } - - return layout; + LayoutParams lp = generateDefaultLayoutParams(); + lp.isFullScreenPage = true; + addView(page, -1, lp); + return page; } @Override @@ -300,6 +246,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { */ @Override public void arrangeChildren(ArrayList list, int itemCount) { + arrangeChildren(list, itemCount, true); + } + + private void arrangeChildren(ArrayList list, int itemCount, boolean saveChanges) { ArrayList pages = new ArrayList(); for (int i = 0; i < getChildCount(); i++) { CellLayout page = (CellLayout) getChildAt(i); @@ -321,8 +271,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { if (pageItr.hasNext()) { currentPage = pageItr.next(); } else { - currentPage = newCellLayout(); - addFullScreenPage(currentPage); + currentPage = createAndAddNewPage(); } position = 0; } @@ -335,8 +284,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { info.cellX = newX; info.cellY = newY; info.rank = rank; - LauncherModel.addOrMoveItemInDatabase(getContext(), info, - mFolder.mInfo.id, 0, info.cellX, info.cellY); + if (saveChanges) { + LauncherModel.addOrMoveItemInDatabase(getContext(), info, + mFolder.mInfo.id, 0, info.cellX, info.cellY); + } } lp.cellX = info.cellX; lp.cellY = info.cellY; @@ -346,13 +297,11 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); } + // Remove extra views. boolean removed = false; while (pageItr.hasNext()) { - CellLayout layout = pageItr.next(); - if (layout != mFirstPage) { - removeView(layout); - removed = true; - } + removeView(pageItr.next()); + removed = true; } if (removed) { setCurrentPage(0); @@ -369,11 +318,11 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { public void syncPageItems(int page, boolean immediate) { } public int getDesiredWidth() { - return mFirstPage.getDesiredWidth(); + return getPageCount() > 0 ? getPageAt(0).getDesiredWidth() : 0; } public int getDesiredHeight() { - return mFirstPage.getDesiredHeight(); + return getPageCount() > 0 ? getPageAt(0).getDesiredHeight() : 0; } @Override -- cgit v1.2.3 From b745afbdd75157c73d581b345118cdaff99e912d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 2 Mar 2015 11:51:23 -0800 Subject: Initial changes to break out AllApps into its own view. - Moves launcher state-transition code into its own class - Moves all-apps related code into a separate view/set of classes - Implements a basic list view for all apps Change-Id: I68f174aa9e1bf82c4e46ce9549c78a8dc4623f46 --- res/drawable/apps_list_bg.xml | 21 + res/layout-land/launcher.xml | 6 + res/layout-port/launcher.xml | 6 + res/layout-sw600dp/apps_view.xml | 34 + res/layout-sw720dp/launcher.xml | 6 + res/layout/apps_grid_row_icon_view.xml | 22 + res/layout/apps_grid_row_view.xml | 38 + res/layout/apps_list_reveal_view.xml | 25 + res/layout/apps_list_row_icon_view.xml | 28 + res/layout/apps_list_row_view.xml | 34 + res/layout/apps_list_view.xml | 30 + res/layout/apps_view.xml | 28 + res/values-sw600dp/dimens.xml | 3 + res/values/attrs.xml | 8 + res/values/dimens.xml | 3 + src/com/android/launcher3/AppsContainerView.java | 609 +++++++++++++++ .../android/launcher3/AppsCustomizePagedView.java | 219 +----- .../android/launcher3/AppsCustomizeTabHost.java | 44 +- src/com/android/launcher3/BubbleTextView.java | 101 ++- src/com/android/launcher3/DeviceProfile.java | 2 + src/com/android/launcher3/Folder.java | 4 +- src/com/android/launcher3/Launcher.java | 781 ++++--------------- src/com/android/launcher3/LauncherModel.java | 2 +- .../LauncherStateTransitionAnimation.java | 832 +++++++++++++++++++++ .../launcher3/PagedViewWithDraggableItems.java | 2 +- src/com/android/launcher3/WidgetPreviewLoader.java | 4 +- .../android/launcher3/WidgetsContainerView.java | 88 +++ src/com/android/launcher3/Workspace.java | 67 +- 28 files changed, 2112 insertions(+), 935 deletions(-) create mode 100644 res/drawable/apps_list_bg.xml create mode 100644 res/layout-sw600dp/apps_view.xml create mode 100644 res/layout/apps_grid_row_icon_view.xml create mode 100644 res/layout/apps_grid_row_view.xml create mode 100644 res/layout/apps_list_reveal_view.xml create mode 100644 res/layout/apps_list_row_icon_view.xml create mode 100644 res/layout/apps_list_row_view.xml create mode 100644 res/layout/apps_list_view.xml create mode 100644 res/layout/apps_view.xml create mode 100644 src/com/android/launcher3/AppsContainerView.java create mode 100644 src/com/android/launcher3/LauncherStateTransitionAnimation.java create mode 100644 src/com/android/launcher3/WidgetsContainerView.java diff --git a/res/drawable/apps_list_bg.xml b/res/drawable/apps_list_bg.xml new file mode 100644 index 000000000..61f1c083a --- /dev/null +++ b/res/drawable/apps_list_bg.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index 6f95bd506..b13984a26 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -62,6 +62,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> + + + + + + + + + \ No newline at end of file diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml index 960ccf330..a3d502cf4 100644 --- a/res/layout-sw720dp/launcher.xml +++ b/res/layout-sw720dp/launcher.xml @@ -71,6 +71,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> + + + + + diff --git a/res/layout/apps_grid_row_view.xml b/res/layout/apps_grid_row_view.xml new file mode 100644 index 000000000..bce43bc1b --- /dev/null +++ b/res/layout/apps_grid_row_view.xml @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/res/layout/apps_list_reveal_view.xml b/res/layout/apps_list_reveal_view.xml new file mode 100644 index 000000000..4a26787c8 --- /dev/null +++ b/res/layout/apps_list_reveal_view.xml @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/res/layout/apps_list_row_icon_view.xml b/res/layout/apps_list_row_icon_view.xml new file mode 100644 index 000000000..607af9b0b --- /dev/null +++ b/res/layout/apps_list_row_icon_view.xml @@ -0,0 +1,28 @@ + + + + diff --git a/res/layout/apps_list_row_view.xml b/res/layout/apps_list_row_view.xml new file mode 100644 index 000000000..c4dcd0018 --- /dev/null +++ b/res/layout/apps_list_row_view.xml @@ -0,0 +1,34 @@ + + + + + \ No newline at end of file diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml new file mode 100644 index 000000000..b1b0f310b --- /dev/null +++ b/res/layout/apps_list_view.xml @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml new file mode 100644 index 000000000..19ad3d2c9 --- /dev/null +++ b/res/layout/apps_view.xml @@ -0,0 +1,28 @@ + + + + + + \ No newline at end of file diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 28679be2e..f7ad0c4cd 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -17,6 +17,9 @@ 64dp + + 76dp + 60dp 8dp diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 3331cdec4..4e7c59280 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -18,6 +18,14 @@ + + + + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index d6fc508d1..013bd925b 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -46,6 +46,9 @@ 4dip 12dip + + 64dp + diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java new file mode 100644 index 000000000..cabacec3c --- /dev/null +++ b/src/com/android/launcher3/AppsContainerView.java @@ -0,0 +1,609 @@ +package com.android.launcher3; + +import android.content.ComponentName; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.SectionIndexer; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +/** + * Represents a row in the apps list view. + */ +class AppsRow { + int sectionId; + String sectionDescription; + List apps; + + public AppsRow(int sId, String sc, List ai) { + sectionId = sId; + sectionDescription = sc; + apps = ai; + } + + public AppsRow(int sId, List ai) { + sectionId = sId; + apps = ai; + } +} + +/** + * An interface to an algorithm that generates app rows. + */ +interface AppRowAlgorithm { + public List computeAppRows(List sortedApps, int appsPerRow); + public int getIconViewLayoutId(); + public int getRowViewLayoutId(); + public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info); +} + +/** + * Computes the rows in the apps list view. + */ +class SectionedAppsAlgorithm implements AppRowAlgorithm { + + @Override + public List computeAppRows(List sortedApps, int appsPerRow) { + List rows = new ArrayList<>(); + LinkedHashMap> sections = computeSectionedApps(sortedApps); + int sectionId = 0; + for (Map.Entry> sectionEntry : sections.entrySet()) { + String section = sectionEntry.getKey(); + List apps = sectionEntry.getValue(); + int numRows = (int) Math.ceil((float) apps.size() / appsPerRow); + for (int i = 0; i < numRows; i++) { + List appsInRow = new ArrayList<>(); + int offset = i * appsPerRow; + for (int j = 0; j < appsPerRow; j++) { + if (offset + j < apps.size()) { + appsInRow.add(apps.get(offset + j)); + } + } + if (i == 0) { + rows.add(new AppsRow(sectionId, section, appsInRow)); + } else { + rows.add(new AppsRow(sectionId, appsInRow)); + } + } + sectionId++; + } + return rows; + } + + @Override + public int getIconViewLayoutId() { + return R.layout.apps_grid_row_icon_view; + } + + @Override + public int getRowViewLayoutId() { + return R.layout.apps_grid_row_view; + } + + private LinkedHashMap> computeSectionedApps(List sortedApps) { + LinkedHashMap> sections = new LinkedHashMap<>(); + for (AppInfo info : sortedApps) { + String section = getSection(info); + List sectionApps = sections.get(section); + if (sectionApps == null) { + sectionApps = new ArrayList<>(); + sections.put(section, sectionApps); + } + sectionApps.add(info); + } + return sections; + } + + @Override + public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) { + icon.applyFromApplicationInfo(info); + } + + private String getSection(AppInfo app) { + return app.title.toString().substring(0, 1).toLowerCase(); + } +} + +/** + * Computes the rows in the apps grid view. + */ +class ListedAppsAlgorithm implements AppRowAlgorithm { + + @Override + public List computeAppRows(List sortedApps, int appsPerRow) { + List rows = new ArrayList<>(); + int sectionId = -1; + String prevSection = ""; + for (AppInfo info : sortedApps) { + List appsInRow = new ArrayList<>(); + appsInRow.add(info); + String section = getSection(info); + if (!prevSection.equals(section)) { + prevSection = section; + sectionId++; + rows.add(new AppsRow(sectionId, section, appsInRow)); + } else { + rows.add(new AppsRow(sectionId, appsInRow)); + } + } + return rows; + } + + @Override + public int getIconViewLayoutId() { + return R.layout.apps_list_row_icon_view; + } + + @Override + public int getRowViewLayoutId() { + return R.layout.apps_list_row_view; + } + + @Override + public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) { + icon.applyFromApplicationInfo(info); + } + + private String getSection(AppInfo app) { + return app.title.toString().substring(0, 1).toLowerCase(); + } +} + +/** + * The adapter of all the apps + */ +class AppsListAdapter extends BaseAdapter implements SectionIndexer { + + private LayoutInflater mLayoutInflater; + private List mAppRows = new ArrayList<>(); + private View.OnTouchListener mTouchListener; + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + private AppRowAlgorithm mRowAlgorithm; + private int mAppsPerRow; + + public AppsListAdapter(Context context, View.OnTouchListener touchListener, + View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) { + mLayoutInflater = LayoutInflater.from(context); + mTouchListener = touchListener; + mIconClickListener = iconClickListener; + mIconLongClickListener = iconLongClickListener; + } + + void setApps(List apps, int appsPerRow, AppRowAlgorithm algo) { + mAppsPerRow = appsPerRow; + mRowAlgorithm = algo; + mAppRows.clear(); + mAppRows.addAll(apps); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mAppRows.size(); + } + + @Override + public Object getItem(int position) { + return mAppRows.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + AppsRow info = mAppRows.get(position); + ViewGroup row = (ViewGroup) convertView; + if (row == null) { + // Inflate the row and all the icon children necessary + row = (ViewGroup) mLayoutInflater.inflate(mRowAlgorithm.getRowViewLayoutId(), + parent, false); + for (int i = 0; i < mAppsPerRow; i++) { + BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( + mRowAlgorithm.getIconViewLayoutId(), row, false); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, 1); + lp.gravity = Gravity.CENTER_VERTICAL; + icon.setLayoutParams(lp); + icon.setOnTouchListener(mTouchListener); + icon.setOnClickListener(mIconClickListener); + icon.setOnLongClickListener(mIconLongClickListener); + icon.setFocusable(true); + row.addView(icon); + } + } + // Bind the section header + TextView tv = (TextView) row.findViewById(R.id.section); + if (info.sectionDescription != null) { + tv.setText(info.sectionDescription); + tv.setVisibility(View.VISIBLE); + } else { + tv.setVisibility(View.INVISIBLE); + } + // Bind the icons + for (int i = 0; i < mAppsPerRow; i++) { + BubbleTextView icon = (BubbleTextView) row.getChildAt(i + 1); + if (i < info.apps.size()) { + mRowAlgorithm.bindRowViewIconToInfo(icon, info.apps.get(i)); + icon.setVisibility(View.VISIBLE); + } else { + icon.setVisibility(View.INVISIBLE); + } + } + return row; + } + + @Override + public Object[] getSections() { + ArrayList sections = new ArrayList<>(); + int prevSectionId = -1; + for (AppsRow row : mAppRows) { + if (row.sectionId != prevSectionId) { + sections.add(row.sectionDescription.toUpperCase()); + prevSectionId = row.sectionId; + } + } + return sections.toArray(); + } + + @Override + public int getPositionForSection(int sectionIndex) { + for (int i = 0; i < mAppRows.size(); i++) { + AppsRow row = mAppRows.get(i); + if (row.sectionId == sectionIndex) { + return i; + } + } + return 0; + } + + @Override + public int getSectionForPosition(int position) { + return mAppRows.get(position).sectionId; + } +} + +/** + * The alphabetically sorted list of applications. + */ +class AlphabeticalAppList { + + /** + * Callbacks for when this list is modified. + */ + public interface Callbacks { + public void onAppsUpdated(); + } + + private List mApps; + private Callbacks mCb; + + public AlphabeticalAppList(Callbacks cb) { + mCb = cb; + } + + /** + * Returns the list of applications. + */ + public List getApps() { + return mApps; + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + Collections.sort(apps, LauncherModel.getAppNameComparator()); + mApps = apps; + mCb.onAppsUpdated(); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + // We add it in place, in alphabetical order + Comparator appNameComparator = LauncherModel.getAppNameComparator(); + for (AppInfo info : apps) { + // This call will return the exact index of where the item is if >= 0, or the index + // where it should be inserted if < 0. + int index = Collections.binarySearch(mApps, info, appNameComparator); + if (index < 0) { + mApps.add(-(index + 1), info); + } + } + mCb.onAppsUpdated(); + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + Comparator appNameComparator = LauncherModel.getAppNameComparator(); + for (AppInfo info : apps) { + int index = mApps.indexOf(info); + if (index != -1) { + mApps.set(index, info); + } else { + index = Collections.binarySearch(mApps, info, appNameComparator); + if (index < 0) { + mApps.add(-(index + 1), info); + } + } + } + mCb.onAppsUpdated(); + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + for (AppInfo info : apps) { + int removeIndex = findAppByComponent(mApps, info); + if (removeIndex != -1) { + mApps.remove(removeIndex); + } + } + mCb.onAppsUpdated(); + } + + /** + * Finds the index of an app given a target AppInfo. + */ + private int findAppByComponent(List apps, AppInfo targetInfo) { + ComponentName targetComponent = targetInfo.intent.getComponent(); + int length = apps.size(); + for (int i = 0; i < length; ++i) { + AppInfo info = apps.get(i); + if (info.user.equals(info.user) + && info.intent.getComponent().equals(targetComponent)) { + return i; + } + } + return -1; + } + +} + +/** + * The all apps list view container. + */ +public class AppsContainerView extends FrameLayout implements DragSource, View.OnTouchListener, + View.OnLongClickListener, Insettable, AlphabeticalAppList.Callbacks { + + static final int GRID_LAYOUT = 0; + static final int LIST_LAYOUT = 1; + static final int USE_LAYOUT = LIST_LAYOUT; + + private Launcher mLauncher; + private AppRowAlgorithm mAppRowsAlgorithm; + private AppsListAdapter mAdapter; + private AlphabeticalAppList mApps; + private ListView mList; + private int mAppsRowSize; + private Point mLastTouchDownPos = new Point(); + private Rect mPadding = new Rect(); + + public AppsContainerView(Context context) { + this(context, null); + } + + public AppsContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + + mLauncher = (Launcher) context; + if (USE_LAYOUT == GRID_LAYOUT) { + mAppRowsAlgorithm = new SectionedAppsAlgorithm(); + mAppsRowSize = grid.allAppsRowsSize; + } else if (USE_LAYOUT == LIST_LAYOUT) { + mAppRowsAlgorithm = new ListedAppsAlgorithm(); + mAppsRowSize = 1; + } + mAdapter = new AppsListAdapter(context, this, mLauncher, this); + mApps = new AlphabeticalAppList(this); + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + mApps.setApps(apps); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + mApps.addApps(apps); + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + mApps.updateApps(apps); + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + mApps.removeApps(apps); + } + + /** + * Scrolls this list view to the top. + */ + public void scrollToTop() { + mList.scrollTo(0, 0); + } + + /** + * Returns the content view used for the launcher transitions. + */ + public View getContentView() { + return findViewById(R.id.apps_list); + } + + /** + * Returns the reveal view used for the launcher transitions. + */ + public View getRevealView() { + return findViewById(R.id.all_apps_transition_overlay); + } + + @Override + public void onAppsUpdated() { + List rows = mAppRowsAlgorithm.computeAppRows(mApps.getApps(), mAppsRowSize); + mAdapter.setApps(rows, mAppsRowSize, mAppRowsAlgorithm); + } + + @Override + protected void onFinishInflate() { + mList = (ListView) findViewById(R.id.apps_list); + mList.setFastScrollEnabled(true); + mList.setFastScrollAlwaysVisible(true); + mList.setItemsCanFocus(true); + mList.setAdapter(mAdapter); + mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); + } + + @Override + public void setInsets(Rect insets) { + setPadding(mPadding.left + insets.left, mPadding.top + insets.top, + mPadding.right + insets.right, mPadding.bottom + insets.bottom); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN || + event.getAction() == MotionEvent.ACTION_MOVE) { + mLastTouchDownPos.set((int) event.getX(), (int) event.getY()); + } + return false; + } + + @Override + public boolean onLongClick(View v) { + // Return early if this is not initiated from a touch + if (!v.isInTouchMode()) return false; + // When we have exited all apps or are in transition, disregard long clicks + if (!mLauncher.isAppsViewVisible() || + mLauncher.getWorkspace().isSwitchingState()) return false; + // Return if global dragging is not enabled + if (!mLauncher.isDraggingEnabled()) return false; + + // Start the drag + mLauncher.getWorkspace().beginDragShared(v, mLastTouchDownPos, this, false); + + // We delay entering spring-loaded mode slightly to make sure the UI + // thready is free of any work. + postDelayed(new Runnable() { + @Override + public void run() { + // We don't enter spring-loaded mode if the drag has been cancelled + if (mLauncher.getDragController().isDragging()) { + // Go into spring loaded mode (must happen before we startDrag()) + mLauncher.enterSpringLoadedDragMode(); + } + } + }, 150); + + return false; + } + + @Override + public boolean supportsFlingToDelete() { + return true; + } + + @Override + public boolean supportsAppInfoDropTarget() { + return true; + } + + @Override + public boolean supportsDeleteDropTarget() { + return true; + } + + @Override + public float getIntrinsicIconScaleFactor() { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + return (float) grid.allAppsIconSizePx / grid.iconSizePx; + } + + @Override + public void onFlingToDeleteCompleted() { + // We just dismiss the drag when we fling, so cleanup here + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + mLauncher.unlockScreenOrientation(false); + } + + @Override + public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { + if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && + !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { + // Exit spring loaded mode if we have not successfully dropped or have not handled the + // drop in Workspace + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + mLauncher.unlockScreenOrientation(false); + + // Display an error message if the drag failed due to there not being enough space on the + // target layout we were dropping on. + if (!success) { + boolean showOutOfSpaceMessage = false; + if (target instanceof Workspace) { + int currentScreen = mLauncher.getCurrentWorkspaceScreen(); + Workspace workspace = (Workspace) target; + CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); + ItemInfo itemInfo = (ItemInfo) d.dragInfo; + if (layout != null) { + layout.calculateSpans(itemInfo); + showOutOfSpaceMessage = + !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); + } + } + if (showOutOfSpaceMessage) { + mLauncher.showOutOfSpaceMessage(false); + } + + d.deferDragViewCleanupPostAnimation = false; + } + } +} diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 9f8d499eb..bf368125f 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -149,10 +149,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen * The different content types that this paged view can show. */ public enum ContentType { - Applications, Widgets } - private ContentType mContentType = ContentType.Applications; + private ContentType mContentType = ContentType.Widgets; // Refs private Launcher mLauncher; @@ -164,7 +163,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private int mSaveInstanceStateItemIndex = -1; // Content - private ArrayList mApps; private ArrayList mWidgets; // Caching @@ -174,9 +172,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private int mContentWidth, mContentHeight; private int mWidgetCountX, mWidgetCountY; private PagedViewCellLayout mWidgetSpacingLayout; - private int mNumAppsPages; private int mNumWidgetPages; - private Rect mAllAppsPadding = new Rect(); // Previews & outlines ArrayList mRunningTasks; @@ -214,10 +210,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen super(context, attrs); mLayoutInflater = LayoutInflater.from(context); mPackageManager = context.getPackageManager(); - mApps = new ArrayList(); - mWidgets = new ArrayList(); + mWidgets = new ArrayList<>(); mIconCache = (LauncherAppState.getInstance()).getIconCache(); - mRunningTasks = new ArrayList(); + mRunningTasks = new ArrayList<>(); // Save the default widget preview background TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); @@ -256,10 +251,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen grid.edgeMarginPx, 2 * grid.edgeMarginPx); } - void setAllAppsPadding(Rect r) { - mAllAppsPadding.set(r); - } - void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) { setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight); } @@ -277,22 +268,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int i = -1; if (getPageCount() > 0) { int currentPage = getCurrentPage(); - if (mContentType == ContentType.Applications) { - AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(currentPage); - ShortcutAndWidgetContainer childrenLayout = layout.getShortcutsAndWidgets(); - int numItemsPerPage = mCellCountX * mCellCountY; - int childCount = childrenLayout.getChildCount(); - if (childCount > 0) { - i = (currentPage * numItemsPerPage) + (childCount / 2); - } - } else if (mContentType == ContentType.Widgets) { - int numApps = mApps.size(); + if (mContentType == ContentType.Widgets) { PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage); int numItemsPerPage = mWidgetCountX * mWidgetCountY; int childCount = layout.getChildCount(); if (childCount > 0) { - i = numApps + - (currentPage * numItemsPerPage) + (childCount / 2); + i = (currentPage * numItemsPerPage) + (childCount / 2); } } else { throw new RuntimeException("Invalid ContentType"); @@ -314,13 +295,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int getPageForComponent(int index) { if (index < 0) return 0; - if (index < mApps.size()) { - int numItemsPerPage = mCellCountX * mCellCountY; - return (index / numItemsPerPage); - } else { - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - return (index - mApps.size()) / numItemsPerPage; - } + int numItemsPerPage = mWidgetCountX * mWidgetCountY; + return index / numItemsPerPage; } /** Restores the page for an item at the specified index */ @@ -332,16 +308,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private void updatePageCounts() { mNumWidgetPages = (int) Math.ceil(mWidgets.size() / (float) (mWidgetCountX * mWidgetCountY)); - mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); } protected void onDataReady(int width, int height) { - // Now that the data is ready, we can calculate the content width, the number of cells to - // use for each page - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mCellCountX = (int) grid.allAppsNumCols; - mCellCountY = (int) grid.allAppsNumRows; updatePageCounts(); // Force a measure to update recalculate the gaps @@ -360,7 +329,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen super.onLayout(changed, l, t, r, b); if (!isDataReady()) { - if ((!mApps.isEmpty()) && !mWidgets.isEmpty()) { + if (!mWidgets.isEmpty()) { post(new Runnable() { // This code triggers requestLayout so must be posted outside of the // layout pass. @@ -438,7 +407,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen @Override public void onClick(View v) { // When we have exited all apps or are in transition, disregard clicks - if (!mLauncher.isAllAppsVisible() + if (!mLauncher.isWidgetsViewVisible() || mLauncher.getWorkspace().isSwitchingState() || !(v instanceof PagedViewWidget)) return; @@ -456,11 +425,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen */ @Override protected void determineDraggingStart(android.view.MotionEvent ev) { - // Disable dragging by pulling an app down for now. - } - - private void beginDraggingApplication(View v) { - mLauncher.getWorkspace().beginDragShared(v, this); } static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { @@ -681,12 +645,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen protected boolean beginDragging(final View v) { if (!super.beginDragging(v)) return false; - if (v instanceof BubbleTextView) { - beginDraggingApplication(v); - } else if (v instanceof PagedViewWidget) { + if (v instanceof PagedViewWidget) { if (!beginDraggingWidget(v)) { return false; } + } else { + Log.e(TAG, "Unexpected dragging view: " + v); } // We delay entering spring-loaded mode slightly to make sure the UI @@ -852,7 +816,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Clean up all the async tasks Iterator iter = mRunningTasks.iterator(); while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); task.cancel(false); iter.remove(); mDirtyPageContent.set(task.page, true); @@ -886,7 +850,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Update the thread priorities given the direction lookahead Iterator iter = mRunningTasks.iterator(); while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); int pageIndex = task.page; if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) || (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) { @@ -897,36 +861,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - /* - * Apps PagedView implementation - */ - private void setVisibilityOnChildren(ViewGroup layout, int visibility) { - int childCount = layout.getChildCount(); - for (int i = 0; i < childCount; ++i) { - layout.getChildAt(i).setVisibility(visibility); - } - } - private void setupPage(AppsCustomizeCellLayout layout) { - layout.setGridSize(mCellCountX, mCellCountY); - - // Note: We force a measure here to get around the fact that when we do layout calculations - // immediately after syncing, we don't have a proper width. That said, we already know the - // expected page width, so we can actually optimize by hiding all the TextView-based - // children that are expensive to measure, and let that happen naturally later. - setVisibilityOnChildren(layout, View.GONE); - int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); - int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); - layout.measure(widthSpec, heightSpec); - - Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel); - if (bg != null) { - bg.setAlpha(mPageBackgroundsVisible ? 255: 0); - layout.setBackground(bg); - } - - setVisibilityOnChildren(layout, View.VISIBLE); - } - public void setPageBackgroundsVisible(boolean visible) { mPageBackgroundsVisible = visible; int childCount = getChildCount(); @@ -938,43 +872,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - public void syncAppsPageItems(int page, boolean immediate) { - // ensure that we have the right number of items on the pages - final boolean isRtl = isLayoutRtl(); - int numCells = mCellCountX * mCellCountY; - int startIndex = page * numCells; - int endIndex = Math.min(startIndex + numCells, mApps.size()); - AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page); - - layout.removeAllViewsOnPage(); - ArrayList items = new ArrayList(); - ArrayList images = new ArrayList(); - for (int i = startIndex; i < endIndex; ++i) { - AppInfo info = mApps.get(i); - BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - R.layout.apps_customize_application, layout, false); - icon.applyFromApplicationInfo(info); - icon.setOnClickListener(mLauncher); - icon.setOnLongClickListener(this); - icon.setOnTouchListener(this); - icon.setOnKeyListener(mKeyListener); - icon.setOnFocusChangeListener(layout.mFocusHandlerView); - - int index = i - startIndex; - int x = index % mCellCountX; - int y = index / mCellCountX; - if (isRtl) { - x = mCellCountX - x - 1; - } - layout.addViewToCellLayout(icon, -1, i, new CellLayout.LayoutParams(x,y, 1,1), false); - - items.add(info); - images.add(info.iconBitmap); - } - - enableHwLayersOnVisiblePages(); - } - /** * A helper to return the priority for loading of the specified widget page. */ @@ -991,7 +888,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen Iterator iter = mRunningTasks.iterator(); int minPageDiff = Integer.MAX_VALUE; while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); minPageDiff = Math.abs(task.page - toPage); } @@ -1026,7 +923,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Prune all tasks that are no longer needed Iterator iter = mRunningTasks.iterator(); while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); int taskPage = task.page; if (taskPage < getAssociatedLowerPageBound(mCurrentPage) || taskPage > getAssociatedUpperPageBound(mCurrentPage)) { @@ -1264,14 +1161,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen cancelAllTasks(); Context context = getContext(); - if (mContentType == ContentType.Applications) { - for (int i = 0; i < mNumAppsPages; ++i) { - AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(context); - setupPage(layout); - addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT)); - } - } else if (mContentType == ContentType.Widgets) { + if (mContentType == ContentType.Widgets) { for (int j = 0; j < mNumWidgetPages; ++j) { PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, mWidgetCountY); @@ -1291,7 +1181,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen if (mContentType == ContentType.Widgets) { syncWidgetPageItems(page, immediate); } else { - syncAppsPageItems(page, immediate); + Log.e(TAG, "Unexpected ContentType"); } } @@ -1383,7 +1273,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } /** - * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can + * We should call thise method whenever the core data changes (mWidgets) so that we can * appropriately determine when to invalidate the PagedView page data. In cases where the data * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the * next onMeasure() pass, which will trigger an invalidatePageData() itself. @@ -1399,73 +1289,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - public void setApps(ArrayList list) { - mApps = list; - Collections.sort(mApps, LauncherModel.getAppNameComparator()); - updatePageCountsAndInvalidateData(); - } - - public ArrayList getApps() { - return mApps; - } - - private void addAppsWithoutInvalidate(ArrayList list) { - // We add it in place, in alphabetical order - int count = list.size(); - for (int i = 0; i < count; ++i) { - AppInfo info = list.get(i); - int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator()); - if (index < 0) { - mApps.add(-(index + 1), info); - } - } - } - public void addApps(ArrayList list) { - addAppsWithoutInvalidate(list); - updatePageCountsAndInvalidateData(); - } - private int findAppByComponent(List list, AppInfo item) { - ComponentName removeComponent = item.intent.getComponent(); - int length = list.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = list.get(i); - if (info.user.equals(item.user) - && info.intent.getComponent().equals(removeComponent)) { - return i; - } - } - return -1; - } - private void removeAppsWithoutInvalidate(ArrayList list) { - // loop through all the apps and remove apps that have the same component - int length = list.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = list.get(i); - int removeIndex = findAppByComponent(mApps, info); - if (removeIndex > -1) { - mApps.remove(removeIndex); - } - } - } - public void removeApps(ArrayList appInfos) { - removeAppsWithoutInvalidate(appInfos); - updatePageCountsAndInvalidateData(); - } - public void updateApps(ArrayList list) { - // We remove and re-add the updated applications list because it's properties may have - // changed (ie. the title), and this will ensure that the items will be in their proper - // place in the list. - removeAppsWithoutInvalidate(list); - addAppsWithoutInvalidate(list); - updatePageCountsAndInvalidateData(); - } - public void reset() { // If we have reset, then we should not continue to restore the previous state mSaveInstanceStateItemIndex = -1; - if (mContentType != ContentType.Applications) { - setContentType(ContentType.Applications); + if (mContentType != ContentType.Widgets) { + setContentType(ContentType.Widgets); } if (mCurrentPage != 0) { @@ -1479,7 +1308,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen public void dumpState() { // TODO: Dump information related to current list of Applications, Widgets, etc. - AppInfo.dumpApplicationInfoList(TAG, "mApps", mApps); dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets); } @@ -1534,10 +1362,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int stringId = R.string.default_scroll_format; int count = 0; - if (mContentType == ContentType.Applications) { - stringId = R.string.apps_customize_apps_scroll_format; - count = mNumAppsPages; - } else if (mContentType == ContentType.Widgets) { + if (mContentType == ContentType.Widgets) { stringId = R.string.apps_customize_widgets_scroll_format; count = mNumWidgetPages; } else { diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java index a2717126d..5e2f05c61 100644 --- a/src/com/android/launcher3/AppsCustomizeTabHost.java +++ b/src/com/android/launcher3/AppsCustomizeTabHost.java @@ -27,7 +27,6 @@ import android.widget.FrameLayout; public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable { static final String LOG_TAG = "AppsCustomizeTabHost"; - private static final String APPS_TAB_TAG = "APPS"; private static final String WIDGETS_TAB_TAG = "WIDGETS"; private AppsCustomizePagedView mPagedView; @@ -50,10 +49,6 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit mPagedView.setContentType(type); } - public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) { - setContentTypeImmediate(type); - } - @Override public void setInsets(Rect insets) { mInsets.set(insets); @@ -78,28 +73,39 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit return getTabTagForContentType(mPagedView.getContentType()); } + /** + * Returns the content view used for the launcher transitions. + */ + public View getContentView() { + return findViewById(R.id.apps_customize_pane_content); + } + + /** + * Returns the reveal view used for the launcher transitions. + */ + public View getRevealView() { + return findViewById(R.id.fake_page); + } + + /** + * Returns the page indicators view. + */ + public View getPageIndicators() { + return findViewById(R.id.apps_customize_page_indicator); + } + /** * Returns the content type for the specified tab tag. */ public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) { - if (tag.equals(APPS_TAB_TAG)) { - return AppsCustomizePagedView.ContentType.Applications; - } else if (tag.equals(WIDGETS_TAB_TAG)) { - return AppsCustomizePagedView.ContentType.Widgets; - } - return AppsCustomizePagedView.ContentType.Applications; + return AppsCustomizePagedView.ContentType.Widgets; } /** * Returns the tab tag for a given content type. */ public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) { - if (type == AppsCustomizePagedView.ContentType.Applications) { - return APPS_TAB_TAG; - } else if (type == AppsCustomizePagedView.ContentType.Widgets) { - return WIDGETS_TAB_TAG; - } - return APPS_TAB_TAG; + return WIDGETS_TAB_TAG; } /** @@ -199,6 +205,7 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit ViewGroup parent = (ViewGroup) getParent(); if (parent == null) return; + View appsView = ((Launcher) getContext()).getAppsView(); View overviewPanel = ((Launcher) getContext()).getOverviewPanel(); final int count = parent.getChildCount(); if (!isChildrenDrawingOrderEnabled()) { @@ -207,7 +214,8 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit if (child == this) { break; } else { - if (child.getVisibility() == GONE || child == overviewPanel) { + if (child.getVisibility() == GONE || child == overviewPanel || + child == appsView) { continue; } child.setVisibility(visibility); diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f9255e6bd..5ea84aeb2 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.SparseArray; import android.util.TypedValue; +import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -49,7 +50,7 @@ public class BubbleTextView extends TextView { private static final int SHADOW_SMALL_COLOUR = 0xCC000000; static final float PADDING_V = 3.0f; - + private Drawable mIcon; private final Drawable mBackground; private final CheckLongPressHelper mLongPressHelper; private final HolographicOutlineHelper mOutlineHelper; @@ -62,9 +63,12 @@ public class BubbleTextView extends TextView { private float mSlop; - private int mTextColor; private final boolean mCustomShadowsEnabled; - private boolean mIsTextVisible; + private final boolean mLayoutHorizontal; + private final int mIconSize; + private final int mIconPaddingSize; + private final int mTextSize; + private int mTextColor; private boolean mStayPressed; private boolean mIgnorePressedStateChange; @@ -79,10 +83,19 @@ public class BubbleTextView extends TextView { public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true); + mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false); + mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride, + grid.allAppsIconSizePx); + mIconPaddingSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconPaddingOverride, + grid.iconDrawablePaddingPx); + mTextSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_textSizeOverride, + grid.allAppsIconTextSizePx); a.recycle(); if (mCustomShadowsEnabled) { @@ -92,6 +105,12 @@ public class BubbleTextView extends TextView { } else { mBackground = null; } + + // If we are laying out horizontal, then center the text vertically + if (mLayoutHorizontal) { + setGravity(Gravity.CENTER_VERTICAL); + } + mLongPressHelper = new CheckLongPressHelper(this); mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); @@ -106,9 +125,7 @@ public class BubbleTextView extends TextView { super.onFinishInflate(); // Ensure we are using the right text size - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); + setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize); } public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, @@ -119,16 +136,11 @@ public class BubbleTextView extends TextView { public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, boolean setDefaultPadding, boolean promiseStateChanged) { Bitmap b = info.getIcon(iconCache); - LauncherAppState app = LauncherAppState.getInstance(); FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); iconDrawable.setGhostModeEnabled(info.isDisabled != 0); - setCompoundDrawables(null, iconDrawable, null, null); - if (setDefaultPadding) { - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setCompoundDrawablePadding(grid.iconDrawablePaddingPx); - } + setIcon(iconDrawable, mIconSize, setDefaultPadding ? mIconPaddingSize : -1); if (info.contentDescription != null) { setContentDescription(info.contentDescription); } @@ -141,13 +153,7 @@ public class BubbleTextView extends TextView { } public void applyFromApplicationInfo(AppInfo info) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap); - topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx); - setCompoundDrawables(null, topDrawable, null, null); - setCompoundDrawablePadding(grid.iconDrawablePaddingPx); + setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize, mIconPaddingSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -155,7 +161,6 @@ public class BubbleTextView extends TextView { setTag(info); } - @Override protected boolean setFrame(int left, int top, int right, int bottom) { if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) { @@ -186,10 +191,19 @@ public class BubbleTextView extends TextView { } } + /** Returns the icon for this view. */ + public Drawable getIcon() { + return mIcon; + } + + /** Returns whether the layout is horizontal. */ + public boolean isLayoutHorizontal() { + return mLayoutHorizontal; + } + private void updateIconState() { - Drawable top = getCompoundDrawables()[1]; - if (top instanceof FastBitmapDrawable) { - ((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed); + if (mIcon instanceof FastBitmapDrawable) { + ((FastBitmapDrawable) mIcon).setPressed(isPressed() || mStayPressed); } } @@ -325,10 +339,9 @@ public class BubbleTextView extends TextView { super.onAttachedToWindow(); if (mBackground != null) mBackground.setCallback(this); - Drawable top = getCompoundDrawables()[1]; - if (top instanceof PreloadIconDrawable) { - ((PreloadIconDrawable) top).applyPreloaderTheme(getPreloaderTheme()); + if (mIcon instanceof PreloadIconDrawable) { + ((PreloadIconDrawable) mIcon).applyPreloaderTheme(getPreloaderTheme()); } mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -358,11 +371,6 @@ public class BubbleTextView extends TextView { } else { super.setTextColor(res.getColor(android.R.color.transparent)); } - mIsTextVisible = visible; - } - - public boolean isTextVisible() { - return mIsTextVisible; } @Override @@ -385,15 +393,13 @@ public class BubbleTextView extends TextView { ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ? info.getInstallProgress() : 0)) : 100; - Drawable[] drawables = getCompoundDrawables(); - Drawable top = drawables[1]; - if (top != null) { + if (mIcon != null) { final PreloadIconDrawable preloadDrawable; - if (top instanceof PreloadIconDrawable) { - preloadDrawable = (PreloadIconDrawable) top; + if (mIcon instanceof PreloadIconDrawable) { + preloadDrawable = (PreloadIconDrawable) mIcon; } else { - preloadDrawable = new PreloadIconDrawable(top, getPreloaderTheme()); - setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]); + preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme()); + setIcon(preloadDrawable, mIconSize, -1); } preloadDrawable.setLevel(progressLevel); @@ -417,4 +423,23 @@ public class BubbleTextView extends TextView { } return theme; } + + /** + * Sets the icon for this view based on the layout direction. + */ + private Drawable setIcon(Drawable icon, int iconSize, int drawablePadding) { + mIcon = icon; + if (iconSize != -1) { + mIcon.setBounds(0, 0, iconSize, iconSize); + } + if (mLayoutHorizontal) { + setCompoundDrawablesRelative(mIcon, null, null, null); + } else { + setCompoundDrawablesRelative(null, mIcon, null, null); + } + if (drawablePadding != -1) { + setCompoundDrawablePadding(drawablePadding); + } + return icon; + } } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 7d02e10b5..b5bb55ca7 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -122,6 +122,8 @@ public class DeviceProfile { int hotseatAllAppsRank; int allAppsNumRows; int allAppsNumCols; + // TODO(winsonc): to be used with the grid layout + int allAppsRowsSize; int searchBarSpaceWidthPx; int searchBarSpaceHeightPx; int pageIndicatorHeightPx; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 0a6557907..f6b05208b 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -24,6 +24,7 @@ import android.animation.PropertyValuesHolder; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; @@ -46,7 +47,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; - import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.Workspace.ItemOperator; @@ -219,7 +219,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return false; } - mLauncher.getWorkspace().beginDragShared(v, this); + mLauncher.getWorkspace().beginDragShared(v, new Point(), this, false); mCurrentDragInfo = item; mEmptyCellRank = item.rank; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 984d536a5..9c4632cc8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1,4 +1,3 @@ - /* * Copyright (C) 2008 The Android Open Source Project * @@ -22,7 +21,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.Activity; @@ -48,7 +46,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; -import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; @@ -82,15 +79,12 @@ import android.view.Surface; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; import android.widget.FrameLayout; @@ -132,7 +126,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, - View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener { + View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener, + LauncherStateTransitionAnimation.Callbacks { static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -213,9 +208,10 @@ public class Launcher extends Activity public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data"; /** The different states that Launcher can be in. */ - private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED }; + enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }; private State mState = State.WORKSPACE; private AnimatorSet mStateAnimation; + private LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -235,7 +231,6 @@ public class Launcher extends Activity private static int NEW_APPS_PAGE_MOVE_DELAY = 500; private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; private static int NEW_APPS_ANIMATION_DELAY = 500; - private static final int SINGLE_FRAME_DELAY = 16; private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); @@ -267,6 +262,7 @@ public class Launcher extends Activity private View mAllAppsButton; private SearchDropTargetBar mSearchDropTargetBar; + private AppsContainerView mAppsView; private AppsCustomizeTabHost mAppsCustomizeTabHost; private AppsCustomizePagedView mAppsCustomizeContent; private boolean mAutoAdvanceRunning = false; @@ -305,9 +301,6 @@ public class Launcher extends Activity private View.OnTouchListener mHapticFeedbackTouchListener; - public static final int BUILD_LAYER = 0; - public static final int BUILD_AND_SET_LAYER = 1; - // Related to the auto-advancing of widgets private final int ADVANCE_MSG = 1; private final int mAdvanceInterval = 20000; @@ -431,6 +424,7 @@ public class Launcher extends Activity mIconCache.flushInvalidIcons(grid); mDragController = new DragController(this); mInflater = getLayoutInflater(); + mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this); mStats = new Stats(this); @@ -990,10 +984,12 @@ public class Launcher extends Activity super.onResume(); // Restore the previous launcher state - if (mOnResumeState == State.WORKSPACE) { + if (mOnResumeState == State.WORKSPACE || mOnResumeState == State.NONE) { showWorkspace(false); - } else if (mOnResumeState == State.APPS_CUSTOMIZE) { - showAllApps(false, mAppsCustomizeContent.getContentType(), false); + } else if (mOnResumeState == State.APPS) { + showAppsView(false /* animated */, false /* resetListToTop */); + } else if (mOnResumeState == State.WIDGETS) { + showWidgetsView(false, false); } mOnResumeState = State.NONE; @@ -1302,8 +1298,8 @@ public class Launcher extends Activity } State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal())); - if (state == State.APPS_CUSTOMIZE) { - mOnResumeState = State.APPS_CUSTOMIZE; + if (state == State.APPS || state == State.WIDGETS) { + mOnResumeState = state; } int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, @@ -1429,6 +1425,9 @@ public class Launcher extends Activity mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.search_drop_target_bar); + // Setup Apps + mAppsView = (AppsContainerView) findViewById(R.id.apps_view); + // Setup AppsCustomize mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane); mAppsCustomizeContent = (AppsCustomizePagedView) @@ -1646,16 +1645,17 @@ public class Launcher extends Activity if (Intent.ACTION_SCREEN_OFF.equals(action)) { mUserPresent = false; mDragLayer.clearAllResizeFrames(); - updateRunning(); + updateAutoAdvanceState(); // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop - if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) { + if (mAppsView != null && mAppsCustomizeTabHost != null && + mPendingAddInfo.container == ItemInfo.NO_ID) { showWorkspace(false); } } else if (Intent.ACTION_USER_PRESENT.equals(action)) { mUserPresent = true; - updateRunning(); + updateAutoAdvanceState(); } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) { mModel.resetLoadedState(false, true); mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE, @@ -1723,12 +1723,12 @@ public class Launcher extends Activity unregisterReceiver(mReceiver); mAttached = false; } - updateRunning(); + updateAutoAdvanceState(); } public void onWindowVisibilityChanged(int visibility) { mVisible = visibility == View.VISIBLE; - updateRunning(); + updateAutoAdvanceState(); // The following code used to be in onResume, but it turns out onResume is called when // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged // is a more appropriate event to handle @@ -1775,7 +1775,7 @@ public class Launcher extends Activity mAutoAdvanceSentTime = System.currentTimeMillis(); } - private void updateRunning() { + private void updateAutoAdvanceState() { boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty(); if (autoAdvanceRunning != mAutoAdvanceRunning) { mAutoAdvanceRunning = autoAdvanceRunning; @@ -1821,14 +1821,14 @@ public class Launcher extends Activity if (v instanceof Advanceable) { mWidgetsToAdvance.put(hostView, appWidgetInfo); ((Advanceable) v).fyiWillBeAdvancedByHostKThx(); - updateRunning(); + updateAutoAdvanceState(); } } void removeWidgetToAutoAdvance(View hostView) { if (mWidgetsToAdvance.containsKey(hostView)) { mWidgetsToAdvance.remove(hostView); - updateRunning(); + updateAutoAdvanceState(); } } @@ -1842,14 +1842,18 @@ public class Launcher extends Activity Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); } - public ArrayList getAllAppsList() { - return mAppsCustomizeContent.getApps(); - } - public DragLayer getDragLayer() { return mDragLayer; } + public AppsContainerView getAppsView() { + return mAppsView; + } + + public AppsCustomizeTabHost getWidgetsView() { + return mAppsCustomizeTabHost; + } + public Workspace getWorkspace() { return mWorkspace; } @@ -1935,6 +1939,11 @@ public class Launcher extends Activity imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } + // Reset the apps view + if (!alreadyOnHome && mAppsView != null) { + mAppsView.scrollToTop(); + } + // Reset the apps customize page if (!alreadyOnHome && mAppsCustomizeTabHost != null) { mAppsCustomizeTabHost.reset(); @@ -2452,13 +2461,10 @@ public class Launcher extends Activity return; } - if (isAllAppsVisible()) { - if (mAppsCustomizeContent.getContentType() == - AppsCustomizePagedView.ContentType.Applications) { - showWorkspace(true); - } else { - showOverviewMode(true); - } + if (isAppsViewVisible()) { + showWorkspace(true); + } else if (isWidgetsViewVisible()) { + showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { mWorkspace.exitOverviewMode(true); } else if (mWorkspace.getOpenFolder() != null) { @@ -2589,10 +2595,10 @@ public class Launcher extends Activity */ protected void onClickAllAppsButton(View v) { if (LOGD) Log.d(TAG, "onClickAllAppsButton"); - if (isAllAppsVisible()) { + if (isAppsViewVisible()) { showWorkspace(true); } else { - showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false); + showAppsView(true /* animated */, false /* resetListToTop */); } if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickAllAppsButton(v); @@ -2763,7 +2769,7 @@ public class Launcher extends Activity if (mIsSafeModeEnabled) { Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); } else { - showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true); + showWidgetsView(true /* animated */, true /* resetPageToZero */); if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickAddWidgetButton(view); } @@ -2920,12 +2926,11 @@ public class Launcher extends Activity int width = v.getMeasuredWidth(), height = v.getMeasuredHeight(); if (v instanceof TextView) { // Launch from center of icon, not entire view - TextView tv = (TextView) v; - Drawable[] drawables = tv.getCompoundDrawables(); - if (drawables != null && drawables[1] != null) { - Rect bounds = drawables[1].getBounds(); + Drawable icon = Workspace.getTextViewIcon((TextView) v); + if (icon != null) { + Rect bounds = icon.getBounds(); left = (width - bounds.width()) / 2; - top = tv.getPaddingTop(); + top = v.getPaddingTop(); width = bounds.width(); height = bounds.height(); } @@ -3219,12 +3224,23 @@ public class Launcher extends Activity return null; } } else { - return (CellLayout) mWorkspace.getScreenWithId(screenId); + return mWorkspace.getScreenWithId(screenId); } } + /** + * For overridden classes. + */ public boolean isAllAppsVisible() { - return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE); + return isAppsViewVisible(); + } + + public boolean isAppsViewVisible() { + return (mState == State.APPS) || (mOnResumeState == State.APPS); + } + + public boolean isWidgetsViewVisible() { + return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS); } private void setWorkspaceBackground(boolean workspace) { @@ -3242,578 +3258,6 @@ public class Launcher extends Activity setWorkspaceBackground(visible); } - private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace); - } - } - - private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace); - } - - // Update the workspace transition step as well - dispatchOnLauncherTransitionStep(v, 0f); - } - - private void dispatchOnLauncherTransitionStep(View v, float t) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionStep(this, t); - } - } - - private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace); - } - - // Update the workspace transition step as well - dispatchOnLauncherTransitionStep(v, 1f); - } - - /** - * Things to test when changing the following seven functions. - * - Home from workspace - * - from center screen - * - from other screens - * - Home from all apps - * - from center screen - * - from other screens - * - Back from all apps - * - from center screen - * - from other screens - * - Launch app from workspace and quit - * - with back - * - with home - * - Launch app from all apps and quit - * - with back - * - with home - * - Go to a screen that's not the default, then all - * apps, and launch and app, and go back - * - with back - * -with home - * - On workspace, long press power and go back - * - with back - * - with home - * - On all apps, long press power and go back - * - with back - * - with home - * - On workspace, power off - * - On all apps, power off - * - Launch an app and turn off the screen while in that app - * - Go back with home key - * - Go back with back key TODO: make this not go to workspace - * - From all apps - * - From workspace - * - Enter and exit car mode (becuase it causes an extra configuration changed) - * - From all apps - * - From the center workspace - * - From another workspace - */ - - /** - * Zoom the camera out from the workspace to reveal 'toView'. - * Assumes that the view to show is anchored at either the very top or very bottom - * of the screen. - */ - private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) { - AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType(); - showAppsCustomizeHelper(animated, springLoaded, contentType); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded, - final AppsCustomizePagedView.ContentType contentType) { - if (mStateAnimation != null) { - mStateAnimation.setDuration(0); - mStateAnimation.cancel(); - mStateAnimation = null; - } - - boolean material = Utilities.isLmpOrAbove(); - - final Resources res = getResources(); - - final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); - final int itemsAlphaStagger = - res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); - - final View fromView = mWorkspace; - final AppsCustomizeTabHost toView = mAppsCustomizeTabHost; - - final HashMap layerViews = new HashMap(); - - Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ? - Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN; - Animator workspaceAnim = - mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews); - if (contentType == AppsCustomizePagedView.ContentType.Widgets) { - // Set the content type for the all apps/widgets space - mAppsCustomizeTabHost.setContentTypeImmediate(contentType); - } - - // If for some reason our views aren't initialized, don't animate - boolean initialized = getAllAppsButton() != null; - - if (animated && initialized) { - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - final AppsCustomizePagedView content = (AppsCustomizePagedView) - toView.findViewById(R.id.apps_customize_pane_content); - - final View page = content.getPageAt(content.getCurrentPage()); - final View revealView = toView.findViewById(R.id.fake_page); - - final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets; - if (isWidgetTray) { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); - } else { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel)); - } - - // Hide the real page background, and swap in the fake one - content.setPageBackgroundsVisible(false); - revealView.setVisibility(View.VISIBLE); - // We need to hide this view as the animation start will be posted. - revealView.setAlpha(0); - - int width = revealView.getMeasuredWidth(); - int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); - - revealView.setTranslationY(0); - revealView.setTranslationX(0); - - // Get the y delta between the center of the page and the center of the all apps button - int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, - getAllAppsButton(), null); - - float alpha = 0; - float xDrift = 0; - float yDrift = 0; - if (material) { - alpha = isWidgetTray ? 0.3f : 1f; - yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1]; - xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0]; - } else { - yDrift = 2 * height / 3; - xDrift = 0; - } - final float initAlpha = alpha; - - layerViews.put(revealView, BUILD_AND_SET_LAYER); - PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f); - PropertyValuesHolder panelDriftY = - PropertyValuesHolder.ofFloat("translationY", yDrift, 0); - PropertyValuesHolder panelDriftX = - PropertyValuesHolder.ofFloat("translationX", xDrift, 0); - - ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, - panelAlpha, panelDriftY, panelDriftX); - - panelAlphaAndDrift.setDuration(revealDuration); - panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); - - mStateAnimation.play(panelAlphaAndDrift); - - if (page != null) { - page.setVisibility(View.VISIBLE); - layerViews.put(page, BUILD_AND_SET_LAYER); - - ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0); - page.setTranslationY(yDrift); - pageDrift.setDuration(revealDuration); - pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); - pageDrift.setStartDelay(itemsAlphaStagger); - mStateAnimation.play(pageDrift); - - page.setAlpha(0f); - ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f); - itemsAlpha.setDuration(revealDuration); - itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - itemsAlpha.setStartDelay(itemsAlphaStagger); - mStateAnimation.play(itemsAlpha); - } - - View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator); - pageIndicators.setAlpha(0.01f); - ObjectAnimator indicatorsAlpha = - ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f); - indicatorsAlpha.setDuration(revealDuration); - mStateAnimation.play(indicatorsAlpha); - - if (material) { - final View allApps = getAllAppsButton(); - int allAppsButtonSize = LauncherAppState.getInstance(). - getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; - float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2; - Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2, - height / 2, startRadius, revealRadius); - reveal.setDuration(revealDuration); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - - reveal.addListener(new AnimatorListenerAdapter() { - public void onAnimationStart(Animator animation) { - if (!isWidgetTray) { - allApps.setVisibility(View.INVISIBLE); - } - } - public void onAnimationEnd(Animator animation) { - if (!isWidgetTray) { - allApps.setVisibility(View.VISIBLE); - } - } - }); - mStateAnimation.play(reveal); - } - - mStateAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - dispatchOnLauncherTransitionEnd(fromView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - - revealView.setVisibility(View.INVISIBLE); - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - content.setPageBackgroundsVisible(true); - - // Hide the search bar - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.hideSearchBar(false); - } - - // This can hold unnecessary references to views. - mStateAnimation = null; - } - - }); - - if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); - } - - dispatchOnLauncherTransitionPrepare(fromView, animated, false); - dispatchOnLauncherTransitionPrepare(toView, animated, false); - final AnimatorSet stateAnimation = mStateAnimation; - final Runnable startAnimRunnable = new Runnable() { - public void run() { - // Check that mStateAnimation hasn't changed while - // we waited for a layout/draw pass - if (mStateAnimation != stateAnimation) - return; - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - - revealView.setAlpha(initAlpha); - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - } - - if (Utilities.isLmpOrAbove()) { - for (View v : layerViews.keySet()) { - if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); - } - } - mStateAnimation.start(); - } - }; - toView.bringToFront(); - toView.setVisibility(View.VISIBLE); - toView.post(startAnimRunnable); - } else { - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setScaleX(1.0f); - toView.setScaleY(1.0f); - toView.setVisibility(View.VISIBLE); - toView.bringToFront(); - - if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) { - // Hide the search bar - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.hideSearchBar(false); - } - } - dispatchOnLauncherTransitionPrepare(fromView, animated, false); - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionEnd(fromView, animated, false); - dispatchOnLauncherTransitionPrepare(toView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - } - } - - /** - * Zoom the camera back into the workspace, hiding 'fromView'. - * This is the opposite of showAppsCustomizeHelper. - * @param animated If true, the transition will be animated. - */ - private void hideAppsCustomizeHelper(Workspace.State toState, final boolean animated, - final boolean springLoaded, final Runnable onCompleteRunnable) { - - if (mStateAnimation != null) { - mStateAnimation.setDuration(0); - mStateAnimation.cancel(); - mStateAnimation = null; - } - - boolean material = Utilities.isLmpOrAbove(); - Resources res = getResources(); - - final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime); - final int itemsAlphaStagger = - res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); - - final View fromView = mAppsCustomizeTabHost; - final View toView = mWorkspace; - Animator workspaceAnim = null; - final HashMap layerViews = new HashMap(); - - if (toState == Workspace.State.NORMAL) { - workspaceAnim = mWorkspace.getChangeStateAnimation( - toState, animated, layerViews); - } else if (toState == Workspace.State.SPRING_LOADED || - toState == Workspace.State.OVERVIEW) { - workspaceAnim = mWorkspace.getChangeStateAnimation( - toState, animated, layerViews); - } - - // If for some reason our views aren't initialized, don't animate - boolean initialized = getAllAppsButton() != null; - - if (animated && initialized) { - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); - } - - final AppsCustomizePagedView content = (AppsCustomizePagedView) - fromView.findViewById(R.id.apps_customize_pane_content); - - final View page = content.getPageAt(content.getNextPage()); - - // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases - int count = content.getChildCount(); - for (int i = 0; i < count; i++) { - View child = content.getChildAt(i); - if (child != page) { - child.setVisibility(View.INVISIBLE); - } - } - final View revealView = fromView.findViewById(R.id.fake_page); - - // hideAppsCustomizeHelper is called in some cases when it is already hidden - // don't perform all these no-op animations. In particularly, this was causing - // the all-apps button to pop in and out. - if (fromView.getVisibility() == View.VISIBLE) { - AppsCustomizePagedView.ContentType contentType = content.getContentType(); - final boolean isWidgetTray = - contentType == AppsCustomizePagedView.ContentType.Widgets; - - if (isWidgetTray) { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); - } else { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel)); - } - - int width = revealView.getMeasuredWidth(); - int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); - - // Hide the real page background, and swap in the fake one - revealView.setVisibility(View.VISIBLE); - content.setPageBackgroundsVisible(false); - - final View allAppsButton = getAllAppsButton(); - revealView.setTranslationY(0); - int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, - allAppsButton, null); - - float xDrift = 0; - float yDrift = 0; - if (material) { - yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1]; - xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0]; - } else { - yDrift = 2 * height / 3; - xDrift = 0; - } - - layerViews.put(revealView, BUILD_AND_SET_LAYER); - TimeInterpolator decelerateInterpolator = material ? - new LogDecelerateInterpolator(100, 0) : - new DecelerateInterpolator(1f); - - // The vertical motion of the apps panel should be delayed by one frame - // from the conceal animation in order to give the right feel. We correpsondingly - // shorten the duration so that the slide and conceal end at the same time. - ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY", - 0, yDrift); - panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); - panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - panelDriftY.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelDriftY); - - ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX", - 0, xDrift); - panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); - panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - panelDriftX.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelDriftX); - - if (isWidgetTray || !material) { - float finalAlpha = material ? 0.4f : 0f; - revealView.setAlpha(1f); - ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", - 1f, finalAlpha); - panelAlpha.setDuration(material ? revealDuration : 150); - panelAlpha.setInterpolator(decelerateInterpolator); - panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); - mStateAnimation.play(panelAlpha); - } - - if (page != null) { - layerViews.put(page, BUILD_AND_SET_LAYER); - - ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY", - 0, yDrift); - page.setTranslationY(0); - pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); - pageDrift.setInterpolator(decelerateInterpolator); - pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - mStateAnimation.play(pageDrift); - - page.setAlpha(1f); - ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f); - itemsAlpha.setDuration(100); - itemsAlpha.setInterpolator(decelerateInterpolator); - mStateAnimation.play(itemsAlpha); - } - - View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator); - pageIndicators.setAlpha(1f); - ObjectAnimator indicatorsAlpha = - LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f); - indicatorsAlpha.setDuration(revealDuration); - indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); - mStateAnimation.play(indicatorsAlpha); - - width = revealView.getMeasuredWidth(); - - if (material) { - if (!isWidgetTray) { - allAppsButton.setVisibility(View.INVISIBLE); - } - int allAppsButtonSize = LauncherAppState.getInstance(). - getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; - float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2; - Animator reveal = - LauncherAnimUtils.createCircularReveal(revealView, width / 2, - height / 2, revealRadius, finalRadius); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - reveal.setDuration(revealDuration); - reveal.setStartDelay(itemsAlphaStagger); - - reveal.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - revealView.setVisibility(View.INVISIBLE); - if (!isWidgetTray) { - allAppsButton.setVisibility(View.VISIBLE); - } - } - }); - - mStateAnimation.play(reveal); - } - - dispatchOnLauncherTransitionPrepare(fromView, animated, true); - dispatchOnLauncherTransitionPrepare(toView, animated, true); - mAppsCustomizeContent.stopScrolling(); - } - - mStateAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - fromView.setVisibility(View.GONE); - dispatchOnLauncherTransitionEnd(fromView, animated, true); - dispatchOnLauncherTransitionEnd(toView, animated, true); - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - - content.setPageBackgroundsVisible(true); - // Unhide side pages - int count = content.getChildCount(); - for (int i = 0; i < count; i++) { - View child = content.getChildAt(i); - child.setVisibility(View.VISIBLE); - } - - // Reset page transforms - if (page != null) { - page.setTranslationX(0); - page.setTranslationY(0); - page.setAlpha(1); - } - content.setCurrentPage(content.getNextPage()); - - mAppsCustomizeContent.updateCurrentPageScroll(); - - // This can hold unnecessary references to views. - mStateAnimation = null; - } - }); - - final AnimatorSet stateAnimation = mStateAnimation; - final Runnable startAnimRunnable = new Runnable() { - public void run() { - // Check that mStateAnimation hasn't changed while - // we waited for a layout/draw pass - if (mStateAnimation != stateAnimation) - return; - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - } - - if (Utilities.isLmpOrAbove()) { - for (View v : layerViews.keySet()) { - if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); - } - } - mStateAnimation.start(); - } - }; - fromView.post(startAnimRunnable); - } else { - fromView.setVisibility(View.GONE); - dispatchOnLauncherTransitionPrepare(fromView, animated, true); - dispatchOnLauncherTransitionStart(fromView, animated, true); - dispatchOnLauncherTransitionEnd(fromView, animated, true); - dispatchOnLauncherTransitionPrepare(toView, animated, true); - dispatchOnLauncherTransitionStart(toView, animated, true); - dispatchOnLauncherTransitionEnd(toView, animated, true); - } - } - @Override public void onTrimMemory(int level) { super.onTrimMemory(level); @@ -3829,19 +3273,24 @@ public class Launcher extends Activity } } - protected void showWorkspace(boolean animated) { - showWorkspace(animated, null); + @Override + public void onStateTransitionHideSearchBar() { + // Hide the search bar + if (mSearchDropTargetBar != null) { + mSearchDropTargetBar.hideSearchBar(false /* animated */); + } } - protected void showWorkspace() { - showWorkspace(true); + protected void showWorkspace(boolean animated) { + showWorkspace(animated, null); } void showWorkspace(boolean animated, Runnable onCompleteRunnable) { if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) { boolean wasInSpringLoadedMode = (mState != State.WORKSPACE); mWorkspace.setVisibility(View.VISIBLE); - hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable); + mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL, + animated, onCompleteRunnable); // Show the search bar (only animate if we were showing the drop target bar in spring // loaded mode) @@ -3860,7 +3309,7 @@ public class Launcher extends Activity // Resume the auto-advance of widgets mUserPresent = true; - updateRunning(); + updateAutoAdvanceState(); // Send an accessibility event to announce the context change getWindow().getDecorView() @@ -3871,7 +3320,8 @@ public class Launcher extends Activity void showOverviewMode(boolean animated) { mWorkspace.setVisibility(View.VISIBLE); - hideAppsCustomizeHelper(Workspace.State.OVERVIEW, animated, false, null); + mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW, + animated, null /* onCompleteRunnable */); mState = State.WORKSPACE; onWorkspaceShown(animated); } @@ -3879,14 +3329,24 @@ public class Launcher extends Activity public void onWorkspaceShown(boolean animated) { } - void showAllApps(boolean animated, AppsCustomizePagedView.ContentType contentType, - boolean resetPageToZero) { - if (mState != State.WORKSPACE) return; + /** + * Shows the apps view. + */ + void showAppsView(boolean animated, boolean resetListToTop) { + if (resetListToTop) { + mAppsView.scrollToTop(); + } + showAppsOrWidgets(animated, State.APPS); + } + /** + * Shows the widgets view. + */ + void showWidgetsView(boolean animated, boolean resetPageToZero) { if (resetPageToZero) { mAppsCustomizeTabHost.reset(); } - showAppsCustomizeHelper(animated, false, contentType); + showAppsOrWidgets(animated, State.WIDGETS); mAppsCustomizeTabHost.post(new Runnable() { @Override public void run() { @@ -3894,13 +3354,27 @@ public class Launcher extends Activity mAppsCustomizeTabHost.requestFocus(); } }); + } + + /** + * Sets up the transition to show the apps/widgets view. + */ + private void showAppsOrWidgets(boolean animated, State toState) { + if (mState != State.WORKSPACE) return; + if (toState != State.APPS && toState != State.WIDGETS) return; + + if (toState == State.APPS) { + mStateTransitionAnimation.startAnimationToAllApps(animated); + } else { + mStateTransitionAnimation.startAnimationToWidgets(animated); + } // Change the state *after* we've called all the transition code - mState = State.APPS_CUSTOMIZE; + mState = toState; // Pause the auto-advance of widgets until we are out of AllApps mUserPresent = false; - updateRunning(); + updateAutoAdvanceState(); closeFolder(); // Send an accessibility event to announce the context change @@ -3909,15 +3383,19 @@ public class Launcher extends Activity } void enterSpringLoadedDragMode() { - if (isAllAppsVisible()) { - hideAppsCustomizeHelper(Workspace.State.SPRING_LOADED, true, true, null); - mState = State.APPS_CUSTOMIZE_SPRING_LOADED; + if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED || + mState == State.WIDGETS_SPRING_LOADED) { + return; } + + mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED, + true /* animated */, null /* onCompleteRunnable */); + mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; } void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { - if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return; + if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; mHandler.postDelayed(new Runnable() { @Override @@ -3936,11 +3414,12 @@ public class Launcher extends Activity } void exitSpringLoadedDragMode() { - if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) { - final boolean animated = true; - final boolean springLoaded = true; - showAppsCustomizeHelper(animated, springLoaded); - mState = State.APPS_CUSTOMIZE; + if (mState == State.APPS_SPRING_LOADED) { + mStateTransitionAnimation.startAnimationToAllApps(true /* animated */); + mState = State.APPS; + } else if (mState == State.WIDGETS_SPRING_LOADED) { + mStateTransitionAnimation.startAnimationToWidgets(true /* animated */); + mState = State.WIDGETS; } // Otherwise, we are not in spring loaded mode, so don't do anything. } @@ -4018,8 +3497,10 @@ public class Launcher extends Activity final List text = event.getText(); text.clear(); // Populate event with a fake title based on the current state. - if (mState == State.APPS_CUSTOMIZE) { - text.add(mAppsCustomizeTabHost.getContentTag()); + if (mState == State.APPS) { + text.add("Apps"); + } else if (mState == State.WIDGETS) { + text.add("Widgets"); } else { text.add(getString(R.string.all_apps_home_button_label)); } @@ -4242,8 +3723,8 @@ public class Launcher extends Activity // Remove the extra empty screen mWorkspace.removeExtraEmptyScreen(false, false); - if (addedApps != null && mAppsCustomizeContent != null) { - mAppsCustomizeContent.addApps(addedApps); + if (addedApps != null && mAppsView != null) { + mAppsView.addApps(addedApps); } } @@ -4617,8 +4098,10 @@ public class Launcher extends Activity * Implementation of the method from LauncherModel.Callbacks. */ public void bindAllApplications(final ArrayList apps) { + if (mAppsView != null) { + mAppsView.setApps(apps); + } if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setApps(apps); mAppsCustomizeContent.onPackagesUpdated( LauncherModel.getSortedWidgetsAndShortcuts(this)); } @@ -4642,8 +4125,8 @@ public class Launcher extends Activity return; } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.updateApps(apps); + if (mAppsView != null) { + mAppsView.updateApps(apps); } } @@ -4757,8 +4240,8 @@ public class Launcher extends Activity } // Update AllApps - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.removeApps(appInfos); + if (mAppsView != null) { + mAppsView.removeApps(appInfos); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index cb9d12e01..3cada6fb8 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3308,7 +3308,7 @@ public class LauncherModel extends BroadcastReceiver } } - // Returns a list of ResolveInfos/AppWindowInfos in sorted order + // Returns a list of ResolveInfos/AppWidgetInfos in sorted order public static ArrayList getSortedWidgetsAndShortcuts(Context context) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java new file mode 100644 index 000000000..484ed5c30 --- /dev/null +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -0,0 +1,832 @@ +/* + * 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.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.TimeInterpolator; +import android.content.res.Resources; +import android.util.Log; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +import java.util.HashMap; + +/** + * TODO: figure out what kind of tests we can write for this + * + * Things to test when changing the following class. + * - Home from workspace + * - from center screen + * - from other screens + * - Home from all apps + * - from center screen + * - from other screens + * - Back from all apps + * - from center screen + * - from other screens + * - Launch app from workspace and quit + * - with back + * - with home + * - Launch app from all apps and quit + * - with back + * - with home + * - Go to a screen that's not the default, then all + * apps, and launch and app, and go back + * - with back + * -with home + * - On workspace, long press power and go back + * - with back + * - with home + * - On all apps, long press power and go back + * - with back + * - with home + * - On workspace, power off + * - On all apps, power off + * - Launch an app and turn off the screen while in that app + * - Go back with home key + * - Go back with back key TODO: make this not go to workspace + * - From all apps + * - From workspace + * - Enter and exit car mode (becuase it causes an extra configuration changed) + * - From all apps + * - From the center workspace + * - From another workspace + */ +public class LauncherStateTransitionAnimation { + + /** + * Callbacks made during the state transition + */ + interface Callbacks { + public void onStateTransitionHideSearchBar(); + } + + /** + * Private callbacks made during transition setup. + */ + static abstract class PrivateTransitionCallbacks { + void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {} + void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {} + float getMaterialRevealViewFinalAlpha(View revealView) { + return 0; + } + float getMaterialRevealViewFinalXDrift(View revealView) { + return 0; + } + float getMaterialRevealViewFinalYDrift(View revealView) { + return 0; + } + float getMaterialRevealViewStartFinalRadius() { + return 0; + } + AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView, + View allAppsButtonView) { + return null; + } + } + + public static final String TAG = "LauncherStateTransitionAnimation"; + + // Flags to determine how to set the layers on views before the transition animation + public static final int BUILD_LAYER = 0; + public static final int BUILD_AND_SET_LAYER = 1; + public static final int SINGLE_FRAME_DELAY = 16; + + private Launcher mLauncher; + private Callbacks mCb; + private AnimatorSet mStateAnimation; + + public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) { + mLauncher = l; + mCb = cb; + } + + /** + * Starts an animation to the apps view. + */ + public void startAnimationToAllApps(final boolean animated) { + final AppsContainerView toView = mLauncher.getAppsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + private int[] mAllAppsToPanelDelta; + + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + toView.setBackground(null); + // Get the y delta between the center of the page and the center of the all apps + // button + mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, + allAppsButtonView, null); + } + @Override + public float getMaterialRevealViewFinalAlpha(View revealView) { + return 1f; + } + @Override + public float getMaterialRevealViewFinalXDrift(View revealView) { + return mAllAppsToPanelDelta[0]; + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return mAllAppsToPanelDelta[1]; + } + @Override + public float getMaterialRevealViewStartFinalRadius() { + int allAppsButtonSize = LauncherAppState.getInstance(). + getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; + return allAppsButtonSize / 2; + } + @Override + public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( + final View revealView, final View allAppsButtonView) { + return new AnimatorListenerAdapter() { + public void onAnimationStart(Animator animation) { + allAppsButtonView.setVisibility(View.INVISIBLE); + } + public void onAnimationEnd(Animator animation) { + allAppsButtonView.setVisibility(View.VISIBLE); + } + }; + } + }; + startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), + toView.getRevealView(), null, animated, cb); + } + + /** + * Starts an animation to the widgets view. + */ + public void startAnimationToWidgets(final boolean animated) { + final AppsCustomizeTabHost toView = mLauncher.getWidgetsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + // Hide the real page background, and swap in the fake one + ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false); + revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); + } + @Override + public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { + // Show the real page background + ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true); + } + @Override + public float getMaterialRevealViewFinalAlpha(View revealView) { + return 0.3f; + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return revealView.getMeasuredHeight() / 2; + } + }; + startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(), + toView.getRevealView(), toView.getPageIndicators(), animated, cb); + } + + /** + * Starts and animation to the workspace from the current overlay view. + */ + public void startAnimationToWorkspace(final Launcher.State fromState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + if (toWorkspaceState != Workspace.State.NORMAL && + toWorkspaceState != Workspace.State.SPRING_LOADED && + toWorkspaceState != Workspace.State.OVERVIEW) { + Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); + } + + if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { + startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, animated, + onCompleteRunnable); + } else { + startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, animated, + onCompleteRunnable); + } + } + + /** + * Creates and starts a new animation to a particular overlay view. + */ + private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView, + final View contentView, final View revealView, final View pageIndicatorsView, + final boolean animated, final PrivateTransitionCallbacks pCb) { + final Resources res = mLauncher.getResources(); + final boolean material = Utilities.isLmpOrAbove(); + final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final int itemsAlphaStagger = + res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); + + final View allAppsButtonView = mLauncher.getAllAppsButton(); + final View fromView = mLauncher.getWorkspace(); + + final HashMap layerViews = new HashMap<>(); + + // If for some reason our views aren't initialized, don't animate + boolean initialized = allAppsButtonView != null; + + // Cancel the current animation + cancelAnimation(); + + // Create the workspace animation. + // NOTE: this call apparently also sets the state for the workspace if !animated + Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( + toWorkspaceState, animated, layerViews); + + if (animated && initialized) { + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + + // Setup the reveal view animation + int width = revealView.getMeasuredWidth(); + int height = revealView.getMeasuredHeight(); + float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); + revealView.setVisibility(View.VISIBLE); + revealView.setAlpha(0f); + revealView.setTranslationY(0f); + revealView.setTranslationX(0f); + pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView); + + // Calculate the final animation values + final float revealViewToAlpha; + final float revealViewToXDrift; + final float revealViewToYDrift; + if (material) { + revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView); + revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView); + revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView); + } else { + revealViewToAlpha = 0f; + revealViewToYDrift = 2 * height / 3; + revealViewToXDrift = 0; + } + + // Create the animators + PropertyValuesHolder panelAlpha = + PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f); + PropertyValuesHolder panelDriftY = + PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0); + PropertyValuesHolder panelDriftX = + PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0); + ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, + panelAlpha, panelDriftY, panelDriftX); + panelAlphaAndDrift.setDuration(revealDuration); + panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); + + // Play the animation + layerViews.put(revealView, BUILD_AND_SET_LAYER); + mStateAnimation.play(panelAlphaAndDrift); + + // Setup the animation for the page indicators + if (pageIndicatorsView != null) { + pageIndicatorsView.setAlpha(0.01f); + ObjectAnimator indicatorsAlpha = + ObjectAnimator.ofFloat(pageIndicatorsView, "alpha", 1f); + indicatorsAlpha.setDuration(revealDuration); + mStateAnimation.play(indicatorsAlpha); + } + + // Setup the animation for the content view + contentView.setVisibility(View.VISIBLE); + contentView.setAlpha(0f); + contentView.setTranslationY(revealViewToYDrift); + layerViews.put(contentView, BUILD_AND_SET_LAYER); + + // Create the individual animators + ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY", + revealViewToYDrift, 0); + pageDrift.setDuration(revealDuration); + pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); + pageDrift.setStartDelay(itemsAlphaStagger); + mStateAnimation.play(pageDrift); + + ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f); + itemsAlpha.setDuration(revealDuration); + itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); + itemsAlpha.setStartDelay(itemsAlphaStagger); + mStateAnimation.play(itemsAlpha); + + if (material) { + // Animate the all apps button + float startRadius = pCb.getMaterialRevealViewStartFinalRadius(); + AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener( + revealView, allAppsButtonView); + Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2, + height / 2, startRadius, revealRadius); + reveal.setDuration(revealDuration); + reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); + if (listener != null) { + reveal.addListener(listener); + } + mStateAnimation.play(reveal); + } + + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + + // Hide the reveal view + revealView.setVisibility(View.INVISIBLE); + pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); + + // Disable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + + // Hide the search bar + mCb.onStateTransitionHideSearchBar(); + + // This can hold unnecessary references to views. + mStateAnimation = null; + } + + }); + + // Play the workspace animation + if (workspaceAnim != null) { + mStateAnimation.play(workspaceAnim); + } + + // Dispatch the prepare transition signal + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); + + + final AnimatorSet stateAnimation = mStateAnimation; + final Runnable startAnimRunnable = new Runnable() { + public void run() { + // Check that mStateAnimation hasn't changed while + // we waited for a layout/draw pass + if (mStateAnimation != stateAnimation) + return; + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + + // Enable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + if (Utilities.isViewAttachedToWindow(v)) { + v.buildLayer(); + } + } + + // Focus the new view + toView.requestFocus(); + + mStateAnimation.start(); + } + }; + + toView.bringToFront(); + toView.setVisibility(View.VISIBLE); + toView.post(startAnimRunnable); + } else { + toView.setTranslationX(0.0f); + toView.setTranslationY(0.0f); + toView.setScaleX(1.0f); + toView.setScaleY(1.0f); + toView.setVisibility(View.VISIBLE); + toView.bringToFront(); + + // Show the content view + contentView.setVisibility(View.VISIBLE); + + // Hide the search bar + mCb.onStateTransitionHideSearchBar(); + + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + } + } + + /** + * Starts and animation to the workspace from the apps view. + */ + private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + AppsContainerView appsView = mLauncher.getAppsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + int[] mAllAppsToPanelDelta; + + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + // Get the y delta between the center of the page and the center of the all apps + // button + mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, + allAppsButtonView, null); + } + @Override + public float getMaterialRevealViewFinalXDrift(View revealView) { + return mAllAppsToPanelDelta[0]; + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return mAllAppsToPanelDelta[1]; + } + @Override + float getMaterialRevealViewFinalAlpha(View revealView) { + // No alpha anim from all apps + return 1f; + } + @Override + float getMaterialRevealViewStartFinalRadius() { + int allAppsButtonSize = LauncherAppState.getInstance(). + getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; + return allAppsButtonSize / 2; + } + @Override + public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( + final View revealView, final View allAppsButtonView) { + return new AnimatorListenerAdapter() { + public void onAnimationStart(Animator animation) { + // We set the alpha instead of visibility to ensure that the focus does not + // get taken from the all apps view + allAppsButtonView.setVisibility(View.VISIBLE); + allAppsButtonView.setAlpha(0f); + } + public void onAnimationEnd(Animator animation) { + // Hide the reveal view + revealView.setVisibility(View.INVISIBLE); + + // Show the all apps button, and focus it + allAppsButtonView.setAlpha(1f); + } + }; + } + }; + startAnimationToWorkspaceFromOverlay(toWorkspaceState, appsView, appsView.getContentView(), + appsView.getRevealView(), null /* pageIndicatorsView */, animated, + onCompleteRunnable, cb); + } + + /** + * Starts and animation to the workspace from the widgets view. + */ + private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + @Override + public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { + AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); + + // Hide the real page background, and swap in the fake one + pagedView.stopScrolling(); + pagedView.setPageBackgroundsVisible(false); + revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); + + // Hide the side pages of the Widget tray to avoid some ugly edge cases + final View currentPage = pagedView.getPageAt(pagedView.getNextPage()); + int count = pagedView.getChildCount(); + for (int i = 0; i < count; i++) { + View child = pagedView.getChildAt(i); + if (child != currentPage) { + child.setVisibility(View.INVISIBLE); + } + } + } + @Override + public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { + AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); + + // Show the real page background and force-update the page + pagedView.setPageBackgroundsVisible(true); + pagedView.setCurrentPage(pagedView.getNextPage()); + pagedView.updateCurrentPageScroll(); + + // Unhide the side pages + int count = pagedView.getChildCount(); + for (int i = 0; i < count; i++) { + View child = pagedView.getChildAt(i); + child.setVisibility(View.VISIBLE); + } + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return revealView.getMeasuredHeight() / 2; + } + @Override + float getMaterialRevealViewFinalAlpha(View revealView) { + return 0.4f; + } + @Override + public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( + final View revealView, final View allAppsButtonView) { + return new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + // Hide the reveal view + revealView.setVisibility(View.INVISIBLE); + } + }; + } + }; + startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, + widgetsView.getContentView(), widgetsView.getRevealView(), + widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb); + } + + /** + * Creates and starts a new animation to the workspace. + */ + private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, + final View fromView, final View contentView, final View revealView, + final View pageIndicatorsView, final boolean animated, + final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { + final Resources res = mLauncher.getResources(); + final boolean material = Utilities.isLmpOrAbove(); + final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final int itemsAlphaStagger = + res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); + + final View allAppsButtonView = mLauncher.getAllAppsButton(); + final View toView = mLauncher.getWorkspace(); + + final HashMap layerViews = new HashMap<>(); + + // If for some reason our views aren't initialized, don't animate + boolean initialized = allAppsButtonView != null; + + // Cancel the current animation + cancelAnimation(); + + // Create the workspace animation. + // NOTE: this call apparently also sets the state for the workspace if !animated + Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( + toWorkspaceState, animated, layerViews); + + if (animated && initialized) { + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + + // Play the workspace animation + if (workspaceAnim != null) { + mStateAnimation.play(workspaceAnim); + } + + // hideAppsCustomizeHelper is called in some cases when it is already hidden + // don't perform all these no-op animations. In particularly, this was causing + // the all-apps button to pop in and out. + if (fromView.getVisibility() == View.VISIBLE) { + int width = revealView.getMeasuredWidth(); + int height = revealView.getMeasuredHeight(); + float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); + revealView.setVisibility(View.VISIBLE); + revealView.setAlpha(1f); + revealView.setTranslationY(0); + layerViews.put(revealView, BUILD_AND_SET_LAYER); + pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView); + + // Calculate the final animation values + final float revealViewToXDrift; + final float revealViewToYDrift; + if (material) { + revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView); + revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView); + } else { + revealViewToYDrift = 2 * height / 3; + revealViewToXDrift = 0; + } + + // The vertical motion of the apps panel should be delayed by one frame + // from the conceal animation in order to give the right feel. We correspondingly + // shorten the duration so that the slide and conceal end at the same time. + TimeInterpolator decelerateInterpolator = material ? + new LogDecelerateInterpolator(100, 0) : + new DecelerateInterpolator(1f); + ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY", + 0, revealViewToYDrift); + panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); + panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); + panelDriftY.setInterpolator(decelerateInterpolator); + mStateAnimation.play(panelDriftY); + + ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX", + 0, revealViewToXDrift); + panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); + panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); + panelDriftX.setInterpolator(decelerateInterpolator); + mStateAnimation.play(panelDriftX); + + // Setup animation for the reveal panel alpha + final float revealViewToAlpha = !material ? 0f : + pCb.getMaterialRevealViewFinalAlpha(revealView); + if (revealViewToAlpha != 1f) { + ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", + 1f, revealViewToAlpha); + panelAlpha.setDuration(material ? revealDuration : 150); + panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); + panelAlpha.setInterpolator(decelerateInterpolator); + mStateAnimation.play(panelAlpha); + } + + // Setup the animation for the content view + layerViews.put(contentView, BUILD_AND_SET_LAYER); + + // Create the individual animators + ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY", + 0, revealViewToYDrift); + contentView.setTranslationY(0); + pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); + pageDrift.setInterpolator(decelerateInterpolator); + pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); + mStateAnimation.play(pageDrift); + + contentView.setAlpha(1f); + ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f); + itemsAlpha.setDuration(100); + itemsAlpha.setInterpolator(decelerateInterpolator); + mStateAnimation.play(itemsAlpha); + + // Setup the page indicators animation + if (pageIndicatorsView != null) { + pageIndicatorsView.setAlpha(1f); + ObjectAnimator indicatorsAlpha = + LauncherAnimUtils.ofFloat(pageIndicatorsView, "alpha", 0f); + indicatorsAlpha.setDuration(revealDuration); + indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); + mStateAnimation.play(indicatorsAlpha); + } + + if (material) { + // Animate the all apps button + float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); + AnimatorListenerAdapter listener = + pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView); + Animator reveal = + LauncherAnimUtils.createCircularReveal(revealView, width / 2, + height / 2, revealRadius, finalRadius); + reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); + reveal.setDuration(revealDuration); + reveal.setStartDelay(itemsAlphaStagger); + if (listener != null) { + reveal.addListener(listener); + } + mStateAnimation.play(reveal); + } + + dispatchOnLauncherTransitionPrepare(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, true); + } + + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + fromView.setVisibility(View.GONE); + dispatchOnLauncherTransitionEnd(fromView, animated, true); + dispatchOnLauncherTransitionEnd(toView, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + // Animation complete callback + pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); + + // Disable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + + // Reset page transforms + if (contentView != null) { + contentView.setTranslationX(0); + contentView.setTranslationY(0); + contentView.setAlpha(1); + } + + // This can hold unnecessary references to views. + mStateAnimation = null; + } + }); + + final AnimatorSet stateAnimation = mStateAnimation; + final Runnable startAnimRunnable = new Runnable() { + public void run() { + // Check that mStateAnimation hasn't changed while + // we waited for a layout/draw pass + if (mStateAnimation != stateAnimation) + return; + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + + // Enable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + if (Utilities.isLmpOrAbove()) { + v.buildLayer(); + } + } + mStateAnimation.start(); + } + }; + fromView.post(startAnimRunnable); + } else { + fromView.setVisibility(View.GONE); + dispatchOnLauncherTransitionPrepare(fromView, animated, true); + dispatchOnLauncherTransitionStart(fromView, animated, true); + dispatchOnLauncherTransitionEnd(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, true); + dispatchOnLauncherTransitionStart(toView, animated, true); + dispatchOnLauncherTransitionEnd(toView, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + } + } + + + /** + * Dispatches the prepare-transition event to suitable views. + */ + void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated, + toWorkspace); + } + } + + /** + * Dispatches the start-transition event to suitable views. + */ + void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated, + toWorkspace); + } + + // Update the workspace transition step as well + dispatchOnLauncherTransitionStep(v, 0f); + } + + /** + * Dispatches the step-transition event to suitable views. + */ + void dispatchOnLauncherTransitionStep(View v, float t) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t); + } + } + + /** + * Dispatches the end-transition event to suitable views. + */ + void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated, + toWorkspace); + } + + // Update the workspace transition step as well + dispatchOnLauncherTransitionStep(v, 1f); + } + + /** + * Cancels the current animation. + */ + private void cancelAnimation() { + if (mStateAnimation != null) { + mStateAnimation.setDuration(0); + mStateAnimation.cancel(); + mStateAnimation = null; + } + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/PagedViewWithDraggableItems.java b/src/com/android/launcher3/PagedViewWithDraggableItems.java index 0e593698d..f0743cf1c 100644 --- a/src/com/android/launcher3/PagedViewWithDraggableItems.java +++ b/src/com/android/launcher3/PagedViewWithDraggableItems.java @@ -109,7 +109,7 @@ public abstract class PagedViewWithDraggableItems extends PagedView // Return early if we are still animating the pages if (mNextPage != INVALID_PAGE) return false; // When we have exited all apps or are in transition, disregard long clicks - if (!mLauncher.isAllAppsVisible() || + if (!mLauncher.isWidgetsViewVisible() || mLauncher.getWorkspace().isSwitchingState()) return false; // Return if global dragging is not enabled if (!mLauncher.isDraggingEnabled()) return false; diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index d963f2db9..312814039 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -131,8 +131,8 @@ public class WidgetPreviewLoader { private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache(); private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache(); - private final HashMap> mLoadedPreviews = new HashMap>(); - private final ArrayList> mUnusedBitmaps = new ArrayList>(); + private final HashMap> mLoadedPreviews = new HashMap<>(); + private final ArrayList> mUnusedBitmaps = new ArrayList<>(); private final Context mContext; private final int mAppIconSize; diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java new file mode 100644 index 000000000..d0dd733a6 --- /dev/null +++ b/src/com/android/launcher3/WidgetsContainerView.java @@ -0,0 +1,88 @@ +package com.android.launcher3; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +class SectionedWidgetsRow { + String section; + List> widgets; + + public SectionedWidgetsRow(String sc) { + section = sc; + } +} + +class SectionedWidgetsAlgorithm { + public List computeSectionedWidgetRows(List sortedWidgets, + int widgetsPerRow) { + List rows = new ArrayList<>(); + LinkedHashMap> sections = computeSectionedApps(sortedWidgets); + for (Map.Entry> sectionEntry : sections.entrySet()) { + String section = sectionEntry.getKey(); + SectionedWidgetsRow row = new SectionedWidgetsRow(section); + List widgets = sectionEntry.getValue(); + int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow); + for (int i = 0; i < numRows; i++) { + List widgetsInRow = new ArrayList<>(); + int offset = i * widgetsPerRow; + for (int j = 0; j < widgetsPerRow; j++) { + widgetsInRow.add(widgets.get(offset + j)); + } + row.widgets.add(widgetsInRow); + } + } + return rows; + } + + private LinkedHashMap> computeSectionedApps(List sortedWidgets) { + LinkedHashMap> sections = new LinkedHashMap<>(); + for (Object info : sortedWidgets) { + String section = getSection(info); + List sectionedWidgets = sections.get(section); + if (sectionedWidgets == null) { + sectionedWidgets = new ArrayList<>(); + sections.put(section, sectionedWidgets); + } + sectionedWidgets.add(info); + } + return sections; + } + + private String getSection(Object widgetOrShortcut) { + return "UNKNOWN"; + } +} + +/** + * The widgets list view container. + */ +public class WidgetsContainerView extends FrameLayout { + + + public WidgetsContainerView(Context context) { + this(context, null); + } + + public WidgetsContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + } +} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 3b293f95b..a59e25e08 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1528,7 +1528,7 @@ public class Workspace extends SmoothPagedView @Override public void announceForAccessibility(CharSequence text) { // Don't announce if apps is on top of us. - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isAppsViewVisible()) { super.announceForAccessibility(text); } } @@ -1816,7 +1816,7 @@ public class Workspace extends SmoothPagedView @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isAppsViewVisible()) { final Folder openFolder = getOpenFolder(); if (openFolder != null) { return openFolder.requestFocus(direction, previouslyFocusedRect); @@ -1837,7 +1837,7 @@ public class Workspace extends SmoothPagedView @Override public void addFocusables(ArrayList views, int direction, int focusableMode) { - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isAppsViewVisible()) { final Folder openFolder = getOpenFolder(); if (openFolder != null) { openFolder.addFocusables(views, direction); @@ -2046,8 +2046,7 @@ public class Workspace extends SmoothPagedView // If this is a text view, use its drawable instead if (v instanceof TextView) { - TextView tv = (TextView) v; - Drawable d = tv.getCompoundDrawables()[1]; + Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); bmpWidth = bounds.width(); bmpHeight = bounds.height(); @@ -2348,7 +2347,7 @@ public class Workspace extends SmoothPagedView cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); } else { if (layerViews != null) { - layerViews.put(cl, Launcher.BUILD_LAYER); + layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); } if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { LauncherViewPropertyAnimator alphaAnim = @@ -2400,8 +2399,8 @@ public class Workspace extends SmoothPagedView if (layerViews != null) { // If layerViews is not null, we add these views, and indicate that // the caller can manage layer state. - layerViews.put(hotseat, Launcher.BUILD_AND_SET_LAYER); - layerViews.put(overviewPanel, Launcher.BUILD_AND_SET_LAYER); + layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); } else { // Otherwise let the animator handle layer management. hotseatAlpha.withLayer(); @@ -2430,7 +2429,7 @@ public class Workspace extends SmoothPagedView if (layerViews != null) { // If layerViews is not null, we add these views, and indicate that // the caller can manage layer state. - layerViews.put(searchBar, Launcher.BUILD_AND_SET_LAYER); + layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); } else { // Otherwise let the animator handle layer management. searchBarAlpha.withLayer(); @@ -2583,6 +2582,19 @@ public class Workspace extends SmoothPagedView return this; } + /** + * Returns the drawable for the given text view. + */ + public static Drawable getTextViewIcon(TextView tv) { + final Drawable[] drawables = tv.getCompoundDrawables(); + for (int i = 0; i < drawables.length; i++) { + if (drawables[i] != null) { + return drawables[i]; + } + } + return null; + } + /** * Draw the View v into the given Canvas. * @@ -2598,7 +2610,7 @@ public class Workspace extends SmoothPagedView destCanvas.save(); if (v instanceof TextView) { - Drawable d = ((TextView) v).getCompoundDrawables()[1]; + Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding); destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top); @@ -2635,7 +2647,7 @@ public class Workspace extends SmoothPagedView int padding = expectedPadding.get(); if (v instanceof TextView) { - Drawable d = ((TextView) v).getCompoundDrawables()[1]; + Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); b = Bitmap.createBitmap(bounds.width() + padding, bounds.height() + padding, Bitmap.Config.ARGB_8888); @@ -2716,11 +2728,12 @@ public class Workspace extends SmoothPagedView beginDragShared(child, this, accessible); } - public void beginDragShared(View child, DragSource source) { - beginDragShared(child, source, false); + public void beginDragShared(View child, DragSource source, boolean accessible) { + beginDragShared(child, new Point(), source, accessible); } - public void beginDragShared(View child, DragSource source, boolean accessible) { + public void beginDragShared(View child, Point relativeTouchPos, DragSource source, + boolean accessible) { child.clearFocus(); child.setPressed(false); @@ -2745,11 +2758,23 @@ public class Workspace extends SmoothPagedView Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { + BubbleTextView icon = (BubbleTextView) child; int iconSize = grid.iconSizePx; int top = child.getPaddingTop(); int left = (bmpWidth - iconSize) / 2; int right = left + iconSize; int bottom = top + iconSize; + if (icon.isLayoutHorizontal()) { + // If the layout is horizontal, then if we are just picking up the icon, then just + // use the child position since the icon is top-left aligned. Otherwise, offset + // the drag layer position horizontally so that the icon is under the current + // touch position. + if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) { + dragLayerX = Math.round(mTempXY[0]); + } else { + dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2)); + } + } dragLayerY += top; // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. @@ -2831,18 +2856,6 @@ public class Workspace extends SmoothPagedView tmpB.recycle(); } - void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId, - int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) { - View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info); - - final int[] cellXY = new int[2]; - target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); - addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst); - - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0], - cellXY[1]); - } - public boolean transitionStateShouldAllowDrop() { return ((!isSwitchingState() || mTransitionProgress > 0.5f) && (mState == State.NORMAL || mState == State.SPRING_LOADED)); @@ -4769,7 +4782,7 @@ public class Workspace extends SmoothPagedView updates.contains(info)) { ShortcutInfo si = (ShortcutInfo) info; BubbleTextView shortcut = (BubbleTextView) v; - boolean oldPromiseState = shortcut.getCompoundDrawables()[1] + boolean oldPromiseState = getTextViewIcon(shortcut) instanceof PreloadIconDrawable; shortcut.applyFromShortcutInfo(si, mIconCache, true, si.isPromise() != oldPromiseState); -- cgit v1.2.3 From c4918358049914a4700bc8f2eb91167eb4a67bec Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Mar 2015 18:15:48 -0700 Subject: Adding a dummy method for fix GSA compilation issues Change-Id: Iec0ab4faf695b9efd3efd3d3b83d729f430a59eb --- src/com/android/launcher3/Folder.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 0a6557907..87ef0bcb4 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -39,6 +39,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; @@ -849,6 +850,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mItemsInvalidated = true; } + // TODO remove this once GSA code fix is submitted + public ViewGroup getContent() { + return (ViewGroup) mContent; + } + public int getItemCount() { return mContent.getItemCount(); } -- cgit v1.2.3 From 70a48befe48af0a318e88745f480d2ed42936bb5 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 11 Mar 2015 16:36:52 -0700 Subject: Fix widget refresh issue on package install/uninstall Culprit CL: https://googleplex-android-review.git.corp.google.com/#/c/592303/3/src/com/android/launcher3/LauncherModel.java b/19658229 Change-Id: I02b0b2a0ed9bc3200bbe1edcb251cf0efe939e53 --- src/com/android/launcher3/Launcher.java | 2 +- src/com/android/launcher3/LauncherAppWidgetHost.java | 3 ++- src/com/android/launcher3/LauncherModel.java | 15 +++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9c4632cc8..bf03f745b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4103,7 +4103,7 @@ public class Launcher extends Activity } if (mAppsCustomizeContent != null) { mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this)); + LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */)); } if (mLauncherCallbacks != null) { mLauncherCallbacks.bindAllApplications(apps); diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 840508a1d..a28fd255a 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -81,7 +81,8 @@ public class LauncherAppWidgetHost extends AppWidgetHost { protected void onProvidersChanged() { // Once we get the message that widget packages are updated, we need to rebind items // in AppsCustomize accordingly. - mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher)); + mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher, + true /* refresh */)); for (Runnable callback : mProviderChangeListeners) { callback.run(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3cada6fb8..2fd9db2c3 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -167,7 +167,6 @@ public class LauncherModel extends BroadcastReceiver // sBgWidgetProviders is the set of widget providers including custom internal widgets public static HashMap sBgWidgetProviders; - public static boolean sWidgetProvidersDirty; // sPendingPackages is a set of packages which could be on sdcard and are not available yet static final HashMap> sPendingPackages = @@ -3252,7 +3251,7 @@ public class LauncherModel extends BroadcastReceiver } final ArrayList widgetsAndShortcuts = - getSortedWidgetsAndShortcuts(context); + getSortedWidgetsAndShortcuts(context, true /* refresh */); mHandler.post(new Runnable() { @Override public void run() { @@ -3275,9 +3274,10 @@ public class LauncherModel extends BroadcastReceiver } } - public static List getWidgetProviders(Context context) { + public static List getWidgetProviders(Context context, + boolean refresh) { synchronized (sBgLock) { - if (sBgWidgetProviders != null && !sWidgetProvidersDirty) { + if (sBgWidgetProviders != null && !refresh) { return new ArrayList(sBgWidgetProviders.values()); } sBgWidgetProviders = new HashMap(); @@ -3294,7 +3294,6 @@ public class LauncherModel extends BroadcastReceiver info = new LauncherAppWidgetProviderInfo(context, widget); sBgWidgetProviders.put(info.provider, info); } - sWidgetProvidersDirty = false; return new ArrayList(sBgWidgetProviders.values()); } } @@ -3302,17 +3301,17 @@ public class LauncherModel extends BroadcastReceiver public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name) { synchronized (sBgLock) { if (sBgWidgetProviders == null) { - getWidgetProviders(ctx); + getWidgetProviders(ctx, false /* refresh */); } return sBgWidgetProviders.get(name); } } // Returns a list of ResolveInfos/AppWidgetInfos in sorted order - public static ArrayList getSortedWidgetsAndShortcuts(Context context) { + public static ArrayList getSortedWidgetsAndShortcuts(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); - widgetsAndShortcuts.addAll(getWidgetProviders(context)); + widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context)); -- cgit v1.2.3 From fe1dcbf0c5a1767e077a051ef5f7b72540d55f62 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 11 Mar 2015 16:36:52 -0700 Subject: Fix widget refresh issue on package install/uninstall Culprit CL: https://googleplex-android-review.git.corp.google.com/#/c/592303/3/src/com/android/launcher3/LauncherModel.java b/19658229 Change-Id: I02b0b2a0ed9bc3200bbe1edcb251cf0efe939e53 --- src/com/android/launcher3/Launcher.java | 4 ++-- src/com/android/launcher3/LauncherAppWidgetHost.java | 3 ++- src/com/android/launcher3/LauncherModel.java | 17 ++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index cd861d4e0..a7e32d31a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4608,13 +4608,13 @@ public class Launcher extends Activity } if (mAppsCustomizeContent != null) { mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this)); + LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */)); } } else { if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this)); + LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */)); } } if (mLauncherCallbacks != null) { diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 840508a1d..a28fd255a 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -81,7 +81,8 @@ public class LauncherAppWidgetHost extends AppWidgetHost { protected void onProvidersChanged() { // Once we get the message that widget packages are updated, we need to rebind items // in AppsCustomize accordingly. - mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher)); + mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher, + true /* refresh */)); for (Runnable callback : mProviderChangeListeners) { callback.run(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 9f976318d..c39bcee09 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -175,7 +175,6 @@ public class LauncherModel extends BroadcastReceiver // sBgWidgetProviders is the set of widget providers including custom internal widgets public static HashMap sBgWidgetProviders; - public static boolean sWidgetProvidersDirty; // sPendingPackages is a set of packages which could be on sdcard and are not available yet static final HashMap> sPendingPackages = @@ -3319,7 +3318,7 @@ public class LauncherModel extends BroadcastReceiver } final ArrayList widgetsAndShortcuts = - getSortedWidgetsAndShortcuts(context); + getSortedWidgetsAndShortcuts(context, true /* refresh */); mHandler.post(new Runnable() { @Override public void run() { @@ -3342,9 +3341,10 @@ public class LauncherModel extends BroadcastReceiver } } - public static List getWidgetProviders(Context context) { + public static List getWidgetProviders(Context context, + boolean refresh) { synchronized (sBgLock) { - if (sBgWidgetProviders != null && !sWidgetProvidersDirty) { + if (sBgWidgetProviders != null && !refresh) { return new ArrayList(sBgWidgetProviders.values()); } sBgWidgetProviders = new HashMap(); @@ -3361,7 +3361,6 @@ public class LauncherModel extends BroadcastReceiver info = new LauncherAppWidgetProviderInfo(context, widget); sBgWidgetProviders.put(info.provider, info); } - sWidgetProvidersDirty = false; return new ArrayList(sBgWidgetProviders.values()); } } @@ -3369,17 +3368,17 @@ public class LauncherModel extends BroadcastReceiver public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name) { synchronized (sBgLock) { if (sBgWidgetProviders == null) { - getWidgetProviders(ctx); + getWidgetProviders(ctx, false /* refresh */); } return sBgWidgetProviders.get(name); } } - // Returns a list of ResolveInfos/AppWindowInfos in sorted order - public static ArrayList getSortedWidgetsAndShortcuts(Context context) { + // Returns a list of ResolveInfos/AppWidgetInfos in sorted order + public static ArrayList getSortedWidgetsAndShortcuts(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); - widgetsAndShortcuts.addAll(getWidgetProviders(context)); + widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context)); -- cgit v1.2.3 From 93f98eaf1800024cb2f28379bdd997f3debae63a Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 10 Mar 2015 16:28:47 -0700 Subject: Adding app grid layout with fastscroller. - Adding filtering and using alphabetic index for app grouping. Change-Id: I745b644fa8f90f5ff24a8642ac377ef1c65d8aff --- Android.mk | 3 + res/drawable/apps_list_fastscroll_bg.xml | 24 + res/drawable/apps_list_scrollbar_thumb.xml | 21 + res/drawable/apps_list_search_bg.xml | 23 + res/layout-sw600dp/apps_view.xml | 8 +- res/layout/apps_grid_row_icon_view.xml | 13 +- res/layout/apps_grid_row_view.xml | 38 -- res/layout/apps_list_reveal_view.xml | 2 +- res/layout/apps_list_row_icon_view.xml | 5 +- res/layout/apps_list_row_view.xml | 7 +- res/layout/apps_list_view.xml | 41 +- res/layout/apps_view.xml | 2 +- res/values-sw600dp/dimens.xml | 1 + res/values/attrs.xml | 1 + res/values/colors.xml | 4 + res/values/dimens.xml | 6 + res/values/strings.xml | 4 + .../android/launcher3/AlphabeticalAppsList.java | 242 +++++++++ .../launcher3/AppsContainerRecyclerView.java | 271 ++++++++++ src/com/android/launcher3/AppsContainerView.java | 564 ++++++--------------- src/com/android/launcher3/AppsGridAdapter.java | 206 ++++++++ src/com/android/launcher3/AppsListAdapter.java | 119 +++++ src/com/android/launcher3/BubbleTextView.java | 9 +- src/com/android/launcher3/DeviceProfile.java | 28 +- .../launcher3/compat/AlphabeticIndexCompat.java | 131 +++++ 25 files changed, 1295 insertions(+), 478 deletions(-) create mode 100644 res/drawable/apps_list_fastscroll_bg.xml create mode 100644 res/drawable/apps_list_scrollbar_thumb.xml create mode 100644 res/drawable/apps_list_search_bg.xml delete mode 100644 res/layout/apps_grid_row_view.xml create mode 100644 src/com/android/launcher3/AlphabeticalAppsList.java create mode 100644 src/com/android/launcher3/AppsContainerRecyclerView.java create mode 100644 src/com/android/launcher3/AppsGridAdapter.java create mode 100644 src/com/android/launcher3/AppsListAdapter.java create mode 100644 src/com/android/launcher3/compat/AlphabeticIndexCompat.java diff --git a/Android.mk b/Android.mk index 5267469b3..385380861 100644 --- a/Android.mk +++ b/Android.mk @@ -37,6 +37,9 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ LOCAL_SDK_VERSION := 21 +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-recyclerview LOCAL_PACKAGE_NAME := Launcher3 #LOCAL_CERTIFICATE := shared diff --git a/res/drawable/apps_list_fastscroll_bg.xml b/res/drawable/apps_list_fastscroll_bg.xml new file mode 100644 index 000000000..4ec18488b --- /dev/null +++ b/res/drawable/apps_list_fastscroll_bg.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable/apps_list_scrollbar_thumb.xml b/res/drawable/apps_list_scrollbar_thumb.xml new file mode 100644 index 000000000..ddd65b231 --- /dev/null +++ b/res/drawable/apps_list_scrollbar_thumb.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/apps_list_search_bg.xml b/res/drawable/apps_list_search_bg.xml new file mode 100644 index 000000000..eda33a918 --- /dev/null +++ b/res/drawable/apps_list_search_bg.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml index 1f773b307..3bb6ec505 100644 --- a/res/layout-sw600dp/apps_view.xml +++ b/res/layout-sw600dp/apps_view.xml @@ -23,12 +23,12 @@ android:descendantFocusability="afterDescendants"> \ No newline at end of file diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml index 11c8eeb4d..81e74b985 100644 --- a/res/layout/apps_grid_row_icon_view.xml +++ b/res/layout/apps_grid_row_icon_view.xml @@ -13,10 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. --> - + android:background="@drawable/focusable_view_bg" + launcher:deferShadowGeneration="true" /> + diff --git a/res/layout/apps_grid_row_view.xml b/res/layout/apps_grid_row_view.xml deleted file mode 100644 index bce43bc1b..000000000 --- a/res/layout/apps_grid_row_view.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - \ No newline at end of file diff --git a/res/layout/apps_list_reveal_view.xml b/res/layout/apps_list_reveal_view.xml index 4a26787c8..19e462bee 100644 --- a/res/layout/apps_list_reveal_view.xml +++ b/res/layout/apps_list_reveal_view.xml @@ -15,7 +15,7 @@ --> + launcher:layoutHorizontal="true" + launcher:deferShadowGeneration="true" /> diff --git a/res/layout/apps_list_row_view.xml b/res/layout/apps_list_row_view.xml index c4dcd0018..83c175bb8 100644 --- a/res/layout/apps_list_row_view.xml +++ b/res/layout/apps_list_row_view.xml @@ -26,9 +26,8 @@ android:layout_width="64dp" android:layout_height="match_parent" android:paddingLeft="16dp" - android:gravity="left|center_vertical" - android:textColor="#009688" - android:textSize="24sp" - android:textAllCaps="true" + android:gravity="start|center_vertical" + android:textColor="@color/apps_view_section_text_color" + android:textSize="@dimen/apps_view_section_text_size" android:focusable="false" /> \ No newline at end of file diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index b1b0f310b..dfb2fc42f 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -13,18 +13,41 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + android:visibility="gone"> + + + \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml index 19ad3d2c9..c1bae63f6 100644 --- a/res/layout/apps_view.xml +++ b/res/layout/apps_view.xml @@ -19,7 +19,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" - android:background="#22000000" + android:background="@drawable/apps_customize_bg" android:descendantFocusability="afterDescendants"> diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index f7ad0c4cd..d9075872a 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -18,6 +18,7 @@ 64dp + 480dp 76dp diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 4e7c59280..845b18230 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -24,6 +24,7 @@ + diff --git a/res/values/colors.xml b/res/values/colors.xml index 2daf9fe12..590a8872b 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -36,4 +36,8 @@ #FFFFFFFF #FF374248 + + #009688 + #009688 + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 013bd925b..9b4c56094 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -47,7 +47,13 @@ 12dip + 0dp + 52dp 64dp + 24sp + 48dp + 64dp + 48dp Choose widget to create + + + Search Apps + diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java new file mode 100644 index 000000000..2847afc89 --- /dev/null +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -0,0 +1,242 @@ +package com.android.launcher3; + +import android.content.ComponentName; +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import com.android.launcher3.compat.AlphabeticIndexCompat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + + +/** + * The alphabetically sorted list of applications. + */ +public class AlphabeticalAppsList { + + /** + * Info about a section in the alphabetic list + */ + public class SectionInfo { + public String sectionName; + public int numAppsInSection; + } + + /** + * A filter interface to limit the set of applications in the apps list. + */ + public interface Filter { + public boolean retainApp(AppInfo info); + } + + // Hack to force RecyclerView to break sections + public static final AppInfo SECTION_BREAK_INFO = null; + + private List mApps = new ArrayList<>(); + private List mFilteredApps = new ArrayList<>(); + private List mSectionedFilteredApps = new ArrayList<>(); + private List mSections = new ArrayList<>(); + private RecyclerView.Adapter mAdapter; + private Filter mFilter; + private AlphabeticIndexCompat mIndexer; + + public AlphabeticalAppsList(Context context) { + mIndexer = new AlphabeticIndexCompat(context); + } + + /** + * Sets the adapter to notify when this dataset changes. + */ + public void setAdapter(RecyclerView.Adapter adapter) { + mAdapter = adapter; + } + + /** + * Returns sections of all the current filtered applications. + */ + public List getSections() { + return mSections; + } + + /** + * Returns the current filtered list of applications broken down into their sections. + */ + public List getApps() { + return mSectionedFilteredApps; + } + + /** + * Returns the current filtered list of applications. + */ + public List getAppsWithoutSectionBreaks() { + return mFilteredApps; + } + + /** + * Returns the section name for the application. + */ + public String getSectionNameForApp(AppInfo info) { + String title = info.title.toString(); + String sectionName = mIndexer.getBucketLabel(mIndexer.getBucketIndex(title)); + return sectionName; + } + + /** + * Returns the indexer for this locale. + */ + public AlphabeticIndexCompat getIndexer() { + return mIndexer; + } + + /** + * Sets the current filter for this list of apps. + */ + public void setFilter(Filter f) { + mFilter = f; + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + Collections.sort(apps, LauncherModel.getAppNameComparator()); + mApps.clear(); + mApps.addAll(apps); + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + // We add it in place, in alphabetical order + for (AppInfo info : apps) { + addApp(info); + } + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + for (AppInfo info : apps) { + int index = mApps.indexOf(info); + if (index != -1) { + mApps.set(index, info); + onAppsUpdated(); + mAdapter.notifyItemChanged(index); + } else { + addApp(info); + } + } + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + for (AppInfo info : apps) { + int removeIndex = findAppByComponent(mApps, info); + if (removeIndex != -1) { + int sectionedIndex = mSectionedFilteredApps.indexOf(info); + int numAppsInSection = numAppsInSection(info); + mApps.remove(removeIndex); + onAppsUpdated(); + if (numAppsInSection == 1) { + // Remove the section and the icon + mAdapter.notifyItemRemoved(sectionedIndex - 1); + mAdapter.notifyItemRemoved(sectionedIndex - 1); + } else { + mAdapter.notifyItemRemoved(sectionedIndex); + } + } + } + } + + /** + * Finds the index of an app given a target AppInfo. + */ + private int findAppByComponent(List apps, AppInfo targetInfo) { + ComponentName targetComponent = targetInfo.intent.getComponent(); + int length = apps.size(); + for (int i = 0; i < length; ++i) { + AppInfo info = apps.get(i); + if (info.user.equals(info.user) + && info.intent.getComponent().equals(targetComponent)) { + return i; + } + } + return -1; + } + + /** + * Implementation to actually add an app to the alphabetic list + */ + private void addApp(AppInfo info) { + Comparator appNameComparator = LauncherModel.getAppNameComparator(); + int index = Collections.binarySearch(mApps, info, appNameComparator); + if (index < 0) { + mApps.add(-(index + 1), info); + onAppsUpdated(); + + int sectionedIndex = mSectionedFilteredApps.indexOf(info); + int numAppsInSection = numAppsInSection(info); + if (numAppsInSection == 1) { + // New section added along with icon + mAdapter.notifyItemInserted(sectionedIndex - 1); + mAdapter.notifyItemInserted(sectionedIndex - 1); + } else { + mAdapter.notifyItemInserted(sectionedIndex); + } + } + } + + /** + * Returns the number of apps in the section that the given info is in. + */ + private int numAppsInSection(AppInfo info) { + int appIndex = mFilteredApps.indexOf(info); + int appCount = 0; + for (SectionInfo section : mSections) { + if (appCount + section.numAppsInSection > appIndex) { + return section.numAppsInSection; + } + appCount += section.numAppsInSection; + } + return 1; + } + + /** + * Updates internals when the set of apps are updated. + */ + private void onAppsUpdated() { + // Recreate the filtered apps + mFilteredApps.clear(); + for (AppInfo info : mApps) { + if (mFilter == null || mFilter.retainApp(info)) { + mFilteredApps.add(info); + } + } + + // Section the apps (for convenience for the grid layout) + mSections.clear(); + mSectionedFilteredApps.clear(); + SectionInfo lastSectionInfo = null; + for (AppInfo info : mFilteredApps) { + String sectionName = getSectionNameForApp(info); + if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) { + lastSectionInfo = new SectionInfo(); + lastSectionInfo.sectionName = sectionName; + mSectionedFilteredApps.add(SECTION_BREAK_INFO); + mSections.add(lastSectionInfo); + } + lastSectionInfo.numAppsInSection++; + mSectionedFilteredApps.add(info); + } + } +} diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java new file mode 100644 index 000000000..2280e99ef --- /dev/null +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -0,0 +1,271 @@ +/* + * 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.animation.ObjectAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import java.util.List; + +/** + * A RecyclerView with custom fastscroll support. This is the main container for the all apps + * icons. + */ +public class AppsContainerRecyclerView extends RecyclerView + implements RecyclerView.OnItemTouchListener { + + private AlphabeticalAppsList mApps; + private int mNumAppsPerRow; + + private Drawable mFastScrollerBg; + private boolean mDraggingFastScroller; + private String mFastScrollSectionName; + private Paint mFastScrollTextPaint; + private Rect mFastScrollTextBounds = new Rect(); + private float mFastScrollAlpha; + private int mDownX; + private int mDownY; + private int mLastX; + private int mLastY; + private int mGutterSize; + + public AppsContainerRecyclerView(Context context) { + this(context, null); + } + + public AppsContainerRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr); + + Resources res = context.getResources(); + int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size); + mFastScrollerBg = res.getDrawable(R.drawable.apps_list_fastscroll_bg); + mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); + mFastScrollTextPaint = new Paint(); + mFastScrollTextPaint.setColor(Color.WHITE); + mFastScrollTextPaint.setAntiAlias(true); + mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.apps_view_fast_scroll_text_size)); + mGutterSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_gutter_size); + setFastScrollerAlpha(getFastScrollerAlpha()); + } + + /** + * Sets the list of apps in this view, used to determine the fastscroll position. + */ + public void setApps(AlphabeticalAppsList apps) { + mApps = apps; + } + + /** + * Sets the number of apps per row in this recycler view. + */ + public void setNumAppsPerRow(int rowSize) { + mNumAppsPerRow = rowSize; + } + + /** + * Sets the fast scroller alpha. + */ + public void setFastScrollerAlpha(float alpha) { + mFastScrollAlpha = alpha; + invalidateFastScroller(); + } + + /** + * Gets the fast scroller alpha. + */ + public float getFastScrollerAlpha() { + return mFastScrollAlpha; + } + + @Override + protected void onFinishInflate() { + addOnItemTouchListener(this); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + if (mFastScrollAlpha > 0f) { + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + Rect bgBounds = mFastScrollerBg.getBounds(); + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + int x; + if (isRtl) { + x = getPaddingLeft() + getScrollBarSize(); + } else { + x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); + } + int y = mLastY - bgBounds.height() / 2; + y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - + bgBounds.height())); + canvas.translate(x, y); + mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollerBg.draw(canvas); + mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, + mFastScrollSectionName.length(), mFastScrollTextBounds); + canvas.drawText(mFastScrollSectionName, + (bgBounds.width() - mFastScrollTextBounds.width()) / 2, + bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2, + mFastScrollTextPaint); + canvas.restoreToCount(restoreCount); + } + } + + /** + * We intercept the touch handling only to support fast scrolling when initiated from the + * gutter. Otherwise, we fall back to the default RecyclerView touch handling. + */ + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { + return handleTouchEvent(ev); + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent ev) { + handleTouchEvent(ev); + } + + /** + * Handles the touch event and determines whether to show the fast scroller (or updates it if + * it is already showing). + */ + private boolean handleTouchEvent(MotionEvent ev) { + ViewConfiguration config = ViewConfiguration.get(getContext()); + + int action = ev.getAction(); + int x = (int) ev.getX(); + int y = (int) ev.getY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + // Keep track of the down positions + mDownX = mLastX = x; + mDownY = mLastY = y; + stopScroll(); + break; + case MotionEvent.ACTION_MOVE: + // Check if we are scrolling + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + boolean isInGutter; + if (isRtl) { + isInGutter = mDownX < mGutterSize; + } else { + isInGutter = mDownX >= (getWidth() - mGutterSize); + } + if (!mDraggingFastScroller && isInGutter && + Math.abs(y - mDownY) > config.getScaledTouchSlop()) { + getParent().requestDisallowInterceptTouchEvent(true); + mDraggingFastScroller = true; + animateFastScrollerVisibility(true); + } + if (mDraggingFastScroller) { + mLastX = x; + mLastY = y; + + // Scroll to the right position, and update the section name + int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); + int bottom = getHeight() - getPaddingBottom() - + (mFastScrollerBg.getBounds().height() / 2); + float boundedY = (float) Math.max(top, Math.min(bottom, y)); + mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / + (bottom - top)); + invalidateFastScroller(); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mDraggingFastScroller = false; + animateFastScrollerVisibility(false); + break; + } + return mDraggingFastScroller; + + } + + /** + * Animates the visibility of the fast scroller popup. + */ + private void animateFastScrollerVisibility(boolean visible) { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); + anim.setDuration(visible ? 200 : 150); + anim.start(); + } + + /** + * Invalidates the fast scroller popup. + */ + private void invalidateFastScroller() { + invalidate(getWidth() - getPaddingRight() - getScrollBarSize() - + mFastScrollerBg.getIntrinsicWidth(), 0, getWidth(), getHeight()); + } + + /** + * Maps the progress (from 0..1) to the position that should be visible + */ + private String scrollToPositionAtProgress(float progress) { + List sections = mApps.getSections(); + // Get the total number of rows + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); + rowCount += numRowsInSection; + } + + // Find the index of the first app in that row and scroll to that position + int rowAtProgress = (int) (progress * rowCount); + int appIndex = 0; + rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); + if (rowCount + numRowsInSection > rowAtProgress) { + appIndex += (rowAtProgress - rowCount) * mNumAppsPerRow; + break; + } + rowCount += numRowsInSection; + appIndex += info.numAppsInSection; + } + appIndex = Math.max(0, Math.min(mApps.getAppsWithoutSectionBreaks().size() - 1, appIndex)); + AppInfo appInfo = mApps.getAppsWithoutSectionBreaks().get(appIndex); + int sectionedAppIndex = mApps.getApps().indexOf(appInfo); + scrollToPosition(sectionedAppIndex); + + // Returns the section name of the row + return mApps.getSectionNameForApp(appInfo); + } +} diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index cabacec3c..cc31e20fa 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -1,409 +1,64 @@ +/* + * 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.ComponentName; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.SectionIndexer; import android.widget.TextView; +import com.android.launcher3.compat.AlphabeticIndexCompat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; - - -/** - * Represents a row in the apps list view. - */ -class AppsRow { - int sectionId; - String sectionDescription; - List apps; - - public AppsRow(int sId, String sc, List ai) { - sectionId = sId; - sectionDescription = sc; - apps = ai; - } - - public AppsRow(int sId, List ai) { - sectionId = sId; - apps = ai; - } -} - -/** - * An interface to an algorithm that generates app rows. - */ -interface AppRowAlgorithm { - public List computeAppRows(List sortedApps, int appsPerRow); - public int getIconViewLayoutId(); - public int getRowViewLayoutId(); - public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info); -} - -/** - * Computes the rows in the apps list view. - */ -class SectionedAppsAlgorithm implements AppRowAlgorithm { - - @Override - public List computeAppRows(List sortedApps, int appsPerRow) { - List rows = new ArrayList<>(); - LinkedHashMap> sections = computeSectionedApps(sortedApps); - int sectionId = 0; - for (Map.Entry> sectionEntry : sections.entrySet()) { - String section = sectionEntry.getKey(); - List apps = sectionEntry.getValue(); - int numRows = (int) Math.ceil((float) apps.size() / appsPerRow); - for (int i = 0; i < numRows; i++) { - List appsInRow = new ArrayList<>(); - int offset = i * appsPerRow; - for (int j = 0; j < appsPerRow; j++) { - if (offset + j < apps.size()) { - appsInRow.add(apps.get(offset + j)); - } - } - if (i == 0) { - rows.add(new AppsRow(sectionId, section, appsInRow)); - } else { - rows.add(new AppsRow(sectionId, appsInRow)); - } - } - sectionId++; - } - return rows; - } - - @Override - public int getIconViewLayoutId() { - return R.layout.apps_grid_row_icon_view; - } - - @Override - public int getRowViewLayoutId() { - return R.layout.apps_grid_row_view; - } - - private LinkedHashMap> computeSectionedApps(List sortedApps) { - LinkedHashMap> sections = new LinkedHashMap<>(); - for (AppInfo info : sortedApps) { - String section = getSection(info); - List sectionApps = sections.get(section); - if (sectionApps == null) { - sectionApps = new ArrayList<>(); - sections.put(section, sectionApps); - } - sectionApps.add(info); - } - return sections; - } - - @Override - public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) { - icon.applyFromApplicationInfo(info); - } - - private String getSection(AppInfo app) { - return app.title.toString().substring(0, 1).toLowerCase(); - } -} - -/** - * Computes the rows in the apps grid view. - */ -class ListedAppsAlgorithm implements AppRowAlgorithm { - - @Override - public List computeAppRows(List sortedApps, int appsPerRow) { - List rows = new ArrayList<>(); - int sectionId = -1; - String prevSection = ""; - for (AppInfo info : sortedApps) { - List appsInRow = new ArrayList<>(); - appsInRow.add(info); - String section = getSection(info); - if (!prevSection.equals(section)) { - prevSection = section; - sectionId++; - rows.add(new AppsRow(sectionId, section, appsInRow)); - } else { - rows.add(new AppsRow(sectionId, appsInRow)); - } - } - return rows; - } - - @Override - public int getIconViewLayoutId() { - return R.layout.apps_list_row_icon_view; - } - - @Override - public int getRowViewLayoutId() { - return R.layout.apps_list_row_view; - } - - @Override - public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) { - icon.applyFromApplicationInfo(info); - } - - private String getSection(AppInfo app) { - return app.title.toString().substring(0, 1).toLowerCase(); - } -} - -/** - * The adapter of all the apps - */ -class AppsListAdapter extends BaseAdapter implements SectionIndexer { - - private LayoutInflater mLayoutInflater; - private List mAppRows = new ArrayList<>(); - private View.OnTouchListener mTouchListener; - private View.OnClickListener mIconClickListener; - private View.OnLongClickListener mIconLongClickListener; - private AppRowAlgorithm mRowAlgorithm; - private int mAppsPerRow; - - public AppsListAdapter(Context context, View.OnTouchListener touchListener, - View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) { - mLayoutInflater = LayoutInflater.from(context); - mTouchListener = touchListener; - mIconClickListener = iconClickListener; - mIconLongClickListener = iconLongClickListener; - } - - void setApps(List apps, int appsPerRow, AppRowAlgorithm algo) { - mAppsPerRow = appsPerRow; - mRowAlgorithm = algo; - mAppRows.clear(); - mAppRows.addAll(apps); - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return mAppRows.size(); - } - - @Override - public Object getItem(int position) { - return mAppRows.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - AppsRow info = mAppRows.get(position); - ViewGroup row = (ViewGroup) convertView; - if (row == null) { - // Inflate the row and all the icon children necessary - row = (ViewGroup) mLayoutInflater.inflate(mRowAlgorithm.getRowViewLayoutId(), - parent, false); - for (int i = 0; i < mAppsPerRow; i++) { - BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - mRowAlgorithm.getIconViewLayoutId(), row, false); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, 1); - lp.gravity = Gravity.CENTER_VERTICAL; - icon.setLayoutParams(lp); - icon.setOnTouchListener(mTouchListener); - icon.setOnClickListener(mIconClickListener); - icon.setOnLongClickListener(mIconLongClickListener); - icon.setFocusable(true); - row.addView(icon); - } - } - // Bind the section header - TextView tv = (TextView) row.findViewById(R.id.section); - if (info.sectionDescription != null) { - tv.setText(info.sectionDescription); - tv.setVisibility(View.VISIBLE); - } else { - tv.setVisibility(View.INVISIBLE); - } - // Bind the icons - for (int i = 0; i < mAppsPerRow; i++) { - BubbleTextView icon = (BubbleTextView) row.getChildAt(i + 1); - if (i < info.apps.size()) { - mRowAlgorithm.bindRowViewIconToInfo(icon, info.apps.get(i)); - icon.setVisibility(View.VISIBLE); - } else { - icon.setVisibility(View.INVISIBLE); - } - } - return row; - } - - @Override - public Object[] getSections() { - ArrayList sections = new ArrayList<>(); - int prevSectionId = -1; - for (AppsRow row : mAppRows) { - if (row.sectionId != prevSectionId) { - sections.add(row.sectionDescription.toUpperCase()); - prevSectionId = row.sectionId; - } - } - return sections.toArray(); - } - - @Override - public int getPositionForSection(int sectionIndex) { - for (int i = 0; i < mAppRows.size(); i++) { - AppsRow row = mAppRows.get(i); - if (row.sectionId == sectionIndex) { - return i; - } - } - return 0; - } - - @Override - public int getSectionForPosition(int position) { - return mAppRows.get(position).sectionId; - } -} -/** - * The alphabetically sorted list of applications. - */ -class AlphabeticalAppList { - - /** - * Callbacks for when this list is modified. - */ - public interface Callbacks { - public void onAppsUpdated(); - } - - private List mApps; - private Callbacks mCb; - - public AlphabeticalAppList(Callbacks cb) { - mCb = cb; - } - - /** - * Returns the list of applications. - */ - public List getApps() { - return mApps; - } - - /** - * Sets the current set of apps. - */ - public void setApps(List apps) { - Collections.sort(apps, LauncherModel.getAppNameComparator()); - mApps = apps; - mCb.onAppsUpdated(); - } - - /** - * Adds new apps to the list. - */ - public void addApps(List apps) { - // We add it in place, in alphabetical order - Comparator appNameComparator = LauncherModel.getAppNameComparator(); - for (AppInfo info : apps) { - // This call will return the exact index of where the item is if >= 0, or the index - // where it should be inserted if < 0. - int index = Collections.binarySearch(mApps, info, appNameComparator); - if (index < 0) { - mApps.add(-(index + 1), info); - } - } - mCb.onAppsUpdated(); - } - - /** - * Updates existing apps in the list - */ - public void updateApps(List apps) { - Comparator appNameComparator = LauncherModel.getAppNameComparator(); - for (AppInfo info : apps) { - int index = mApps.indexOf(info); - if (index != -1) { - mApps.set(index, info); - } else { - index = Collections.binarySearch(mApps, info, appNameComparator); - if (index < 0) { - mApps.add(-(index + 1), info); - } - } - } - mCb.onAppsUpdated(); - } - - /** - * Removes some apps from the list. - */ - public void removeApps(List apps) { - for (AppInfo info : apps) { - int removeIndex = findAppByComponent(mApps, info); - if (removeIndex != -1) { - mApps.remove(removeIndex); - } - } - mCb.onAppsUpdated(); - } - - /** - * Finds the index of an app given a target AppInfo. - */ - private int findAppByComponent(List apps, AppInfo targetInfo) { - ComponentName targetComponent = targetInfo.intent.getComponent(); - int length = apps.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = apps.get(i); - if (info.user.equals(info.user) - && info.intent.getComponent().equals(targetComponent)) { - return i; - } - } - return -1; - } - -} /** * The all apps list view container. */ public class AppsContainerView extends FrameLayout implements DragSource, View.OnTouchListener, - View.OnLongClickListener, Insettable, AlphabeticalAppList.Callbacks { + View.OnLongClickListener, Insettable, TextWatcher, TextView.OnEditorActionListener, + LauncherTransitionable { - static final int GRID_LAYOUT = 0; - static final int LIST_LAYOUT = 1; - static final int USE_LAYOUT = LIST_LAYOUT; + private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; + + private static final int GRID_LAYOUT = 0; + private static final int LIST_LAYOUT = 1; + private static final int USE_LAYOUT = GRID_LAYOUT; private Launcher mLauncher; - private AppRowAlgorithm mAppRowsAlgorithm; - private AppsListAdapter mAdapter; - private AlphabeticalAppList mApps; - private ListView mList; - private int mAppsRowSize; + private AlphabeticalAppsList mApps; + private RecyclerView.Adapter mAdapter; + private RecyclerView.LayoutManager mLayoutManager; + private RecyclerView.ItemDecoration mItemDecoration; + private AppsContainerRecyclerView mAppsListView; + private EditText mSearchBar; + private int mNumAppsPerRow; private Point mLastTouchDownPos = new Point(); private Rect mPadding = new Rect(); + private int mContentMarginStart; public AppsContainerView(Context context) { this(context, null); @@ -423,15 +78,22 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); mLauncher = (Launcher) context; + mApps = new AlphabeticalAppsList(context); if (USE_LAYOUT == GRID_LAYOUT) { - mAppRowsAlgorithm = new SectionedAppsAlgorithm(); - mAppsRowSize = grid.allAppsRowsSize; + mNumAppsPerRow = grid.appsViewNumCols; + AppsGridAdapter adapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, + mLauncher, this); + mLayoutManager = adapter.getLayoutManager(context); + mItemDecoration = adapter.getItemDecoration(); + mAdapter = adapter; + mContentMarginStart = adapter.getContentMarginStart(); } else if (USE_LAYOUT == LIST_LAYOUT) { - mAppRowsAlgorithm = new ListedAppsAlgorithm(); - mAppsRowSize = 1; + mNumAppsPerRow = 1; + AppsListAdapter adapter = new AppsListAdapter(context, mApps, this, mLauncher, this); + mLayoutManager = adapter.getLayoutManager(context); + mAdapter = adapter; } - mAdapter = new AppsListAdapter(context, this, mLauncher, this); - mApps = new AlphabeticalAppList(this); + mApps.setAdapter(mAdapter); } /** @@ -466,7 +128,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O * Scrolls this list view to the top. */ public void scrollToTop() { - mList.scrollTo(0, 0); + mAppsListView.scrollToPosition(0); } /** @@ -480,23 +142,37 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O * Returns the reveal view used for the launcher transitions. */ public View getRevealView() { - return findViewById(R.id.all_apps_transition_overlay); - } - - @Override - public void onAppsUpdated() { - List rows = mAppRowsAlgorithm.computeAppRows(mApps.getApps(), mAppsRowSize); - mAdapter.setApps(rows, mAppsRowSize, mAppRowsAlgorithm); + return findViewById(R.id.apps_view_transition_overlay); } @Override protected void onFinishInflate() { - mList = (ListView) findViewById(R.id.apps_list); - mList.setFastScrollEnabled(true); - mList.setFastScrollAlwaysVisible(true); - mList.setItemsCanFocus(true); - mList.setAdapter(mAdapter); - mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + if (USE_LAYOUT == GRID_LAYOUT) { + ((AppsGridAdapter) mAdapter).setRtl(isRtl); + } + mSearchBar = (EditText) findViewById(R.id.app_search_box); + mSearchBar.addTextChangedListener(this); + mSearchBar.setOnEditorActionListener(this); + mAppsListView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view); + mAppsListView.setApps(mApps); + mAppsListView.setNumAppsPerRow(mNumAppsPerRow); + mAppsListView.setLayoutManager(mLayoutManager); + mAppsListView.setAdapter(mAdapter); + mAppsListView.setHasFixedSize(true); + if (isRtl) { + mAppsListView.setPadding(mAppsListView.getPaddingLeft(), mAppsListView.getPaddingTop(), + mAppsListView.getPaddingRight() + mContentMarginStart, mAppsListView.getPaddingBottom()); + } else { + mAppsListView.setPadding(mAppsListView.getPaddingLeft() + mContentMarginStart, mAppsListView.getPaddingTop(), + mAppsListView.getPaddingRight(), mAppsListView.getPaddingBottom()); + } + if (mItemDecoration != null) { + mAppsListView.addItemDecoration(mItemDecoration); + } + mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), + getPaddingBottom()); } @Override @@ -574,7 +250,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O } @Override - public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { + public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, + boolean success) { if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { // Exit spring loaded mode if we have not successfully dropped or have not handled the @@ -606,4 +283,83 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O d.deferDragViewCleanupPostAnimation = false; } } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + @Override + public void afterTextChanged(final Editable s) { + if (s.toString().isEmpty()) { + mApps.setFilter(null); + } else { + final AlphabeticIndexCompat indexer = mApps.getIndexer(); + final String filterText = s.toString().toLowerCase().replaceAll("\\s+", ""); + mApps.setFilter(new AlphabeticalAppsList.Filter() { + @Override + public boolean retainApp(AppInfo info) { + String title = info.title.toString(); + String sectionName = mApps.getSectionNameForApp(info); + return sectionName.toLowerCase().contains(filterText) || + title.toLowerCase().replaceAll("\\s+", "").contains(filterText); + } + }); + } + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { + List appsWithoutSections = mApps.getAppsWithoutSectionBreaks(); + List apps = mApps.getApps(); + if (appsWithoutSections.size() == 1) { + mSearchBar.clearFocus(); + mAppsListView.getChildAt(apps.indexOf(appsWithoutSections.get(0))).performClick(); + InputMethodManager imm = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getWindowToken(), 0); + } + return true; + } + return false; + } + + @Override + public View getContent() { + return null; + } + + @Override + public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { + if (!toWorkspace) { + // Disable the focus so that the search bar doesn't get focus + mSearchBar.setFocusableInTouchMode(false); + } + } + + @Override + public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { + // Do nothing + } + + @Override + public void onLauncherTransitionStep(Launcher l, float t) { + // Do nothing + } + + @Override + public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { + if (toWorkspace) { + // Clear the search bar + mSearchBar.setText(""); + } else { + mSearchBar.setFocusableInTouchMode(true); + } + } } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java new file mode 100644 index 000000000..6727e4f09 --- /dev/null +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -0,0 +1,206 @@ +package com.android.launcher3; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import com.android.launcher3.compat.AlphabeticIndexCompat; + + +/** + * The grid view adapter of all the apps. + */ +class AppsGridAdapter extends RecyclerView.Adapter { + + public static final String TAG = "AppsGridAdapter"; + + private static final int SECTION_BREAK_VIEW_TYPE = 0; + private static final int ICON_VIEW_TYPE = 1; + + /** + * ViewHolder for each icon. + */ + public static class ViewHolder extends RecyclerView.ViewHolder { + public View mContent; + public boolean mIsSectionRow; + + public ViewHolder(View v, boolean isSectionRow) { + super(v); + mContent = v; + mIsSectionRow = isSectionRow; + } + } + + /** + * Helper class to size the grid items. + */ + public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup { + @Override + public int getSpanSize(int position) { + AppInfo info = mApps.getApps().get(position); + if (info == AlphabeticalAppsList.SECTION_BREAK_INFO) { + return mAppsPerRow; + } else { + return 1; + } + } + } + + /** + * Helper class to draw the section headers + */ + public class GridItemDecoration extends RecyclerView.ItemDecoration { + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + AlphabeticIndexCompat indexer = mApps.getIndexer(); + for (int i = 0; i < parent.getChildCount(); i++) { + View child = parent.getChildAt(i); + ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); + if (holder != null) { + GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) + child.getLayoutParams(); + if (!holder.mIsSectionRow && !lp.isItemRemoved()) { + if (mApps.getApps().get(holder.getPosition() - 1) == + AlphabeticalAppsList.SECTION_BREAK_INFO) { + // Draw at the parent + AppInfo info = mApps.getApps().get(holder.getPosition()); + String section = mApps.getSectionNameForApp(info); + mSectionTextPaint.getTextBounds(section, 0, section.length(), + mTmpBounds); + if (mIsRtl) { + c.drawText(section, parent.getWidth() - mStartMargin + + (mStartMargin - mTmpBounds.width()) / 2, + child.getTop() + (2 * child.getPaddingTop()) + + mTmpBounds.height(), mSectionTextPaint); + } else { + c.drawText(section, (mStartMargin - mTmpBounds.width()) / 2, + child.getTop() + (2 * child.getPaddingTop()) + + mTmpBounds.height(), mSectionTextPaint); + } + } + } + } + } + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, + RecyclerView.State state) { + // Do nothing + } + } + + private LayoutInflater mLayoutInflater; + private AlphabeticalAppsList mApps; + private GridSpanSizer mGridSizer; + private GridItemDecoration mItemDecoration; + private View.OnTouchListener mTouchListener; + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + private int mAppsPerRow; + private boolean mIsRtl; + + // Section drawing + private int mStartMargin; + private Paint mSectionTextPaint; + private Rect mTmpBounds = new Rect(); + + + public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, + View.OnTouchListener touchListener, View.OnClickListener iconClickListener, + View.OnLongClickListener iconLongClickListener) { + Resources res = context.getResources(); + mApps = apps; + mAppsPerRow = appsPerRow; + mGridSizer = new GridSpanSizer(); + mItemDecoration = new GridItemDecoration(); + mLayoutInflater = LayoutInflater.from(context); + mTouchListener = touchListener; + mIconClickListener = iconClickListener; + mIconLongClickListener = iconLongClickListener; + mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); + mSectionTextPaint = new Paint(); + mSectionTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.apps_view_section_text_size)); + mSectionTextPaint.setColor(res.getColor(R.color.apps_view_section_text_color)); + mSectionTextPaint.setAntiAlias(true); + } + + /** + * Sets whether we are in RTL mode. + */ + public void setRtl(boolean rtl) { + mIsRtl = rtl; + } + + /** + * Returns the grid layout manager. + */ + public GridLayoutManager getLayoutManager(Context context) { + GridLayoutManager layoutMgr = new GridLayoutManager(context, mAppsPerRow, + GridLayoutManager.VERTICAL, false); + layoutMgr.setSpanSizeLookup(mGridSizer); + return layoutMgr; + } + + /** + * Returns the item decoration for the recycler view. + */ + public RecyclerView.ItemDecoration getItemDecoration() { + return mItemDecoration; + } + + /** + * Returns the left padding for the recycler view. + */ + public int getContentMarginStart() { + return mStartMargin; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case SECTION_BREAK_VIEW_TYPE: + return new ViewHolder(new View(parent.getContext()), true); + case ICON_VIEW_TYPE: + BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( + R.layout.apps_grid_row_icon_view, parent, false); + icon.setOnTouchListener(mTouchListener); + icon.setOnClickListener(mIconClickListener); + icon.setOnLongClickListener(mIconLongClickListener); + icon.setFocusable(true); + return new ViewHolder(icon, false); + default: + throw new RuntimeException("Unexpected view type"); + } + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + AppInfo info = mApps.getApps().get(position); + if (info != AlphabeticalAppsList.SECTION_BREAK_INFO) { + BubbleTextView icon = (BubbleTextView) holder.mContent; + icon.applyFromApplicationInfo(info); + } + } + + @Override + public int getItemCount() { + return mApps.getApps().size(); + } + + @Override + public int getItemViewType(int position) { + if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { + return SECTION_BREAK_VIEW_TYPE; + } + return ICON_VIEW_TYPE; + } +} diff --git a/src/com/android/launcher3/AppsListAdapter.java b/src/com/android/launcher3/AppsListAdapter.java new file mode 100644 index 000000000..8ac381e79 --- /dev/null +++ b/src/com/android/launcher3/AppsListAdapter.java @@ -0,0 +1,119 @@ +package com.android.launcher3; + +import android.content.Context; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.android.launcher3.compat.AlphabeticIndexCompat; + +/** + * The linear list view adapter for all the apps. + */ +class AppsListAdapter extends RecyclerView.Adapter { + + /** + * ViewHolder for each row. + */ + public static class ViewHolder extends RecyclerView.ViewHolder { + public View mContent; + + public ViewHolder(View v) { + super(v); + mContent = v; + } + } + + private static final int SECTION_BREAK_VIEW_TYPE = 0; + private static final int ICON_VIEW_TYPE = 1; + + private LayoutInflater mLayoutInflater; + private AlphabeticalAppsList mApps; + private View.OnTouchListener mTouchListener; + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + + public AppsListAdapter(Context context, AlphabeticalAppsList apps, + View.OnTouchListener touchListener, View.OnClickListener iconClickListener, + View.OnLongClickListener iconLongClickListener) { + mApps = apps; + mLayoutInflater = LayoutInflater.from(context); + mTouchListener = touchListener; + mIconClickListener = iconClickListener; + mIconLongClickListener = iconLongClickListener; + } + + public RecyclerView.LayoutManager getLayoutManager(Context context) { + return new LinearLayoutManager(context); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case SECTION_BREAK_VIEW_TYPE: + return new ViewHolder(new View(parent.getContext())); + case ICON_VIEW_TYPE: + // Inflate the row and all the icon children necessary + ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.apps_list_row_view, + parent, false); + BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( + R.layout.apps_list_row_icon_view, row, false); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, 1); + lp.gravity = Gravity.CENTER_VERTICAL; + icon.setLayoutParams(lp); + icon.setOnTouchListener(mTouchListener); + icon.setOnClickListener(mIconClickListener); + icon.setOnLongClickListener(mIconLongClickListener); + icon.setFocusable(true); + row.addView(icon); + return new ViewHolder(row); + default: + throw new RuntimeException("Unexpected view type"); + } + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + AppInfo info = mApps.getApps().get(position); + if (info != AlphabeticalAppsList.SECTION_BREAK_INFO) { + ViewGroup content = (ViewGroup) holder.mContent; + String sectionDescription = mApps.getSectionNameForApp(info); + + // Bind the section header + boolean showSectionHeader = true; + if (position > 0) { + AppInfo prevInfo = mApps.getApps().get(position - 1); + showSectionHeader = (prevInfo == AlphabeticalAppsList.SECTION_BREAK_INFO); + } + TextView tv = (TextView) content.findViewById(R.id.section); + if (showSectionHeader) { + tv.setText(sectionDescription); + tv.setVisibility(View.VISIBLE); + } else { + tv.setVisibility(View.INVISIBLE); + } + + // Bind the icon + BubbleTextView icon = (BubbleTextView) content.getChildAt(1); + icon.applyFromApplicationInfo(info); + } + } + + @Override + public int getItemCount() { + return mApps.getApps().size(); + } + + @Override + public int getItemViewType(int position) { + if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { + return SECTION_BREAK_VIEW_TYPE; + } + return ICON_VIEW_TYPE; + } +} diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5ea84aeb2..fabae5702 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -63,6 +63,7 @@ public class BubbleTextView extends TextView { private float mSlop; + private final boolean mDeferShadowGenerationOnTouch; private final boolean mCustomShadowsEnabled; private final boolean mLayoutHorizontal; private final int mIconSize; @@ -96,6 +97,8 @@ public class BubbleTextView extends TextView { grid.iconDrawablePaddingPx); mTextSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_textSizeOverride, grid.allAppsIconTextSizePx); + mDeferShadowGenerationOnTouch = + a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false); a.recycle(); if (mCustomShadowsEnabled) { @@ -218,7 +221,7 @@ public class BubbleTextView extends TextView { // So that the pressed outline is visible immediately on setStayPressed(), // we pre-create it on ACTION_DOWN (it takes a small but perceptible amount of time // to create it) - if (mPressedBackground == null) { + if (!mDeferShadowGenerationOnTouch && mPressedBackground == null) { mPressedBackground = mOutlineHelper.createMediumDropShadow(this); } @@ -247,6 +250,10 @@ public class BubbleTextView extends TextView { mStayPressed = stayPressed; if (!stayPressed) { mPressedBackground = null; + } else { + if (mPressedBackground == null) { + mPressedBackground = mOutlineHelper.createMediumDropShadow(this); + } } // Only show the shadow effect when persistent pressed state is set. diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index b5bb55ca7..ddd300257 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -122,8 +122,7 @@ public class DeviceProfile { int hotseatAllAppsRank; int allAppsNumRows; int allAppsNumCols; - // TODO(winsonc): to be used with the grid layout - int allAppsRowsSize; + int appsViewNumCols; int searchBarSpaceWidthPx; int searchBarSpaceHeightPx; int pageIndicatorHeightPx; @@ -365,7 +364,7 @@ public class DeviceProfile { } } - private void updateIconSize(float scale, int drawablePadding, Resources resources, + private void updateIconSize(float scale, int drawablePadding, Resources res, DisplayMetrics dm) { iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale); iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale); @@ -374,9 +373,9 @@ public class DeviceProfile { // Search Bar searchBarSpaceWidthPx = Math.min(widthPx, - resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width)); + res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width)); searchBarSpaceHeightPx = getSearchBarTopOffset() - + resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height); + + res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height); // Calculate the actual text height Paint textPaint = new Paint(); @@ -384,7 +383,7 @@ public class DeviceProfile { FontMetrics fm = textPaint.getFontMetrics(); cellWidthPx = iconSizePx; cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top); - final float scaleDps = resources.getDimensionPixelSize(R.dimen.dragViewScale); + final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); dragViewScale = (iconSizePx + scaleDps) / iconSizePx; // Hotseat @@ -402,11 +401,11 @@ public class DeviceProfile { allAppsCellWidthPx = allAppsIconSizePx; allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx; int maxLongEdgeCellCount = - resources.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count); + res.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count); int maxShortEdgeCellCount = - resources.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count); + res.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count); int minEdgeCellCount = - resources.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count); + res.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count); int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount); int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount); @@ -417,10 +416,17 @@ public class DeviceProfile { allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) / (allAppsCellHeightPx + allAppsCellPaddingPx); allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows)); - allAppsNumCols = (availableWidthPx) / - (allAppsCellWidthPx + allAppsCellPaddingPx); + allAppsNumCols = (availableWidthPx) / (allAppsCellWidthPx + allAppsCellPaddingPx); allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols)); } + + int appsContainerViewPx = res.getDimensionPixelSize(R.dimen.apps_container_width); + int appsViewLeftMarginPx = + res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); + int availableAppsWidthPx = (appsContainerViewPx > 0) ? appsContainerViewPx : + availableWidthPx; + appsViewNumCols = (availableAppsWidthPx - appsViewLeftMarginPx) / + (allAppsCellWidthPx + allAppsCellPaddingPx); } void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx, diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java new file mode 100644 index 000000000..602a84566 --- /dev/null +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -0,0 +1,131 @@ +package com.android.launcher3.compat; + +import android.content.Context; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Locale; + +/** + * Fallback class to support Alphabetic indexing if not supported by the framework. + * TODO(winsonc): disable for non-english locales + */ +class BaseAlphabeticIndex { + + private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-"; + private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1; + + public BaseAlphabeticIndex() {} + + /** + * Sets the max number of the label buckets in this index. + */ + public void setMaxLabelCount(int count) { + // Not currently supported + } + + /** + * Returns the index of the bucket in which the given string should appear. + */ + public int getBucketIndex(String s) { + if (s.isEmpty()) { + return UNKNOWN_BUCKET_INDEX; + } + int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase()); + if (index != -1) { + return index; + } + return UNKNOWN_BUCKET_INDEX; + } + + /** + * Returns the label for the bucket at the given index (as returned by getBucketIndex). + */ + public String getBucketLabel(int index) { + return BUCKETS.substring(index, index + 1); + } +} + +/** + * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base alphabetic index. + */ +public class AlphabeticIndexCompat extends BaseAlphabeticIndex { + + private Object mAlphabeticIndex; + private Method mAddLabelsMethod; + private Method mSetMaxLabelCountMethod; + private Method mGetBucketIndexMethod; + private Method mGetBucketLabelMethod; + private boolean mHasValidAlphabeticIndex; + + public AlphabeticIndexCompat(Context context) { + super(); + try { + Locale curLocale = context.getResources().getConfiguration().locale; + Class clazz = Class.forName("libcore.icu.AlphabeticIndex"); + Constructor ctor = clazz.getConstructor(Locale.class); + mAddLabelsMethod = clazz.getDeclaredMethod("addLabels", Locale.class); + mSetMaxLabelCountMethod = clazz.getDeclaredMethod("setMaxLabelCount", int.class); + mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class); + mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class); + mAlphabeticIndex = ctor.newInstance(curLocale); + try { + // Ensure we always have some base English locale buckets + if (!curLocale.getLanguage().equals(new Locale("en").getLanguage())) { + mAddLabelsMethod.invoke(mAlphabeticIndex, Locale.ENGLISH); + } + } catch (Exception e) { + e.printStackTrace(); + } + mHasValidAlphabeticIndex = true; + } catch (Exception e) { + mHasValidAlphabeticIndex = false; + } + } + + /** + * Sets the max number of the label buckets in this index. + * (ICU 51 default is 99) + */ + public void setMaxLabelCount(int count) { + if (mHasValidAlphabeticIndex) { + try { + mSetMaxLabelCountMethod.invoke(mAlphabeticIndex, count); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + super.setMaxLabelCount(count); + } + } + + /** + * Returns the index of the bucket in which {@param s} should appear. + * Function is synchronized because underlying routine walks an iterator + * whose state is maintained inside the index object. + */ + public int getBucketIndex(String s) { + if (mHasValidAlphabeticIndex) { + try { + return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s); + } catch (Exception e) { + e.printStackTrace(); + } + } + return super.getBucketIndex(s); + } + + /** + * Returns the label for the bucket at the given index (as returned by getBucketIndex). + */ + public String getBucketLabel(int index) { + if (mHasValidAlphabeticIndex) { + try { + return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index); + } catch (Exception e) { + e.printStackTrace(); + } + } + return super.getBucketLabel(index); + } +} -- cgit v1.2.3 From 4846193300245c8c0a1f9bde3175f273df044309 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 9 Mar 2015 17:41:09 -0700 Subject: Scrolling folder during drag-drop > Show a scroll hint (partial scroll) when the icon is over the last icon (some fraction) > Automatically scroll the folder if the user stays in that position for some time > Rearrance the icons on the new page only after the scroll animaiton is complete Change-Id: I7a2dd85ab23802d647801686df069975d197cd39 --- src/com/android/launcher3/DragController.java | 4 +- src/com/android/launcher3/Folder.java | 192 ++++++++++++++++++++++++- src/com/android/launcher3/FolderPagedView.java | 100 +++++++++---- 3 files changed, 261 insertions(+), 35 deletions(-) diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 09c59a057..8dc6e185c 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -49,8 +49,8 @@ public class DragController { /** Indicates the drag is a copy. */ public static int DRAG_ACTION_COPY = 1; - private static final int SCROLL_DELAY = 500; - private static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150; + public static final int SCROLL_DELAY = 500; + public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150; private static final boolean PROFILE_DRAWING_DURING_DRAG = false; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index bef1f0da4..7ff60de4f 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -48,6 +48,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; + import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.Workspace.ItemOperator; @@ -74,6 +75,21 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList static final int STATE_ANIMATING = 1; static final int STATE_OPEN = 2; + /** + * Fraction of the width to scroll when showing the next page hint. + */ + private static final float SCROLL_HINT_FRACTION = 0.07f; + + /** + * Time for which the scroll hint is shown before automatically changing page. + */ + public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY; + + /** + * Fraction of icon width which behave as scroll region. + */ + private static final float ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f; + private static final int REORDER_DELAY = 250; private static final int ON_EXIT_CLOSE_DELAY = 400; private static final Rect sTempRect = new Rect(); @@ -129,6 +145,17 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private boolean mDeferDropAfterUninstall; private boolean mUninstallSuccessful; + // Folder scrolling + private int mScrollAreaOffset; + private Alarm mOnScrollHintAlarm; + private Alarm mScrollPauseAlarm; + + // TODO: Use {@link #mContent} once {@link #ALLOW_FOLDER_SCROLL} is removed. + private FolderPagedView mPagedView; + + private int mScrollHintDir = DragController.SCROLL_NONE; + private int mCurrentScrollDir = DragController.SCROLL_NONE; + /** * Used to inflate the Workspace from XML. * @@ -157,6 +184,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // name is complete, we have something to focus on, thus hiding the cursor and giving // reliable behavior when clicking the text field (since it will always gain focus on click). setFocusableInTouchMode(true); + + if (ALLOW_FOLDER_SCROLL) { + mOnScrollHintAlarm = new Alarm(); + mScrollPauseAlarm = new Alarm(); + } } @Override @@ -183,6 +215,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList int measureSpec = MeasureSpec.UNSPECIFIED; mFooter.measure(measureSpec, measureSpec); mFooterHeight = mFooter.getMeasuredHeight(); + + if (ALLOW_FOLDER_SCROLL) { + mPagedView = (FolderPagedView) mContent; + } } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -386,6 +422,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void animateOpen() { if (!(getParent() instanceof DragLayer)) return; + if (ALLOW_FOLDER_SCROLL) { + // Always open on the first page. + mPagedView.snapToPageImmediately(0); + } + Animator openFolderAnim = null; final Runnable onCompleteRunnable; if (!Utilities.isLmpOrAbove()) { @@ -544,6 +585,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void onDragEnter(DragObject d) { mPrevTargetRank = -1; mOnExitAlarm.cancelAlarm(); + if (ALLOW_FOLDER_SCROLL) { + // Get the area offset such that the folder only closes if half the drag icon width + // is outside the folder area + mScrollAreaOffset = d.dragView.getDragRegionWidth() / 2 - d.xOffset; + } } OnAlarmListener mReorderAlarmListener = new OnAlarmListener() { @@ -558,18 +604,80 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } + @Override public void onDragOver(DragObject d) { - final float[] r = d.getVisualCenter(null); - r[0] -= getPaddingLeft(); - r[1] -= getPaddingTop(); + onDragOver(d, REORDER_DELAY); + } + + private int getTargetRank(DragObject d, float[] recycle) { + recycle = d.getVisualCenter(recycle); + return mContent.findNearestArea( + (int) recycle[0] - getPaddingLeft(), (int) recycle[1] - getPaddingTop()); + } + + private void onDragOver(DragObject d, int reorderDelay) { + if (ALLOW_FOLDER_SCROLL && mScrollPauseAlarm.alarmPending()) { + return; + } + final float[] r = new float[2]; + mTargetRank = getTargetRank(d, r); - mTargetRank = mContent.findNearestArea((int) r[0], (int) r[1]); if (mTargetRank != mPrevTargetRank) { mReorderAlarm.cancelAlarm(); mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); mReorderAlarm.setAlarm(REORDER_DELAY); mPrevTargetRank = mTargetRank; } + + if (!ALLOW_FOLDER_SCROLL) { + return; + } + + float x = r[0]; + int currentPage = mPagedView.getNextPage(); + int cellWidth = mPagedView.getCurrentCellLayout().getCellWidth(); + if (currentPage > 0 && x < cellWidth * ICON_OVERSCROLL_WIDTH_FACTOR) { + // Show scroll hint on the left + if (mScrollHintDir != DragController.SCROLL_LEFT) { + mPagedView.showScrollHint(-SCROLL_HINT_FRACTION); + mScrollHintDir = DragController.SCROLL_LEFT; + } + + // Set alarm for when the hint is complete + if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != DragController.SCROLL_LEFT) { + mCurrentScrollDir = DragController.SCROLL_LEFT; + mOnScrollHintAlarm.cancelAlarm(); + mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d)); + mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION); + + mReorderAlarm.cancelAlarm(); + mTargetRank = mEmptyCellRank; + } + } else if (currentPage < (mPagedView.getPageCount() - 1) && + (x > (getWidth() - cellWidth * ICON_OVERSCROLL_WIDTH_FACTOR))) { + // Show scroll hint on the right + if (mScrollHintDir != DragController.SCROLL_RIGHT) { + mPagedView.showScrollHint(SCROLL_HINT_FRACTION); + mScrollHintDir = DragController.SCROLL_RIGHT; + } + + // Set alarm for when the hint is complete + if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != DragController.SCROLL_RIGHT) { + mCurrentScrollDir = DragController.SCROLL_RIGHT; + mOnScrollHintAlarm.cancelAlarm(); + mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d)); + mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION); + + mReorderAlarm.cancelAlarm(); + mTargetRank = mEmptyCellRank; + } + } else { + mOnScrollHintAlarm.cancelAlarm(); + if (mScrollHintDir != DragController.SCROLL_NONE) { + mPagedView.clearScrollHint(); + mScrollHintDir = DragController.SCROLL_NONE; + } + } } OnAlarmListener mOnExitAlarmListener = new OnAlarmListener() { @@ -595,6 +703,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY); } mReorderAlarm.cancelAlarm(); + + if (ALLOW_FOLDER_SCROLL) { + mOnScrollHintAlarm.cancelAlarm(); + mScrollPauseAlarm.cancelAlarm(); + if (mScrollHintDir != DragController.SCROLL_NONE) { + mPagedView.clearScrollHint(); + mScrollHintDir = DragController.SCROLL_NONE; + } + } } public void onDropCompleted(final View target, final DragObject d, @@ -960,6 +1077,22 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList }; } + if (ALLOW_FOLDER_SCROLL) { + // If the icon was dropped while the page was being scrolled, we need to compute + // the target location again such that the icon is placed of the final page. + if (!mPagedView.rankOnCurrentPage(mEmptyCellRank)) { + // Reorder again. + mTargetRank = getTargetRank(d, null); + + // Rearrange items immediately. + mReorderAlarmListener.onAlarm(mReorderAlarm); + + mOnScrollHintAlarm.cancelAlarm(); + mScrollPauseAlarm.cancelAlarm(); + } + mPagedView.completePendingPageChanges(); + } + View currentDragView; ShortcutInfo si = mCurrentDragInfo; if (mIsExternalDrag) { @@ -1090,6 +1223,57 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public void getHitRectRelativeToDragLayer(Rect outRect) { getHitRect(outRect); + outRect.left -= mScrollAreaOffset; + outRect.right += mScrollAreaOffset; + } + + private class OnScrollHintListener implements OnAlarmListener { + + private final DragObject mDragObject; + + OnScrollHintListener(DragObject object) { + mDragObject = object; + } + + /** + * Scroll hint has been shown long enough. Now scroll to appropriate page. + */ + @Override + public void onAlarm(Alarm alarm) { + if (mCurrentScrollDir == DragController.SCROLL_LEFT) { + mPagedView.scrollLeft(); + mScrollHintDir = DragController.SCROLL_NONE; + } else if (mCurrentScrollDir == DragController.SCROLL_RIGHT) { + mPagedView.scrollRight(); + mScrollHintDir = DragController.SCROLL_NONE; + } else { + // This should not happen + return; + } + mCurrentScrollDir = DragController.SCROLL_NONE; + + // Pause drag event until the scrolling is finished + mScrollPauseAlarm.setOnAlarmListener(new OnScrollFinishedListener(mDragObject)); + mScrollPauseAlarm.setAlarm(DragController.RESCROLL_DELAY); + } + } + + private class OnScrollFinishedListener implements OnAlarmListener { + + private final DragObject mDragObject; + + OnScrollFinishedListener(DragObject object) { + mDragObject = object; + } + + /** + * Page scroll is complete. + */ + @Override + public void onAlarm(Alarm alarm) { + // Reorder immediately on page change. + onDragOver(mDragObject, 1); + } } public static interface FolderContent { diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index c9e825aa3..b4a7a7546 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -22,6 +22,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.animation.DecelerateInterpolator; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; import com.android.launcher3.PageIndicator.PageMarkerResources; @@ -30,12 +31,15 @@ import com.android.launcher3.Workspace.ItemOperator; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; public class FolderPagedView extends PagedView implements Folder.FolderContent { private static final String TAG = "FolderPagedView"; private static final int REORDER_ANIMATION_DURATION = 230; + private static final int START_VIEW_REORDER_DELAY = 30; + private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; private static final int[] sTempPosArray = new int[2]; // TODO: Remove this restriction @@ -137,18 +141,9 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { public int allocateNewLastItemRank() { int rank = getItemCount(); int total = rank + 1; - if (rank < mMaxItemsPerPage) { - // Rearrange the items as the grid size might change. - mFolder.rearrangeChildren(total); - } else { - setupContentDimensions(total); - } + // Rearrange the items as the grid size might change. + mFolder.rearrangeChildren(total); - // Add a new page if last page is full - if (getPageAt(getChildCount() - 1).getShortcutsAndWidgets().getChildCount() - >= mMaxItemsPerPage) { - createAndAddNewPage(); - } setCurrentPage(getChildCount() - 1); return rank; } @@ -265,7 +260,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { int newX, newY, rank; rank = 0; - for (View v : list) { + for (int i = 0; i < itemCount; i++) { + View v = list.size() > i ? list.get(i) : null; if (currentPage == null || position >= mMaxItemsPerPage) { // Next page if (pageItr.hasNext()) { @@ -276,25 +272,28 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { position = 0; } - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - newX = position % mGridCountX; - newY = position / mGridCountX; - ItemInfo info = (ItemInfo) v.getTag(); - if (info.cellX != newX || info.cellY != newY || info.rank != rank) { - info.cellX = newX; - info.cellY = newY; - info.rank = rank; - if (saveChanges) { - LauncherModel.addOrMoveItemInDatabase(getContext(), info, - mFolder.mInfo.id, 0, info.cellX, info.cellY); + if (v != null) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); + newX = position % mGridCountX; + newY = position / mGridCountX; + ItemInfo info = (ItemInfo) v.getTag(); + if (info.cellX != newX || info.cellY != newY || info.rank != rank) { + info.cellX = newX; + info.cellY = newY; + info.rank = rank; + if (saveChanges) { + LauncherModel.addOrMoveItemInDatabase(getContext(), info, + mFolder.mInfo.id, 0, info.cellX, info.cellY); + } } + lp.cellX = info.cellX; + lp.cellY = info.cellY; + currentPage.addViewToCellLayout( + v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); } - lp.cellX = info.cellX; - lp.cellY = info.cellY; + rank ++; position++; - currentPage.addViewToCellLayout( - v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); } // Remove extra views. @@ -328,6 +327,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { @Override public int getItemCount() { int lastPage = getChildCount() - 1; + if (lastPage < 0) { + // If there are no pages, there must be only one icon in the folder. + return 1; + } return getPageAt(lastPage).getShortcutsAndWidgets().getChildCount() + lastPage * mMaxItemsPerPage; } @@ -406,10 +409,49 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } } + /** + * Scrolls the current view by a fraction + */ + public void showScrollHint(float fraction) { + int hint = (int) (fraction * getWidth()); + int scroll = getScrollForPage(getNextPage()) + hint; + int delta = scroll - mUnboundedScrollX; + if (delta != 0) { + mScroller.setInterpolator(new DecelerateInterpolator()); + mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, Folder.SCROLL_HINT_DURATION); + invalidate(); + } + } + + public void clearScrollHint() { + if (mUnboundedScrollX != getScrollForPage(getNextPage())) { + snapToPage(getNextPage()); + } + } + + /** + * Finish animation all the views which are animating across pages + */ + public void completePendingPageChanges() { + if (!mPageChangingViews.isEmpty()) { + HashMap pendingViews = new HashMap<>(mPageChangingViews); + for (Map.Entry e : pendingViews.entrySet()) { + e.getKey().animate().cancel(); + e.getValue().run(); + } + } + } + + public boolean rankOnCurrentPage(int rank) { + int p = rank / mMaxItemsPerPage; + return p == getNextPage(); + } + @Override public void realTimeReorder(int empty, int target) { + completePendingPageChanges(); int delay = 0; - float delayAmount = 30; + float delayAmount = START_VIEW_REORDER_DELAY; // Animation only happens on the current page. int pageToAnimate = getNextPage(); @@ -523,7 +565,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX, REORDER_ANIMATION_DURATION, delay, true, true)) { delay += delayAmount; - delayAmount *= 0.9; + delayAmount *= VIEW_REORDER_DELAY_FACTOR; } } } -- cgit v1.2.3 From d68725cd20be0c1d5ebd583da5fde0f007a69bf8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 13 Mar 2015 10:26:27 -0700 Subject: Fixing build error in launcher_protoutil_lib > Since test build rules were included before the launcher_protoutil_lib rule, all the following paths got prefixed with "tests/" giving file not found error Change-Id: I13f96fc2f14336b0ff40f086176cc4afbc254791 --- Android.mk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index 385380861..d7bfdd464 100644 --- a/Android.mk +++ b/Android.mk @@ -50,8 +50,6 @@ LOCAL_PROGUARD_FLAG_FILES := proguard.flags include $(BUILD_PACKAGE) -include $(call all-makefiles-under,$(LOCAL_PATH)) - # # Protocol Buffer Debug Utility in Java # @@ -89,3 +87,6 @@ $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/util/etc/launcher_protoutil | $(ACP) $(hide) chmod 755 $@ INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE) + +# ================================================== +include $(call all-makefiles-under,$(LOCAL_PATH)) -- cgit v1.2.3 From 888b3a10bf1e99192f12c844ee275c6f360d6b34 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 13 Mar 2015 11:14:16 -0700 Subject: Minor changes to apps view. - Ensuring that apps with numbers and in other locals have a section header. - Adding an empty state when there are no apps with the current filter - Removing unnecessary call to check AppInfos Change-Id: I9dc541c680475b98745fa257ad7e4af06e3966c9 --- res/layout-sw600dp/apps_view.xml | 2 +- res/layout/apps_empty_view.xml | 28 +++++++++ res/layout/apps_list_reveal_view.xml | 25 -------- res/layout/apps_reveal_view.xml | 25 ++++++++ res/layout/apps_view.xml | 2 +- res/values/strings.xml | 4 ++ .../android/launcher3/AlphabeticalAppsList.java | 11 +++- src/com/android/launcher3/AppsContainerView.java | 24 ++++++-- src/com/android/launcher3/AppsGridAdapter.java | 53 +++++++++++++---- src/com/android/launcher3/AppsListAdapter.java | 66 +++++++++++++++------- src/com/android/launcher3/BubbleTextView.java | 3 +- .../launcher3/compat/AlphabeticIndexCompat.java | 26 +++++++-- 12 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 res/layout/apps_empty_view.xml delete mode 100644 res/layout/apps_list_reveal_view.xml create mode 100644 res/layout/apps_reveal_view.xml diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml index 3bb6ec505..0628ca6d3 100644 --- a/res/layout-sw600dp/apps_view.xml +++ b/res/layout-sw600dp/apps_view.xml @@ -22,7 +22,7 @@ android:background="#22000000" android:descendantFocusability="afterDescendants"> diff --git a/res/layout/apps_empty_view.xml b/res/layout/apps_empty_view.xml new file mode 100644 index 000000000..8408077a2 --- /dev/null +++ b/res/layout/apps_empty_view.xml @@ -0,0 +1,28 @@ + + + + diff --git a/res/layout/apps_list_reveal_view.xml b/res/layout/apps_list_reveal_view.xml deleted file mode 100644 index 19e462bee..000000000 --- a/res/layout/apps_list_reveal_view.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - \ No newline at end of file diff --git a/res/layout/apps_reveal_view.xml b/res/layout/apps_reveal_view.xml new file mode 100644 index 000000000..19e462bee --- /dev/null +++ b/res/layout/apps_reveal_view.xml @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml index c1bae63f6..00f3cca88 100644 --- a/res/layout/apps_view.xml +++ b/res/layout/apps_view.xml @@ -22,7 +22,7 @@ android:background="@drawable/apps_customize_bg" android:descendantFocusability="afterDescendants"> + layout="@layout/apps_reveal_view" /> \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 0d113dbf7..408109d15 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -74,6 +74,10 @@ Search Apps + + Loading Apps... + + No Apps found matching \"%1$s\" diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 2847afc89..c1d2738da 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -78,9 +78,7 @@ public class AlphabeticalAppsList { * Returns the section name for the application. */ public String getSectionNameForApp(AppInfo info) { - String title = info.title.toString(); - String sectionName = mIndexer.getBucketLabel(mIndexer.getBucketIndex(title)); - return sectionName; + return mIndexer.computeSectionName(info.title.toString().trim()); } /** @@ -90,6 +88,13 @@ public class AlphabeticalAppsList { return mIndexer; } + /** + * Returns whether there are no filtered results. + */ + public boolean hasNoFilteredResults() { + return (mFilter != null) && mFilteredApps.isEmpty(); + } + /** * Sets the current filter for this list of apps. */ diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index cc31e20fa..64b27baf5 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -16,10 +16,12 @@ package com.android.launcher3; import android.content.Context; +import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.text.Editable; +import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.KeyEvent; @@ -30,7 +32,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.TextView; -import com.android.launcher3.compat.AlphabeticIndexCompat; import java.util.List; @@ -76,6 +77,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O super(context, attrs, defStyleAttr, defStyleRes); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + Resources res = context.getResources(); mLauncher = (Launcher) context; mApps = new AlphabeticalAppsList(context); @@ -83,6 +85,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O mNumAppsPerRow = grid.appsViewNumCols; AppsGridAdapter adapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, mLauncher, this); + adapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); mLayoutManager = adapter.getLayoutManager(context); mItemDecoration = adapter.getItemDecoration(); mAdapter = adapter; @@ -90,6 +93,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O } else if (USE_LAYOUT == LIST_LAYOUT) { mNumAppsPerRow = 1; AppsListAdapter adapter = new AppsListAdapter(context, mApps, this, mLauncher, this); + adapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); mLayoutManager = adapter.getLayoutManager(context); mAdapter = adapter; } @@ -163,10 +167,12 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O mAppsListView.setHasFixedSize(true); if (isRtl) { mAppsListView.setPadding(mAppsListView.getPaddingLeft(), mAppsListView.getPaddingTop(), - mAppsListView.getPaddingRight() + mContentMarginStart, mAppsListView.getPaddingBottom()); + mAppsListView.getPaddingRight() + mContentMarginStart, + mAppsListView.getPaddingBottom()); } else { - mAppsListView.setPadding(mAppsListView.getPaddingLeft() + mContentMarginStart, mAppsListView.getPaddingTop(), - mAppsListView.getPaddingRight(), mAppsListView.getPaddingBottom()); + mAppsListView.setPadding(mAppsListView.getPaddingLeft() + mContentMarginStart, + mAppsListView.getPaddingTop(), mAppsListView.getPaddingRight(), + mAppsListView.getPaddingBottom()); } if (mItemDecoration != null) { mAppsListView.addItemDecoration(mItemDecoration); @@ -299,7 +305,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, View.O if (s.toString().isEmpty()) { mApps.setFilter(null); } else { - final AlphabeticIndexCompat indexer = mApps.getIndexer(); + String formatStr = getResources().getString(R.string.apps_view_no_search_results); + if (USE_LAYOUT == GRID_LAYOUT) { + ((AppsGridAdapter) mAdapter).setEmptySearchText(String.format(formatStr, + s.toString())); + } else { + ((AppsListAdapter) mAdapter).setEmptySearchText(String.format(formatStr, + s.toString())); + } + final String filterText = s.toString().toLowerCase().replaceAll("\\s+", ""); mApps.setFilter(new AlphabeticalAppsList.Filter() { @Override diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 6727e4f09..028cd8f70 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -10,6 +10,7 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import com.android.launcher3.compat.AlphabeticIndexCompat; @@ -22,6 +23,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { private static final int SECTION_BREAK_VIEW_TYPE = 0; private static final int ICON_VIEW_TYPE = 1; + private static final int EMPTY_VIEW_TYPE = 2; /** * ViewHolder for each icon. @@ -29,11 +31,13 @@ class AppsGridAdapter extends RecyclerView.Adapter { public static class ViewHolder extends RecyclerView.ViewHolder { public View mContent; public boolean mIsSectionRow; + public boolean mIsEmptyRow; - public ViewHolder(View v, boolean isSectionRow) { + public ViewHolder(View v, boolean isSectionRow, boolean isEmptyRow) { super(v); mContent = v; mIsSectionRow = isSectionRow; + mIsEmptyRow = isEmptyRow; } } @@ -43,8 +47,14 @@ class AppsGridAdapter extends RecyclerView.Adapter { public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup { @Override public int getSpanSize(int position) { + if (mApps.hasNoFilteredResults()) { + // Empty view spans full width + return mAppsPerRow; + } + AppInfo info = mApps.getApps().get(position); if (info == AlphabeticalAppsList.SECTION_BREAK_INFO) { + // Section break spans full width return mAppsPerRow; } else { return 1; @@ -59,14 +69,13 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - AlphabeticIndexCompat indexer = mApps.getIndexer(); for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); if (holder != null) { GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) child.getLayoutParams(); - if (!holder.mIsSectionRow && !lp.isItemRemoved()) { + if (!holder.mIsSectionRow && !holder.mIsEmptyRow && !lp.isItemRemoved()) { if (mApps.getApps().get(holder.getPosition() - 1) == AlphabeticalAppsList.SECTION_BREAK_INFO) { // Draw at the parent @@ -106,6 +115,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { private View.OnLongClickListener mIconLongClickListener; private int mAppsPerRow; private boolean mIsRtl; + private String mEmptySearchText; // Section drawing private int mStartMargin; @@ -140,6 +150,13 @@ class AppsGridAdapter extends RecyclerView.Adapter { mIsRtl = rtl; } + /** + * Sets the text to show when there are no apps. + */ + public void setEmptySearchText(String query) { + mEmptySearchText = query; + } + /** * Returns the grid layout manager. */ @@ -167,8 +184,12 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { + case EMPTY_VIEW_TYPE: + return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent, + false), false /* isSectionRow */, true /* isEmptyRow */); case SECTION_BREAK_VIEW_TYPE: - return new ViewHolder(new View(parent.getContext()), true); + return new ViewHolder(new View(parent.getContext()), true /* isSectionRow */, + false /* isEmptyRow */); case ICON_VIEW_TYPE: BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( R.layout.apps_grid_row_icon_view, parent, false); @@ -176,7 +197,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { icon.setOnClickListener(mIconClickListener); icon.setOnLongClickListener(mIconLongClickListener); icon.setFocusable(true); - return new ViewHolder(icon, false); + return new ViewHolder(icon, false /* isSectionRow */, false /* isEmptyRow */); default: throw new RuntimeException("Unexpected view type"); } @@ -184,21 +205,33 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(ViewHolder holder, int position) { - AppInfo info = mApps.getApps().get(position); - if (info != AlphabeticalAppsList.SECTION_BREAK_INFO) { - BubbleTextView icon = (BubbleTextView) holder.mContent; - icon.applyFromApplicationInfo(info); + switch (holder.getItemViewType()) { + case ICON_VIEW_TYPE: + AppInfo info = mApps.getApps().get(position); + BubbleTextView icon = (BubbleTextView) holder.mContent; + icon.applyFromApplicationInfo(info); + break; + case EMPTY_VIEW_TYPE: + TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text); + emptyViewText.setText(mEmptySearchText); + break; } } @Override public int getItemCount() { + if (mApps.hasNoFilteredResults()) { + // For the empty view + return 1; + } return mApps.getApps().size(); } @Override public int getItemViewType(int position) { - if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { + if (mApps.hasNoFilteredResults()) { + return EMPTY_VIEW_TYPE; + } else if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { return SECTION_BREAK_VIEW_TYPE; } return ICON_VIEW_TYPE; diff --git a/src/com/android/launcher3/AppsListAdapter.java b/src/com/android/launcher3/AppsListAdapter.java index 8ac381e79..e1f4d3578 100644 --- a/src/com/android/launcher3/AppsListAdapter.java +++ b/src/com/android/launcher3/AppsListAdapter.java @@ -30,12 +30,14 @@ class AppsListAdapter extends RecyclerView.Adapter { private static final int SECTION_BREAK_VIEW_TYPE = 0; private static final int ICON_VIEW_TYPE = 1; + private static final int EMPTY_VIEW_TYPE = 2; private LayoutInflater mLayoutInflater; private AlphabeticalAppsList mApps; private View.OnTouchListener mTouchListener; private View.OnClickListener mIconClickListener; private View.OnLongClickListener mIconLongClickListener; + private String mEmptySearchText; public AppsListAdapter(Context context, AlphabeticalAppsList apps, View.OnTouchListener touchListener, View.OnClickListener iconClickListener, @@ -51,9 +53,19 @@ class AppsListAdapter extends RecyclerView.Adapter { return new LinearLayoutManager(context); } + /** + * Sets the text to show when there are no apps. + */ + public void setEmptySearchText(String query) { + mEmptySearchText = query; + } + @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { + case EMPTY_VIEW_TYPE: + return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent, + false)); case SECTION_BREAK_VIEW_TYPE: return new ViewHolder(new View(parent.getContext())); case ICON_VIEW_TYPE: @@ -79,39 +91,51 @@ class AppsListAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(ViewHolder holder, int position) { - AppInfo info = mApps.getApps().get(position); - if (info != AlphabeticalAppsList.SECTION_BREAK_INFO) { - ViewGroup content = (ViewGroup) holder.mContent; - String sectionDescription = mApps.getSectionNameForApp(info); + switch (holder.getItemViewType()) { + case ICON_VIEW_TYPE: + AppInfo info = mApps.getApps().get(position); + ViewGroup content = (ViewGroup) holder.mContent; + String sectionDescription = mApps.getSectionNameForApp(info); - // Bind the section header - boolean showSectionHeader = true; - if (position > 0) { - AppInfo prevInfo = mApps.getApps().get(position - 1); - showSectionHeader = (prevInfo == AlphabeticalAppsList.SECTION_BREAK_INFO); - } - TextView tv = (TextView) content.findViewById(R.id.section); - if (showSectionHeader) { - tv.setText(sectionDescription); - tv.setVisibility(View.VISIBLE); - } else { - tv.setVisibility(View.INVISIBLE); - } + // Bind the section header + boolean showSectionHeader = true; + if (position > 0) { + AppInfo prevInfo = mApps.getApps().get(position - 1); + showSectionHeader = (prevInfo == AlphabeticalAppsList.SECTION_BREAK_INFO); + } + TextView tv = (TextView) content.findViewById(R.id.section); + if (showSectionHeader) { + tv.setText(sectionDescription); + tv.setVisibility(View.VISIBLE); + } else { + tv.setVisibility(View.INVISIBLE); + } - // Bind the icon - BubbleTextView icon = (BubbleTextView) content.getChildAt(1); - icon.applyFromApplicationInfo(info); + // Bind the icon + BubbleTextView icon = (BubbleTextView) content.getChildAt(1); + icon.applyFromApplicationInfo(info); + break; + case EMPTY_VIEW_TYPE: + TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text); + emptyViewText.setText(mEmptySearchText); + break; } } @Override public int getItemCount() { + if (mApps.hasNoFilteredResults()) { + // For the empty view + return 1; + } return mApps.getApps().size(); } @Override public int getItemViewType(int position) { - if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { + if (mApps.hasNoFilteredResults()) { + return EMPTY_VIEW_TYPE; + } else if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { return SECTION_BREAK_VIEW_TYPE; } return ICON_VIEW_TYPE; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index fabae5702..8ef234bb0 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -161,7 +161,8 @@ public class BubbleTextView extends TextView { if (info.contentDescription != null) { setContentDescription(info.contentDescription); } - setTag(info); + // We don't need to check the info since it's not a ShortcutInfo + super.setTag(info); } @Override diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java index 602a84566..47e1b7a98 100644 --- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -27,7 +27,7 @@ class BaseAlphabeticIndex { /** * Returns the index of the bucket in which the given string should appear. */ - public int getBucketIndex(String s) { + protected int getBucketIndex(String s) { if (s.isEmpty()) { return UNKNOWN_BUCKET_INDEX; } @@ -41,7 +41,7 @@ class BaseAlphabeticIndex { /** * Returns the label for the bucket at the given index (as returned by getBucketIndex). */ - public String getBucketLabel(int index) { + protected String getBucketLabel(int index) { return BUCKETS.substring(index, index + 1); } } @@ -99,12 +99,30 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { } } + /** + * Computes the section name for an given string {@param s}. + */ + public String computeSectionName(String s) { + String sectionName = getBucketLabel(getBucketIndex(s)); + if (sectionName.trim().isEmpty() && s.length() > 0) { + boolean startsWithDigit = Character.isDigit(s.charAt(0)); + if (startsWithDigit) { + // Digit section + return "#"; + } else { + // Unknown section + return "\u2022"; + } + } + return sectionName; + } + /** * Returns the index of the bucket in which {@param s} should appear. * Function is synchronized because underlying routine walks an iterator * whose state is maintained inside the index object. */ - public int getBucketIndex(String s) { + protected int getBucketIndex(String s) { if (mHasValidAlphabeticIndex) { try { return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s); @@ -118,7 +136,7 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { /** * Returns the label for the bucket at the given index (as returned by getBucketIndex). */ - public String getBucketLabel(int index) { + protected String getBucketLabel(int index) { if (mHasValidAlphabeticIndex) { try { return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index); -- cgit v1.2.3 From ef30a2a8312b83985a8ad3eb8feebbb944cbae01 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 13 Mar 2015 10:26:27 -0700 Subject: [DO NOT MERGE] Fixing build error in launcher_protoutil_lib > Since test build rules were included before the launcher_protoutil_lib rule, all the following paths got prefixed with "tests/" giving file not found error Change-Id: I13f96fc2f14336b0ff40f086176cc4afbc254791 (cherry picked from commit d68725cd20be0c1d5ebd583da5fde0f007a69bf8) --- Android.mk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index 110117be4..340caca0b 100644 --- a/Android.mk +++ b/Android.mk @@ -47,8 +47,6 @@ LOCAL_PROGUARD_FLAG_FILES := proguard.flags include $(BUILD_PACKAGE) -include $(call all-makefiles-under,$(LOCAL_PATH)) - # # Protocol Buffer Debug Utility in Java # @@ -86,3 +84,6 @@ $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/util/etc/launcher_protoutil | $(ACP) $(hide) chmod 755 $@ INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE) + +# ================================================== +include $(call all-makefiles-under,$(LOCAL_PATH)) -- cgit v1.2.3 From b4cd42a1df69ec887d0c66e8fe6fe1f27ebbf9bc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 16 Mar 2015 14:10:24 -0700 Subject: Extracting out CacheKey to a separate class Change-Id: Ifdd7cc79668b34298e3f788ee684080cecb86c3e --- src/com/android/launcher3/IconCache.java | 42 +++++-------------- src/com/android/launcher3/util/ComponentKey.java | 51 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 31 deletions(-) create mode 100644 src/com/android/launcher3/util/ComponentKey.java diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 91d4aaf21..43f838e7f 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -40,6 +40,7 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.ComponentKey; import java.util.HashMap; import java.util.HashSet; @@ -67,35 +68,14 @@ public class IconCache { public CharSequence contentDescription; } - private static class CacheKey { - public ComponentName componentName; - public UserHandleCompat user; - - CacheKey(ComponentName componentName, UserHandleCompat user) { - this.componentName = componentName; - this.user = user; - } - - @Override - public int hashCode() { - return componentName.hashCode() + user.hashCode(); - } - - @Override - public boolean equals(Object o) { - CacheKey other = (CacheKey) o; - return other.componentName.equals(componentName) && other.user.equals(user); - } - } - private final HashMap mDefaultIcons = new HashMap(); private final Context mContext; private final PackageManager mPackageManager; private final UserManagerCompat mUserManager; private final LauncherAppsCompat mLauncherApps; - private final HashMap mCache = - new HashMap(INITIAL_ICON_CACHE_CAPACITY); + private final HashMap mCache = + new HashMap(INITIAL_ICON_CACHE_CAPACITY); private final int mIconDpi; private final IconDB mIconDb; @@ -180,21 +160,21 @@ public class IconCache { * Remove any records for the supplied ComponentName. */ public synchronized void remove(ComponentName componentName, UserHandleCompat user) { - mCache.remove(new CacheKey(componentName, user)); + mCache.remove(new ComponentKey(componentName, user)); } /** * Remove any records for the supplied package name from memory. */ private void removeFromMemCacheLocked(String packageName, UserHandleCompat user) { - HashSet forDeletion = new HashSet(); - for (CacheKey key: mCache.keySet()) { + HashSet forDeletion = new HashSet(); + for (ComponentKey key: mCache.keySet()) { if (key.componentName.getPackageName().equals(packageName) && key.user.equals(user)) { forDeletion.add(key); } } - for (CacheKey condemned: forDeletion) { + for (ComponentKey condemned: forDeletion) { mCache.remove(condemned); } } @@ -324,7 +304,7 @@ public class IconCache { entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext); entry.title = app.getLabel(); entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); - mCache.put(new CacheKey(app.getComponentName(), app.getUser()), entry); + mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry); ContentValues values = new ContentValues(); values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(entry.icon)); @@ -344,7 +324,7 @@ public class IconCache { * Empty out the cache that aren't of the correct grid size */ public synchronized void flushInvalidIcons(DeviceProfile grid) { - Iterator> it = mCache.entrySet().iterator(); + Iterator> it = mCache.entrySet().iterator(); while (it.hasNext()) { final CacheEntry e = it.next().getValue(); if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx @@ -426,7 +406,7 @@ public class IconCache { */ private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info, UserHandleCompat user, boolean usePackageIcon) { - CacheKey cacheKey = new CacheKey(componentName, user); + ComponentKey cacheKey = new ComponentKey(componentName, user); CacheEntry entry = mCache.get(cacheKey); if (entry == null) { entry = new CacheEntry(); @@ -487,7 +467,7 @@ public class IconCache { */ private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) { ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME);; - CacheKey cacheKey = new CacheKey(cn, user); + ComponentKey cacheKey = new ComponentKey(cn, user); CacheEntry entry = mCache.get(cacheKey); if (entry == null) { entry = new CacheEntry(); diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java new file mode 100644 index 000000000..0f17f009e --- /dev/null +++ b/src/com/android/launcher3/util/ComponentKey.java @@ -0,0 +1,51 @@ +package com.android.launcher3.util; + +/** + * 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. + */ + +import android.content.ComponentName; + +import com.android.launcher3.compat.UserHandleCompat; + +import java.util.Arrays; + +public class ComponentKey { + + public final ComponentName componentName; + public final UserHandleCompat user; + + private final int mHashCode; + + public ComponentKey(ComponentName componentName, UserHandleCompat user) { + assert (componentName != null); + assert (user != null); + this.componentName = componentName; + this.user = user; + mHashCode = Arrays.hashCode(new Object[] {componentName, user}); + + } + + @Override + public int hashCode() { + return mHashCode; + } + + @Override + public boolean equals(Object o) { + ComponentKey other = (ComponentKey) o; + return other.componentName.equals(componentName) && other.user.equals(user); + } +} \ No newline at end of file -- cgit v1.2.3 From aa2ab254ea6b59dfe4183015e76c31262036282d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 16 Mar 2015 12:39:05 -0700 Subject: Tweaking the apps list fast scroller. - Making the view span the full width so that you can grab the scroller on the edge of the screen. - Offsetting the fast-scoll popup so that you can see it as you scrub. Change-Id: If1b1934bbeac0660d829cfc29c9e588df927c5e5 --- res/drawable/apps_list_bg.xml | 9 ++++----- res/drawable/apps_list_bg_inset.xml | 23 ++++++++++++++++++++++ res/drawable/apps_list_fastscroll_bg.xml | 9 ++++++--- res/drawable/apps_list_scrollbar_thumb.xml | 2 +- res/drawable/apps_reveal_bg.xml | 20 +++++++++++++++++++ res/drawable/apps_reveal_bg_inset.xml | 21 ++++++++++++++++++++ res/layout-sw600dp/apps_view.xml | 1 - res/layout/apps_list_view.xml | 7 +++++-- res/layout/apps_reveal_view.xml | 2 +- res/layout/apps_view.xml | 16 ++++++++++++--- res/values/dimens.xml | 6 ++++-- .../launcher3/AppsContainerRecyclerView.java | 4 +++- src/com/android/launcher3/AppsContainerView.java | 15 +++++++------- src/com/android/launcher3/AppsGridAdapter.java | 9 ++++++--- 14 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 res/drawable/apps_list_bg_inset.xml create mode 100644 res/drawable/apps_reveal_bg.xml create mode 100644 res/drawable/apps_reveal_bg_inset.xml diff --git a/res/drawable/apps_list_bg.xml b/res/drawable/apps_list_bg.xml index 61f1c083a..64177c16b 100644 --- a/res/drawable/apps_list_bg.xml +++ b/res/drawable/apps_list_bg.xml @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - \ No newline at end of file + \ No newline at end of file diff --git a/res/drawable/apps_list_bg_inset.xml b/res/drawable/apps_list_bg_inset.xml new file mode 100644 index 000000000..5ea78952f --- /dev/null +++ b/res/drawable/apps_list_bg_inset.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/apps_list_fastscroll_bg.xml b/res/drawable/apps_list_fastscroll_bg.xml index 4ec18488b..780d3b0c3 100644 --- a/res/drawable/apps_list_fastscroll_bg.xml +++ b/res/drawable/apps_list_fastscroll_bg.xml @@ -18,7 +18,10 @@ android:shape="rectangle"> - + android:width="64dp" + android:height="64dp" /> + \ No newline at end of file diff --git a/res/drawable/apps_list_scrollbar_thumb.xml b/res/drawable/apps_list_scrollbar_thumb.xml index ddd65b231..59383a5bb 100644 --- a/res/drawable/apps_list_scrollbar_thumb.xml +++ b/res/drawable/apps_list_scrollbar_thumb.xml @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/res/drawable/apps_reveal_bg.xml b/res/drawable/apps_reveal_bg.xml new file mode 100644 index 000000000..47c608f85 --- /dev/null +++ b/res/drawable/apps_reveal_bg.xml @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/res/drawable/apps_reveal_bg_inset.xml b/res/drawable/apps_reveal_bg_inset.xml new file mode 100644 index 000000000..61f1c083a --- /dev/null +++ b/res/drawable/apps_reveal_bg_inset.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml index 0628ca6d3..6f22460fa 100644 --- a/res/layout-sw600dp/apps_view.xml +++ b/res/layout-sw600dp/apps_view.xml @@ -18,7 +18,6 @@ android:id="@+id/apps_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="8dp" android:background="#22000000" android:descendantFocusability="afterDescendants"> + android:descendantFocusability="afterDescendants" + android:background="@drawable/apps_list_bg" /> \ No newline at end of file diff --git a/res/layout/apps_reveal_view.xml b/res/layout/apps_reveal_view.xml index 19e462bee..bc93359c1 100644 --- a/res/layout/apps_reveal_view.xml +++ b/res/layout/apps_reveal_view.xml @@ -21,5 +21,5 @@ android:layout_gravity="center" android:elevation="15dp" android:visibility="invisible" - android:background="@drawable/apps_list_bg" + android:background="@drawable/apps_reveal_bg" android:focusable="false" /> \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml index 00f3cca88..86d67e15f 100644 --- a/res/layout/apps_view.xml +++ b/res/layout/apps_view.xml @@ -13,16 +13,26 @@ See the License for the specific language governing permissions and limitations under the License. --> + + layout="@layout/apps_reveal_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> + layout="@layout/apps_list_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 9b4c56094..b9b9a2412 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -48,12 +48,14 @@ 0dp + 8dp 52dp 64dp 24sp - 48dp + 6dp + 40dp 64dp - 48dp + 40dp - + Add To Workspace - + Item added to workspace - + Item removed from workspace - + Move Item - + Move to empty cell %1$s, %2$s - + Item moved - + Add to folder: %1$s - + Item added to folder - + Create folder with: %1$s - + Folder created -- cgit v1.2.3 From 61e3a972e710d6636c13c342d6464fd862bff249 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 16 Mar 2015 17:23:58 -0700 Subject: Add dummy string to Launcher3Tests resources to fix build error. TL;DR;; since ag/656186 which moved the all-makefiles-under call to the bottom of the launcher's Android.mk file, resources for test package will not build without a dummy file. This change is necessary for the test team to continuously run the android tests. b/19566571 Change-Id: I4ff422ac019df4447348d953c5dd606d2a0dcc58 --- tests/res/values/string.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/res/values/string.xml diff --git a/tests/res/values/string.xml b/tests/res/values/string.xml new file mode 100644 index 000000000..3c1ec5c61 --- /dev/null +++ b/tests/res/values/string.xml @@ -0,0 +1,21 @@ + + + + + + Dummy string for tests. + + -- cgit v1.2.3 From 18bfaafd3da45dce7b5f73eaa1665f228353338c Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 17 Mar 2015 11:32:21 -0700 Subject: key event focus logic should support large tablets TL;DR;; On smaller tablets, landscape = vertical hotseat bar, and portrait = horizontal hotseat bar. However, in larger tablets, hotseat bar is always horizontal. This is now correctly handled using DeviceProfile.isVerticalBar method. b/19732584 Change-Id: I1035c89b4685be12dbc863f8a1465047a5fec6a6 --- src/com/android/launcher3/DeviceProfile.java | 5 +++ src/com/android/launcher3/FocusHelper.java | 43 +++++++++++++++----------- src/com/android/launcher3/util/FocusLogic.java | 13 +++----- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index ddd300257..bc9ef763d 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -81,6 +81,7 @@ public class DeviceProfile { boolean isTablet; boolean isLargeTablet; boolean isLayoutRtl; + boolean transposeLayoutWithOrientation; int desiredWorkspaceLeftRightMarginPx; @@ -699,6 +700,10 @@ public class DeviceProfile { return isLargeTablet; } + /** + * When {@code true}, hotseat is on the bottom row when in landscape mode. + * If {@code false}, hotseat is on the right column when in landscape mode. + */ boolean isVerticalBarLayout() { return isLandscape && transposeLayoutWithOrientation; } diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index b090a7c3f..fc6895201 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -207,12 +207,13 @@ public class FocusHelper { if (e.getAction() == KeyEvent.ACTION_UP || !consume) { return consume; } - int orientation = v.getResources().getConfiguration().orientation; + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile profile = app.getDynamicGrid().getDeviceProfile(); if (DEBUG) { Log.v(TAG, String.format( - "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, orientation=%d", - KeyEvent.keyCodeToString(keyCode), orientation)); + "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s", + KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout())); } // Initialize the variables. @@ -226,6 +227,8 @@ public class FocusHelper { int countX = -1; int countY = -1; int iconIndex = findIndexOfView(hotseatParent, v); + int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets() + .getChildAt(iconIndex).getLayoutParams()).cellX; final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex); final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); @@ -234,23 +237,25 @@ public class FocusHelper { int[][] matrix = null; if (keyCode == KeyEvent.KEYCODE_DPAD_UP && - orientation == Configuration.ORIENTATION_PORTRAIT) { - matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, - hotseat.getAllAppsButtonRank(), true /* include all apps icon */); + !profile.isVerticalBarLayout()) { + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, + true /* hotseat horizontal */, hotseat.getAllAppsButtonRank(), + iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */); iconIndex += iconParent.getChildCount(); countX = iconLayout.getCountX(); countY = iconLayout.getCountY() + hotseatLayout.getCountY(); parent = iconParent; } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && - orientation == Configuration.ORIENTATION_LANDSCAPE) { - matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, - hotseat.getAllAppsButtonRank(), true /* include all apps icon */); + profile.isVerticalBarLayout()) { + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, + false /* hotseat horizontal */, hotseat.getAllAppsButtonRank(), + iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */); iconIndex += iconParent.getChildCount(); countX = iconLayout.getCountX() + hotseatLayout.getCountX(); countY = iconLayout.getCountY(); parent = iconParent; } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && - orientation == Configuration.ORIENTATION_LANDSCAPE) { + profile.isVerticalBarLayout()) { keyCode = KeyEvent.KEYCODE_PAGE_DOWN; }else { // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the @@ -296,10 +301,13 @@ public class FocusHelper { if (e.getAction() == KeyEvent.ACTION_UP || !consume) { return consume; } - int orientation = v.getResources().getConfiguration().orientation; + + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile profile = app.getDynamicGrid().getDeviceProfile(); + if (DEBUG) { - Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] orientation=%d", - KeyEvent.keyCodeToString(keyCode), orientation)); + Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s", + KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout())); } // Initialize the variables. @@ -322,14 +330,13 @@ public class FocusHelper { // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended // with the hotseat. - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && - orientation == Configuration.ORIENTATION_PORTRAIT) { - matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) { + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */, hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */); countY = countY + 1; } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && - orientation == Configuration.ORIENTATION_LANDSCAPE) { - matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation, + profile.isVerticalBarLayout()) { + matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */, hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */); countX = countX + 1; } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index 0c6bfbf35..6e80c2f21 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -184,21 +184,18 @@ public class FocusLogic { */ // TODO: get rid of the dynamic matrix creation public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout, - int orientation, int allappsiconRank, boolean includeAllappsicon) { + boolean isHorizontal, int allappsiconRank, boolean includeAllappsicon) { ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets(); int m, n; - if (orientation == Configuration.ORIENTATION_PORTRAIT) { + if (isHorizontal) { m = iconLayout.getCountX(); n = iconLayout.getCountY() + hotseatLayout.getCountY(); - } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + } else { m = iconLayout.getCountX() + hotseatLayout.getCountX(); n = iconLayout.getCountY(); - } else { - throw new IllegalStateException(String.format( - "orientation type=%d is not supported for key board events.", orientation)); } int[][] matrix = createFullMatrix(m, n, false /* set all cell to empty */); @@ -215,7 +212,7 @@ public class FocusLogic { // is truncated. If it is negative, then all apps icon index is not inserted. for(int i = hotseatParent.getChildCount() - 1; i >= (includeAllappsicon ? 0 : 1); i--) { int delta = 0; - if (orientation == Configuration.ORIENTATION_PORTRAIT) { + if (isHorizontal) { int cx = ((CellLayout.LayoutParams) hotseatParent.getChildAt(i).getLayoutParams()).cellX; if ((includeAllappsicon && cx >= allappsiconRank) || @@ -223,7 +220,7 @@ public class FocusLogic { delta = -1; } matrix[cx + delta][iconLayout.getCountY()] = iconParent.getChildCount() + i; - } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + } else { int cy = ((CellLayout.LayoutParams) hotseatParent.getChildAt(i).getLayoutParams()).cellY; if ((includeAllappsicon && cy >= allappsiconRank) || -- cgit v1.2.3 From 5d85c44fd873c740dc191b28424c2ee367d730a2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Mar 2015 13:14:47 -0700 Subject: Adding sort switch in FolderPagedView > Adding options column in DB to store generation purpose flags > Storing isSorted flag in FolderInfo > Adding a switch for A-Z sorting (only visible if pageCount > 1) > When in sorted mode, spring-load snaps to the target location for 1.5 seconds Change-Id: I8c7c778d2cc3ccbd35a2890a1a705e1c1a7e9a66 --- res/layout/user_folder_scroll.xml | 34 +++- res/values/strings.xml | 2 + src/com/android/launcher3/Folder.java | 28 ++- src/com/android/launcher3/FolderCellLayout.java | 2 +- src/com/android/launcher3/FolderInfo.java | 32 ++++ src/com/android/launcher3/FolderPagedView.java | 228 ++++++++++++++++++++++-- src/com/android/launcher3/LauncherModel.java | 8 +- src/com/android/launcher3/LauncherProvider.java | 43 +++-- src/com/android/launcher3/LauncherSettings.java | 6 + 9 files changed, 336 insertions(+), 47 deletions(-) diff --git a/res/layout/user_folder_scroll.xml b/res/layout/user_folder_scroll.xml index 421e426cb..12e5097f3 100644 --- a/res/layout/user_folder_scroll.xml +++ b/res/layout/user_folder_scroll.xml @@ -45,7 +45,9 @@ android:id="@+id/folder_footer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" > + android:orientation="horizontal" + android:paddingStart="12dp" + android:paddingEnd="8dp" > + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index a1e460183..0b34d00a8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -89,6 +89,8 @@ OK Cancel + + A-Z diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 7ff60de4f..deb94ca28 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -85,6 +85,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList */ public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY; + /** + * Time in milliseconds for which an icon sticks to the target position + * in case of a sorted folder. + */ + private static final int SORTED_STICKY_REORDER_DELAY = 1500; + /** * Fraction of icon width which behave as scroll region. */ @@ -423,8 +429,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (!(getParent() instanceof DragLayer)) return; if (ALLOW_FOLDER_SCROLL) { - // Always open on the first page. - mPagedView.snapToPageImmediately(0); + mPagedView.completePendingPageChanges(); + if (!(mDragInProgress && mPagedView.mIsSorted)) { + // Open on the first page. + mPagedView.snapToPageImmediately(0); + } } Animator openFolderAnim = null; @@ -531,9 +540,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void beginExternalDrag(ShortcutInfo item) { mCurrentDragInfo = item; - mEmptyCellRank = mContent.allocateNewLastItemRank(); + mEmptyCellRank = mContent.allocateRankForNewItem(item); mIsExternalDrag = true; mDragInProgress = true; + if (ALLOW_FOLDER_SCROLL && mPagedView.mIsSorted) { + mScrollPauseAlarm.setOnAlarmListener(null); + mScrollPauseAlarm.cancelAlarm(); + mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY); + } + } private void sendCustomAccessibilityEvent(int type, String text) { @@ -747,6 +762,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (!successfulDrop) { mSuppressFolderDeletion = true; } + mScrollPauseAlarm.cancelAlarm(); completeDragExit(); } } @@ -1155,7 +1171,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // If the item was dropped onto this open folder, we have done the work associated // with adding the item to the folder, as indicated by mSuppressOnAdd being set if (mSuppressOnAdd) return; - mContent.createAndAddViewForRank(item, mContent.allocateNewLastItemRank()); + mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item)); LauncherModel.addOrMoveItemInDatabase( mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); } @@ -1304,10 +1320,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList ArrayList bindItems(ArrayList children); /** - * Create space for a new item at the end, and returns the rank for that item. + * Create space for a new item, and returns the rank for that item. * Resizes the content if necessary. */ - int allocateNewLastItemRank(); + int allocateRankForNewItem(ShortcutInfo info); View createAndAddViewForRank(ShortcutInfo item, int rank); diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java index 1566912b4..8585addd5 100644 --- a/src/com/android/launcher3/FolderCellLayout.java +++ b/src/com/android/launcher3/FolderCellLayout.java @@ -133,7 +133,7 @@ public class FolderCellLayout extends CellLayout implements Folder.FolderContent } @Override - public int allocateNewLastItemRank() { + public int allocateRankForNewItem(ShortcutInfo info) { int rank = getItemCount(); mFolder.rearrangeChildren(rank + 1); return rank; diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 85a792f4b..3240cbf22 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -29,11 +29,20 @@ import java.util.Arrays; */ public class FolderInfo extends ItemInfo { + public static final int NO_FLAGS = 0x00000000; + + /** + * The folder is locked in sorted mode + */ + public static final int FLAG_ITEMS_SORTED = 0x00000001; + /** * Whether this folder has been opened */ boolean opened; + public int options; + /** * The apps and shortcuts */ @@ -83,6 +92,8 @@ public class FolderInfo extends ItemInfo { void onAddToDatabase(Context context, ContentValues values) { super.onAddToDatabase(context, values); values.put(LauncherSettings.Favorites.TITLE, title.toString()); + values.put(LauncherSettings.Favorites.OPTIONS, options); + } void addListener(FolderListener listener) { @@ -121,4 +132,25 @@ public class FolderInfo extends ItemInfo { + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")"; } + + public boolean hasOption(int optionFlag) { + return (options & optionFlag) != 0; + } + + /** + * @param option flag to set or clear + * @param isEnabled whether to set or clear the flag + * @param context if not null, save changes to the db. + */ + public void setOption(int option, boolean isEnabled, Context context) { + int oldOptions = options; + if (isEnabled) { + options |= option; + } else { + options &= ~option; + } + if (context != null && oldOptions != options) { + LauncherModel.updateItemInDatabase(context, this); + } + } } diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index b4a7a7546..529064444 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -20,15 +20,22 @@ import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.OvershootInterpolator; +import android.widget.Switch; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; import com.android.launcher3.PageIndicator.PageMarkerResources; import com.android.launcher3.Workspace.ItemOperator; +import java.text.Collator; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -40,14 +47,19 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { private static final int REORDER_ANIMATION_DURATION = 230; private static final int START_VIEW_REORDER_DELAY = 30; private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; + + private static final int SPAN_TO_PAGE_DURATION = 350; + private static final int SORT_ANIM_HIDE_DURATION = 130; + private static final int SORT_ANIM_SHOW_DURATION = 160; + private static final int[] sTempPosArray = new int[2]; // TODO: Remove this restriction - private static final int MAX_ITEMS_PER_PAGE = 3; + private static final int MAX_ITEMS_PER_PAGE = 4; private final LayoutInflater mInflater; private final IconCache mIconCache; - private final HashMap mPageChangingViews = new HashMap<>(); + private final HashMap mPendingAnimations = new HashMap<>(); private final int mMaxCountX; private final int mMaxCountY; @@ -61,6 +73,13 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { private FocusIndicatorView mFocusIndicatorView; private PagedFolderKeyEventListener mKeyListener; + private View mSortButton; + private Switch mSortSwitch; + private View mPageIndicator; + + private boolean mSortOperationPending; + boolean mIsSorted; + public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); LauncherAppState app = LauncherAppState.getInstance(); @@ -80,6 +99,134 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { mFolder = folder; mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); mKeyListener = new PagedFolderKeyEventListener(folder); + + mSortButton = folder.findViewById(R.id.folder_sort); + mSortButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + onSortClicked(); + } + }); + mPageIndicator = folder.findViewById(R.id.folder_page_indicator); + mSortSwitch = (Switch) folder.findViewById(R.id.folder_sort_switch); + } + + private void onSortClicked() { + if (mSortOperationPending) { + return; + } + if (mIsSorted) { + setIsSorted(false, true); + } else { + mSortOperationPending = true; + doSort(); + } + } + + private void setIsSorted(boolean isSorted, boolean saveChanges) { + mIsSorted = isSorted; + mSortSwitch.setChecked(isSorted); + mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted, + saveChanges ? mFolder.mLauncher : null); + } + + /** + * Sorts the contents of the folder and animates the icons on the first page to reflect + * the changes. + * Steps: + * 1. Scroll to first page + * 2. Sort all icons in one go + * 3. Re-apply the old IconInfos on the first page (so that there is no instant change) + * 4. Animate each view individually to reflect the new icon. + */ + private void doSort() { + if (!mSortOperationPending) { + return; + } + if (getNextPage() != 0) { + snapToPage(0, SPAN_TO_PAGE_DURATION, new DecelerateInterpolator()); + return; + } + + mSortOperationPending = false; + ShortcutInfo[][] oldItems = new ShortcutInfo[mGridCountX][mGridCountY]; + CellLayout currentPage = getCurrentCellLayout(); + for (int x = 0; x < mGridCountX; x++) { + for (int y = 0; y < mGridCountY; y++) { + View v = currentPage.getChildAt(x, y); + if (v != null) { + oldItems[x][y] = (ShortcutInfo) v.getTag(); + } + } + } + + ArrayList views = new ArrayList(mFolder.getItemsInReadingOrder()); + Collections.sort(views, new ViewComparator()); + arrangeChildren(views, views.size()); + + int delay = 0; + float delayAmount = START_VIEW_REORDER_DELAY; + final Interpolator hideInterpolator = new DecelerateInterpolator(2); + final Interpolator showInterpolator = new OvershootInterpolator(0.8f); + + currentPage = getCurrentCellLayout(); + for (int x = 0; x < mGridCountX; x++) { + for (int y = 0; y < mGridCountY; y++) { + final BubbleTextView v = (BubbleTextView) currentPage.getChildAt(x, y); + if (v != null) { + final ShortcutInfo info = (ShortcutInfo) v.getTag(); + final Runnable clearPending = new Runnable() { + + @Override + public void run() { + mPendingAnimations.remove(v); + v.setScaleX(1); + v.setScaleY(1); + } + }; + if (oldItems[x][y] == null) { + v.setScaleX(0); + v.setScaleY(0); + v.animate().setDuration(SORT_ANIM_SHOW_DURATION) + .setStartDelay(SORT_ANIM_HIDE_DURATION + delay) + .scaleX(1).scaleY(1).setInterpolator(showInterpolator) + .withEndAction(clearPending); + mPendingAnimations.put(v, clearPending); + } else { + // Apply the old iconInfo so that there is no sudden change. + v.applyFromShortcutInfo(oldItems[x][y], mIconCache, false); + v.animate().setStartDelay(delay).setDuration(SORT_ANIM_HIDE_DURATION) + .scaleX(0).scaleY(0) + .setInterpolator(hideInterpolator) + .withEndAction(new Runnable() { + + @Override + public void run() { + // Apply the new iconInfo as part of the animation. + v.applyFromShortcutInfo(info, mIconCache, false); + v.animate().scaleX(1).scaleY(1) + .setDuration(SORT_ANIM_SHOW_DURATION).setStartDelay(0) + .setInterpolator(showInterpolator) + .withEndAction(clearPending); + } + }); + mPendingAnimations.put(v, new Runnable() { + + @Override + public void run() { + clearPending.run(); + v.applyFromShortcutInfo(info, mIconCache, false); + } + }); + } + delay += delayAmount; + delayAmount *= VIEW_REORDER_DELAY_FACTOR; + } + } + } + + setIsSorted(true, true); } /** @@ -125,6 +272,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { @Override public ArrayList bindItems(ArrayList items) { + mIsSorted = mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED); ArrayList icons = new ArrayList(); for (ShortcutInfo item : items) { icons.add(createNewView(item)); @@ -138,20 +286,33 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { * Also sets the current page to the last page. */ @Override - public int allocateNewLastItemRank() { + public int allocateRankForNewItem(ShortcutInfo info) { int rank = getItemCount(); - int total = rank + 1; - // Rearrange the items as the grid size might change. - mFolder.rearrangeChildren(total); + ArrayList views = new ArrayList(mFolder.getItemsInReadingOrder()); + if (mIsSorted) { + View tmp = new View(getContext()); + tmp.setTag(info); + int index = Collections.binarySearch(views, tmp, new ViewComparator()); + if (index < 0) { + rank = -index - 1; + } else { + // Item with same name already exists. + // We will just insert it before that item. + rank = index; + } + + } - setCurrentPage(getChildCount() - 1); + views.add(rank, null); + arrangeChildren(views, views.size(), false); + setCurrentPage(rank / mMaxItemsPerPage); return rank; } @Override public View createAndAddViewForRank(ShortcutInfo item, int rank) { View icon = createNewView(item); - addViewForRank(createNewView(item), item, rank); + addViewForRank(icon, item, rank); return icon; } @@ -259,6 +420,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { int position = 0; int newX, newY, rank; + boolean isSorted = mIsSorted; + + ViewComparator comparator = new ViewComparator(); + View lastView = null; rank = 0; for (int i = 0; i < itemCount; i++) { View v = list.size() > i ? list.get(i) : null; @@ -273,6 +438,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } if (v != null) { + if (lastView != null) { + isSorted &= comparator.compare(lastView, v) <= 0; + } + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); newX = position % mGridCountX; newY = position / mGridCountX; @@ -292,6 +461,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); } + lastView = v; rank ++; position++; } @@ -305,6 +475,19 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { if (removed) { setCurrentPage(0); } + + setIsSorted(isSorted, saveChanges); + + // Update footer + if (getPageCount() > 1) { + mPageIndicator.setVisibility(View.VISIBLE); + mSortButton.setVisibility(View.VISIBLE); + mFolder.mFolderName.setGravity(Gravity.START); + } else { + mPageIndicator.setVisibility(View.GONE); + mSortButton.setVisibility(View.GONE); + mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL); + } } @Override @@ -407,6 +590,17 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { if (mFolder != null) { mFolder.updateTextViewFocus(); } + if (mSortOperationPending && getNextPage() == 0) { + post(new Runnable() { + + @Override + public void run() { + if (mSortOperationPending) { + doSort(); + } + } + }); + } } /** @@ -433,8 +627,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { * Finish animation all the views which are animating across pages */ public void completePendingPageChanges() { - if (!mPageChangingViews.isEmpty()) { - HashMap pendingViews = new HashMap<>(mPageChangingViews); + if (!mPendingAnimations.isEmpty()) { + HashMap pendingViews = new HashMap<>(mPendingAnimations); for (Map.Entry e : pendingViews.entrySet()) { e.getKey().animate().cancel(); e.getValue().run(); @@ -533,7 +727,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { @Override public void run() { - mPageChangingViews.remove(v); + mPendingAnimations.remove(v); v.setTranslationX(oldTranslateX); ((CellLayout) v.getParent().getParent()).removeView(v); addViewForRank(v, (ShortcutInfo) v.getTag(), newRank); @@ -544,7 +738,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { .setDuration(REORDER_ANIMATION_DURATION) .setStartDelay(0) .withEndAction(endAction); - mPageChangingViews.put(v, endAction); + mPendingAnimations.put(v, endAction); } } moveStart = rankToMove; @@ -569,4 +763,14 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } } } + + private static class ViewComparator implements Comparator { + private final Collator mCollator = Collator.getInstance(); + + @Override + public int compare(View lhs, View rhs) { + return mCollator.compare( ((ShortcutInfo) lhs.getTag()).title.toString(), + ((ShortcutInfo) rhs.getTag()).title.toString()); + } + } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 2fd9db2c3..d80debb07 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -961,6 +961,7 @@ public class LauncherModel extends BroadcastReceiver final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); + final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS); FolderInfo folderInfo = null; switch (c.getInt(itemTypeIndex)) { @@ -975,6 +976,7 @@ public class LauncherModel extends BroadcastReceiver folderInfo.screenId = c.getInt(screenIndex); folderInfo.cellX = c.getInt(cellXIndex); folderInfo.cellY = c.getInt(cellYIndex); + folderInfo.options = c.getInt(optionsIndex); return folderInfo; } @@ -1862,9 +1864,8 @@ public class LauncherModel extends BroadcastReceiver LauncherSettings.Favorites.RESTORED); final int profileIdIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.PROFILE_ID); - //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); - //final int displayModeIndex = c.getColumnIndexOrThrow( - // LauncherSettings.Favorites.DISPLAY_MODE); + final int optionsIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.OPTIONS); ShortcutInfo info; String intentDescription; @@ -2114,6 +2115,7 @@ public class LauncherModel extends BroadcastReceiver folderInfo.cellY = c.getInt(cellYIndex); folderInfo.spanX = 1; folderInfo.spanY = 1; + folderInfo.options = c.getInt(optionsIndex); // check & update map of what's occupied if (!checkItemPlacement(occupied, folderInfo)) { diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index b7a271e4f..59c8d929a 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -57,7 +57,7 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 22; + private static final int DATABASE_VERSION = 23; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -413,7 +413,8 @@ public class LauncherProvider extends ContentProvider { "modified INTEGER NOT NULL DEFAULT 0," + "restored INTEGER NOT NULL DEFAULT 0," + "profileId INTEGER DEFAULT " + userSerialNumber + "," + - "rank INTEGER NOT NULL DEFAULT 0" + + "rank INTEGER NOT NULL DEFAULT 0," + + "options INTEGER NOT NULL DEFAULT 0" + ");"); addWorkspacesTable(db); @@ -524,18 +525,9 @@ public class LauncherProvider extends ContentProvider { } } case 15: { - db.beginTransaction(); - try { - // Insert new column for holding restore status - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - } catch (SQLException ex) { - Log.e(TAG, ex.getMessage(), ex); + if (!addIntegerColumn(db, Favorites.RESTORED, 0)) { // Old version remains, which means we wipe old data break; - } finally { - db.endTransaction(); } } case 16: { @@ -573,6 +565,12 @@ public class LauncherProvider extends ContentProvider { break; } case 22: { + if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) { + // Old version remains, which means we wipe old data + break; + } + } + case 23: { // DB Upgraded successfully return; } @@ -682,20 +680,21 @@ public class LauncherProvider extends ContentProvider { } private boolean addProfileColumn(SQLiteDatabase db) { + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + // Default to the serial number of this user, for older + // shortcuts. + long userSerialNumber = userManager.getSerialNumberForUser( + UserHandleCompat.myUserHandle()); + return addIntegerColumn(db, Favorites.PROFILE_ID, userSerialNumber); + } + + private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) { db.beginTransaction(); try { - UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); - // Default to the serial number of this user, for older - // shortcuts. - long userSerialNumber = userManager.getSerialNumberForUser( - UserHandleCompat.myUserHandle()); - // Insert new column for holding user serial number - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN profileId INTEGER DEFAULT " - + userSerialNumber + ";"); + db.execSQL("ALTER TABLE favorites ADD COLUMN " + + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";"); db.setTransactionSuccessful(); } catch (SQLException ex) { - // Old version remains, which means we wipe old data Log.e(TAG, ex.getMessage(), ex); return false; } finally { diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 13fd7ee30..d161fbb3d 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -309,5 +309,11 @@ class LauncherSettings { *

Type: INTEGER

*/ static final String RANK = "rank"; + + /** + * Stores general flag based options for {@link ItemInfo}s. + *

Type: INTEGER

+ */ + static final String OPTIONS = "options"; } } -- cgit v1.2.3 From 34b6527cefd36fbd5da78464ce9771e379158552 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 11 Mar 2015 16:56:52 -0700 Subject: Lazy loading high res icons > Loading low-res icons for icons which are not visible on the homescreen. Change-Id: I8ac7bf09f6030ed554cb60a4cd402f3f36ffe12b --- src/com/android/launcher3/AllAppsList.java | 2 +- src/com/android/launcher3/AppInfo.java | 7 +- src/com/android/launcher3/BubbleTextView.java | 45 +++++++++ src/com/android/launcher3/Folder.java | 5 + src/com/android/launcher3/FolderIcon.java | 2 +- src/com/android/launcher3/FolderPagedView.java | 22 +++++ src/com/android/launcher3/IconCache.java | 125 ++++++++++++++++++++----- src/com/android/launcher3/LauncherModel.java | 22 +++-- src/com/android/launcher3/ShortcutInfo.java | 12 ++- src/com/android/launcher3/Workspace.java | 3 +- 10 files changed, 209 insertions(+), 36 deletions(-) diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 5ed7a629a..dd646bb22 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -148,7 +148,7 @@ class AllAppsList { if (applicationInfo == null) { add(new AppInfo(context, info, user, mIconCache)); } else { - mIconCache.getTitleAndIcon(applicationInfo, info); + mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */); modified.add(applicationInfo); } } diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 455c6d16d..a1391b232 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -45,6 +45,11 @@ public class AppInfo extends ItemInfo { */ Bitmap iconBitmap; + /** + * Indicates whether we're using a low res icon + */ + boolean usingLowResIcon; + /** * The time at which the app was first installed. */ @@ -79,7 +84,7 @@ public class AppInfo extends ItemInfo { flags = initFlags(info); firstInstallTime = info.getFirstInstallTime(); - iconCache.getTitleAndIcon(this, info); + iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */); intent = makeLaunchIntent(context, info, user); this.user = user; } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 8ef234bb0..50549cad0 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -34,6 +34,8 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import android.widget.TextView; +import com.android.launcher3.IconCache.IconLoadRequest; + /** * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan * because we want to make the bubble taller than the text and TextView's clip is @@ -74,6 +76,8 @@ public class BubbleTextView extends TextView { private boolean mStayPressed; private boolean mIgnorePressedStateChange; + private IconLoadRequest mIconLoadRequest; + public BubbleTextView(Context context) { this(context, null, 0); } @@ -163,6 +167,9 @@ public class BubbleTextView extends TextView { } // We don't need to check the info since it's not a ShortcutInfo super.setTag(info); + + // Verify high res immediately + verifyHighRes(); } @Override @@ -450,4 +457,42 @@ public class BubbleTextView extends TextView { } return icon; } + + /** + * Applies the item info if it is same as what the view is pointing to currently. + */ + public void reapplyItemInfo(final ItemInfo info) { + if (getTag() == info) { + mIconLoadRequest = null; + if (info instanceof AppInfo) { + applyFromApplicationInfo((AppInfo) info); + } else if (info instanceof ShortcutInfo) { + applyFromShortcutInfo((ShortcutInfo) info, + LauncherAppState.getInstance().getIconCache(), false); + } + } + } + + /** + * Verifies that the current icon is high-res otherwise posts a request to load the icon. + */ + public void verifyHighRes() { + if (mIconLoadRequest != null) { + mIconLoadRequest.cancel(); + mIconLoadRequest = null; + } + if (getTag() instanceof AppInfo) { + AppInfo info = (AppInfo) getTag(); + if (info.usingLowResIcon) { + mIconLoadRequest = LauncherAppState.getInstance().getIconCache() + .updateIconInBackground(BubbleTextView.this, info); + } + } else if (getTag() instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo) getTag(); + if (info.usingLowResIcon) { + mIconLoadRequest = LauncherAppState.getInstance().getIconCache() + .updateIconInBackground(BubbleTextView.this, info); + } + } + } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index deb94ca28..c7c6571ef 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -536,6 +536,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (mDragController.isDragging()) { mDragController.forceTouchMove(); } + + if (ALLOW_FOLDER_SCROLL) { + FolderPagedView pages = (FolderPagedView) mContent; + pages.verifyVisibleHighResIcons(pages.getNextPage()); + } } public void beginExternalDrag(ShortcutInfo item) { diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index a3e82959a..dbfedaafa 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -58,7 +58,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private CheckLongPressHelper mLongPressHelper; // The number of icons to display in the - private static final int NUM_ITEMS_IN_PREVIEW = 3; + public static final int NUM_ITEMS_IN_PREVIEW = 3; private static final int CONSUMPTION_ANIMATION_DURATION = 100; private static final int DROP_IN_ANIMATION_DURATION = 400; private static final int INITIAL_ITEM_ANIMATION_DURATION = 350; diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 529064444..21158b450 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -641,6 +641,28 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { return p == getNextPage(); } + @Override + protected void onPageBeginMoving() { + super.onPageBeginMoving(); + getVisiblePages(sTempPosArray); + for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) { + verifyVisibleHighResIcons(i); + } + } + + /** + * Ensures that all the icons on the given page are of high-res + */ + public void verifyVisibleHighResIcons(int pageNo) { + CellLayout page = getPageAt(pageNo); + if (page != null) { + ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets(); + for (int i = parent.getChildCount() - 1; i >= 0; i--) { + ((BubbleTextView) parent.getChildAt(i)).verifyHighRes(); + } + } + } + @Override public void realTimeReorder(int empty, int target) { completePendingPageChanges(); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 43f838e7f..57d23a7bb 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -31,8 +31,10 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.os.Handler; import android.text.TextUtils; import android.util.Log; @@ -62,10 +64,13 @@ public class IconCache { private static final boolean DEBUG = false; + private static final int LOW_RES_SCALE_FACTOR = 8; + private static class CacheEntry { public Bitmap icon; public CharSequence title; public CharSequence contentDescription; + public boolean isLowResIcon; } private final HashMap mDefaultIcons = @@ -79,6 +84,8 @@ public class IconCache { private final int mIconDpi; private final IconDB mIconDb; + private final Handler mWorkerHandler; + public IconCache(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); @@ -89,6 +96,8 @@ public class IconCache { mLauncherApps = LauncherAppsCompat.getInstance(mContext); mIconDpi = activityManager.getLauncherLargeIconDensity(); mIconDb = new IconDB(context); + + mWorkerHandler = new Handler(LauncherModel.sWorkerThread.getLooper()); } private Drawable getFullResDefaultActivityIcon() { @@ -306,10 +315,7 @@ public class IconCache { entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry); - ContentValues values = new ContentValues(); - values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(entry.icon)); - values.put(IconDB.COLUMN_LABEL, entry.title.toString()); - return values; + return mIconDb.newContentValues(entry.icon, entry.title.toString()); } @@ -335,16 +341,52 @@ public class IconCache { } /** - * Fill in "application" with the icon and label for "info." + * Fetches high-res icon for the provided ItemInfo and updates the caller when done. + * @return a request ID that can be used to cancel the request. */ - public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info) { - CacheEntry entry = cacheLocked(application.componentName, info, info.getUser(), false); + public IconLoadRequest updateIconInBackground(final BubbleTextView caller, final ItemInfo info) { + Runnable request = new Runnable() { + + @Override + public void run() { + if (info instanceof AppInfo) { + getTitleAndIcon((AppInfo) info, null, false); + } else if (info instanceof ShortcutInfo) { + ShortcutInfo st = (ShortcutInfo) info; + getTitleAndIcon(st, + st.promisedIntent != null ? st.promisedIntent : st.intent, + st.user, false); + } + caller.post(new Runnable() { + + @Override + public void run() { + caller.reapplyItemInfo(info); + } + }); + } + }; + mWorkerHandler.post(request); + return new IconLoadRequest(request, mWorkerHandler); + } + /** + * Fill in "application" with the icon and label for "info." + */ + public synchronized void getTitleAndIcon(AppInfo application, + LauncherActivityInfoCompat info, boolean useLowResIcon) { + CacheEntry entry = cacheLocked(application.componentName, info, + info == null ? application.user : info.getUser(), + false, useLowResIcon); application.title = entry.title; application.iconBitmap = entry.icon; application.contentDescription = entry.contentDescription; + application.usingLowResIcon = entry.isLowResIcon; } + /** + * Returns a high res icon for the given intent and user + */ public synchronized Bitmap getIcon(Intent intent, UserHandleCompat user) { ComponentName component = intent.getComponent(); // null info means not installed, but if we have a component from the intent then @@ -354,7 +396,7 @@ public class IconCache { } LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, user, true); + CacheEntry entry = cacheLocked(component, launcherActInfo, user, true, true); return entry.icon; } @@ -363,7 +405,7 @@ public class IconCache { * corresponding activity is not found, it reverts to the package icon. */ public synchronized void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, - UserHandleCompat user) { + UserHandleCompat user, boolean useLowResIcon) { ComponentName component = intent.getComponent(); // null info means not installed, but if we have a component from the intent then // we should still look in the cache for restored app icons. @@ -371,9 +413,10 @@ public class IconCache { shortcutInfo.setIcon(getDefaultIcon(user)); shortcutInfo.title = ""; shortcutInfo.usingFallbackIcon = true; + shortcutInfo.usingLowResIcon = false; } else { LauncherActivityInfoCompat info = mLauncherApps.resolveActivity(intent, user); - getTitleAndIcon(shortcutInfo, component, info, user, true); + getTitleAndIcon(shortcutInfo, component, info, user, true, useLowResIcon); } } @@ -382,11 +425,12 @@ public class IconCache { */ public synchronized void getTitleAndIcon( ShortcutInfo shortcutInfo, ComponentName component, LauncherActivityInfoCompat info, - UserHandleCompat user, boolean usePkgIcon) { - CacheEntry entry = cacheLocked(component, info, user, usePkgIcon); + UserHandleCompat user, boolean usePkgIcon, boolean useLowResIcon) { + CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon); shortcutInfo.setIcon(entry.icon); shortcutInfo.title = entry.title; shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); + shortcutInfo.usingLowResIcon = entry.isLowResIcon; } public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { @@ -405,15 +449,15 @@ public class IconCache { * This method is not thread safe, it must be called from a synchronized method. */ private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info, - UserHandleCompat user, boolean usePackageIcon) { + UserHandleCompat user, boolean usePackageIcon, boolean useLowResIcon) { ComponentKey cacheKey = new ComponentKey(componentName, user); CacheEntry entry = mCache.get(cacheKey); - if (entry == null) { + if (entry == null || (entry.isLowResIcon && !useLowResIcon)) { entry = new CacheEntry(); mCache.put(cacheKey, entry); // Check the DB first. - if (!getEntryFromDB(componentName, user, entry)) { + if (!getEntryFromDB(componentName, user, entry, useLowResIcon)) { if (info != null) { entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext); } else { @@ -509,25 +553,26 @@ public class IconCache { // pass } - ContentValues values = new ContentValues(); + ContentValues values = mIconDb.newContentValues(icon, label); values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString()); values.put(IconDB.COLUMN_USER, userSerial); - values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon)); - values.put(IconDB.COLUMN_LABEL, label); mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); } - private boolean getEntryFromDB(ComponentName component, UserHandleCompat user, CacheEntry entry) { + private boolean getEntryFromDB(ComponentName component, UserHandleCompat user, + CacheEntry entry, boolean lowRes) { Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, - new String[] {IconDB.COLUMN_ICON, IconDB.COLUMN_LABEL}, + new String[] {lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON, + IconDB.COLUMN_LABEL}, IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", new String[] {component.flattenToString(), Long.toString(mUserManager.getSerialNumberForUser(user))}, null, null, null); try { if (c.moveToNext()) { - entry.icon = Utilities.createIconBitmap(c, 0, mContext); + entry.icon = loadIconNoResize(c, 0); + entry.isLowResIcon = lowRes; entry.title = c.getString(1); if (entry.title == null) { entry.title = ""; @@ -543,8 +588,22 @@ public class IconCache { return false; } + public static class IconLoadRequest { + private final Runnable mRunnable; + private final Handler mHandler; + + IconLoadRequest(Runnable runnable, Handler handler) { + mRunnable = runnable; + mHandler = handler; + } + + public void cancel() { + mHandler.removeCallbacks(mRunnable); + } + } + private static final class IconDB extends SQLiteOpenHelper { - private final static int DB_VERSION = 1; + private final static int DB_VERSION = 2; private final static String TABLE_NAME = "icons"; private final static String COLUMN_ROWID = "rowid"; @@ -553,6 +612,7 @@ public class IconCache { private final static String COLUMN_LAST_UPDATED = "lastUpdated"; private final static String COLUMN_VERSION = "version"; private final static String COLUMN_ICON = "icon"; + private final static String COLUMN_ICON_LOW_RES = "icon_low_res"; private final static String COLUMN_LABEL = "label"; public IconDB(Context context) { @@ -567,6 +627,7 @@ public class IconCache { COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " + COLUMN_ICON + " BLOB, " + + COLUMN_ICON_LOW_RES + " BLOB, " + COLUMN_LABEL + " TEXT, " + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " + ");"); @@ -590,5 +651,25 @@ public class IconCache { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } + + public ContentValues newContentValues(Bitmap icon, String label) { + ContentValues values = new ContentValues(); + values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon)); + values.put(IconDB.COLUMN_ICON_LOW_RES, ItemInfo.flattenBitmap( + Bitmap.createScaledBitmap(icon, + icon.getWidth() / LOW_RES_SCALE_FACTOR, + icon.getHeight() / LOW_RES_SCALE_FACTOR, true))); + values.put(IconDB.COLUMN_LABEL, label); + return values; + } + } + + private static Bitmap loadIconNoResize(Cursor c, int iconIndex) { + byte[] data = c.getBlob(iconIndex); + try { + return BitmapFactory.decodeByteArray(data, 0, data.length); + } catch (Exception e) { + return null; + } } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d80debb07..1e16bafe8 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -117,7 +117,7 @@ public class LauncherModel extends BroadcastReceiver private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings"; - private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); + static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); static { sWorkerThread.start(); } @@ -2018,10 +2018,14 @@ public class LauncherModel extends BroadcastReceiver continue; } + container = c.getInt(containerIndex); + boolean useLowResIcon = container >= 0 && + c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW; + if (itemReplaced) { if (user.equals(UserHandleCompat.myUserHandle())) { info = getAppShortcutInfo(manager, intent, user, context, null, - iconIndex, titleIndex, false); + iconIndex, titleIndex, false, useLowResIcon); } else { // Don't replace items for other profiles. itemsToRemove.add(id); @@ -2032,7 +2036,8 @@ public class LauncherModel extends BroadcastReceiver Launcher.addDumpLog(TAG, "constructing info for partially restored package", true); - info = getRestoredItemInfo(c, titleIndex, intent, promiseType); + info = getRestoredItemInfo(c, titleIndex, intent, + promiseType, useLowResIcon); intent = getRestoredItemIntent(c, context, intent); } else { // Don't restore items for other profiles. @@ -2042,7 +2047,7 @@ public class LauncherModel extends BroadcastReceiver } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { info = getAppShortcutInfo(manager, intent, user, context, c, - iconIndex, titleIndex, allowMissingTarget); + iconIndex, titleIndex, allowMissingTarget, useLowResIcon); } else { info = getShortcutInfo(c, context, iconTypeIndex, iconPackageIndex, iconResourceIndex, iconIndex, @@ -2064,7 +2069,6 @@ public class LauncherModel extends BroadcastReceiver if (info != null) { info.id = id; info.intent = intent; - container = c.getInt(containerIndex); info.container = container; info.screenId = c.getInt(screenIndex); info.cellX = c.getInt(cellXIndex); @@ -3352,10 +3356,10 @@ public class LauncherModel extends BroadcastReceiver * to a package that is not yet installed on the system. */ public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent, - int promiseType) { + int promiseType, boolean useLowResIcon) { final ShortcutInfo info = new ShortcutInfo(); info.user = UserHandleCompat.myUserHandle(); - mIconCache.getTitleAndIcon(info, intent, info.user); + mIconCache.getTitleAndIcon(info, intent, info.user, useLowResIcon); if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) { String title = (cursor != null) ? cursor.getString(titleIndex) : null; @@ -3404,7 +3408,7 @@ public class LauncherModel extends BroadcastReceiver */ public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent, UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex, - boolean allowMissingTarget) { + boolean allowMissingTarget, boolean useLowResIcon) { if (user == null) { Log.d(TAG, "Null user found in getShortcutInfo"); return null; @@ -3426,7 +3430,7 @@ public class LauncherModel extends BroadcastReceiver } final ShortcutInfo info = new ShortcutInfo(); - mIconCache.getTitleAndIcon(info, componentName, lai, user, false); + mIconCache.getTitleAndIcon(info, componentName, lai, user, false, useLowResIcon); if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) { Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context); info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon); diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 08ffaa299..9f7da6c3d 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -83,6 +83,11 @@ public class ShortcutInfo extends ItemInfo { */ boolean usingFallbackIcon; + /** + * Indicates whether we're using a low res icon + */ + boolean usingLowResIcon; + /** * If isShortcut=true and customIcon=false, this contains a reference to the * shortcut icon as an application's resource. @@ -192,7 +197,8 @@ public class ShortcutInfo extends ItemInfo { public void updateIcon(IconCache iconCache) { if (itemType == Favorites.ITEM_TYPE_APPLICATION) { - iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user); + iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user, + shouldUseLowResIcon()); } } @@ -264,5 +270,9 @@ public class ShortcutInfo extends ItemInfo { mInstallProgress = progress; status |= FLAG_INSTALL_SESSION_ACTIVE; } + + public boolean shouldUseLowResIcon() { + return usingLowResIcon && container >= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW; + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a59e25e08..7ebdf3ace 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4817,7 +4817,8 @@ public class Workspace extends SmoothPagedView if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { // For auto install apps update the icon as well as label. mIconCache.getTitleAndIcon(shortcutInfo, - shortcutInfo.promisedIntent, user); + shortcutInfo.promisedIntent, user, + shortcutInfo.shouldUseLowResIcon()); } else { // Only update the icon for restored apps. shortcutInfo.updateIcon(mIconCache); -- cgit v1.2.3 From 8758ea050a9202e9733520898dda5d290644839e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Mar 2015 10:07:49 -0700 Subject: Using MainThreadExecuter instead of View.post Change-Id: Ie7ba88bd3bb412d998ecb51095cbaa74b3b39c10 --- src/com/android/launcher3/IconCache.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 57d23a7bb..39a80be78 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -73,8 +73,9 @@ public class IconCache { public boolean isLowResIcon; } - private final HashMap mDefaultIcons = - new HashMap(); + private final HashMap mDefaultIcons = new HashMap<>(); + private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + private final Context mContext; private final PackageManager mPackageManager; private final UserManagerCompat mUserManager; @@ -357,7 +358,7 @@ public class IconCache { st.promisedIntent != null ? st.promisedIntent : st.intent, st.user, false); } - caller.post(new Runnable() { + mMainThreadExecutor.execute(new Runnable() { @Override public void run() { -- cgit v1.2.3 From 2e688a8cc217b69cd6cd46a7a408ca3dbfa48cb9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Mar 2015 10:23:39 -0700 Subject: Invalidating sorted list after the item has been added Bug: 19815825 Change-Id: If10d19d41a2400b5a7350e40fa6116494dc9c5a6 --- src/com/android/launcher3/Folder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index c7c6571ef..5d8a865f7 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -1172,11 +1172,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void onAdd(ShortcutInfo item) { - mItemsInvalidated = true; // If the item was dropped onto this open folder, we have done the work associated // with adding the item to the folder, as indicated by mSuppressOnAdd being set if (mSuppressOnAdd) return; mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item)); + mItemsInvalidated = true; LauncherModel.addOrMoveItemInDatabase( mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); } -- cgit v1.2.3 From 4cad7538c9615303d291f5b52e960aaf0985828f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Mar 2015 15:56:30 -0700 Subject: Cleaning up widget preview loader > Removing unnecessary canvas cache > Removing unnecessary new bitmap creation Change-Id: Ic9f45c5a204ea4d32dbf93c21a6a7fd6baea4b51 --- .../android/launcher3/AppsCustomizePagedView.java | 17 +- .../android/launcher3/LauncherBackupHelper.java | 6 +- src/com/android/launcher3/PagedViewCellLayout.java | 16 -- src/com/android/launcher3/WidgetPreviewLoader.java | 320 ++++++--------------- 4 files changed, 101 insertions(+), 258 deletions(-) diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index bf368125f..3f2aa7036 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -38,7 +38,6 @@ import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.Toast; @@ -48,9 +47,7 @@ import com.android.launcher3.FocusHelper.PagedViewKeyListener; import com.android.launcher3.compat.AppWidgetManagerCompat; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; -import java.util.List; /** * A simple callback interface which also provides the results of the task. @@ -171,7 +168,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Dimens private int mContentWidth, mContentHeight; private int mWidgetCountX, mWidgetCountY; - private PagedViewCellLayout mWidgetSpacingLayout; private int mNumWidgetPages; // Previews & outlines @@ -219,7 +215,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); a.recycle(); - mWidgetSpacingLayout = new PagedViewCellLayout(getContext()); // The padding on the non-matched dimension for the default widget preview icons // (top + bottom) @@ -316,9 +311,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Force a measure to update recalculate the gaps mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); - int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); - int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); - mWidgetSpacingLayout.measure(widthSpec, heightSpec); final boolean hostIsTransitioning = getTabHost().isInTransition(); int page = getPageForComponent(mSaveInstanceStateItemIndex); @@ -596,13 +588,11 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); float minScale = 1.25f; - int maxWidth, maxHeight; - maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); - maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]); + int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); int[] previewSizeBeforeScale = new int[1]; preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, - spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale); + maxWidth, null, previewSizeBeforeScale); // Compare the size of the drag preview to the preview in the AppsCustomize tray int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], @@ -1075,8 +1065,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen maxPreviewHeight = maxSize[1]; } - getWidgetPreviewLoader().setPreviewSize( - maxPreviewWidth, maxPreviewHeight, mWidgetSpacingLayout); + getWidgetPreviewLoader().setPreviewSize(maxPreviewWidth, maxPreviewHeight); if (immediate) { AsyncTaskPageData data = new AsyncTaskPageData(page, items, maxPreviewWidth, maxPreviewHeight, null, null, getWidgetPreviewLoader()); diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 97ff32790..7849e5335 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -631,7 +631,6 @@ public class LauncherBackupHelper implements BackupHelper { } final ContentResolver cr = mContext.getContentResolver(); final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(mContext); - final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(mContext); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile(); if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx); @@ -666,8 +665,9 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount); if (backupWidgetCount < MAX_WIDGETS_PER_PASS) { if (DEBUG) Log.d(TAG, "saving widget " + backupKey); - previewLoader.setPreviewSize(spanX * profile.cellWidthPx, - spanY * profile.cellHeightPx, widgetSpacingLayout); + previewLoader.setPreviewSize( + spanX * profile.cellWidthPx, + spanY * profile.cellHeightPx); writeRowToBackup(key, packWidget(dpi, previewLoader, mIconCache, provider), data); mKeys.add(key); backupWidgetCount ++; diff --git a/src/com/android/launcher3/PagedViewCellLayout.java b/src/com/android/launcher3/PagedViewCellLayout.java index 2d9e10b9d..e3ad9dad1 100644 --- a/src/com/android/launcher3/PagedViewCellLayout.java +++ b/src/com/android/launcher3/PagedViewCellLayout.java @@ -348,22 +348,6 @@ public class PagedViewCellLayout extends ViewGroup implements Page { requestLayout(); } - /** - * Estimates the width that the number of hSpan cells will take up. - */ - public int estimateCellWidth(int hSpan) { - // TODO: we need to take widthGap into effect - return hSpan * mCellWidth; - } - - /** - * Estimates the height that the number of vSpan cells will take up. - */ - public int estimateCellHeight(int vSpan) { - // TODO: we need to take heightGap into effect - return vSpan * mCellHeight; - } - @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new PagedViewCellLayout.LayoutParams(getContext(), attrs); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 312814039..3d57acca4 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -16,19 +16,19 @@ import android.database.sqlite.SQLiteReadOnlyDatabaseException; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; -import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.Shader; +import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.util.Log; + import com.android.launcher3.compat.AppWidgetManagerCompat; import java.io.ByteArrayOutputStream; @@ -46,107 +46,27 @@ import java.util.concurrent.ExecutionException; public class WidgetPreviewLoader { - private static abstract class SoftReferenceThreadLocal { - private ThreadLocal> mThreadLocal; - public SoftReferenceThreadLocal() { - mThreadLocal = new ThreadLocal>(); - } - - abstract T initialValue(); - - public void set(T t) { - mThreadLocal.set(new SoftReference(t)); - } - - public T get() { - SoftReference reference = mThreadLocal.get(); - T obj; - if (reference == null) { - obj = initialValue(); - mThreadLocal.set(new SoftReference(obj)); - return obj; - } else { - obj = reference.get(); - if (obj == null) { - obj = initialValue(); - mThreadLocal.set(new SoftReference(obj)); - } - return obj; - } - } - } - - private static class CanvasCache extends SoftReferenceThreadLocal { - @Override - protected Canvas initialValue() { - return new Canvas(); - } - } - - private static class PaintCache extends SoftReferenceThreadLocal { - @Override - protected Paint initialValue() { - return null; - } - } - - private static class BitmapCache extends SoftReferenceThreadLocal { - @Override - protected Bitmap initialValue() { - return null; - } - } - - private static class RectCache extends SoftReferenceThreadLocal { - @Override - protected Rect initialValue() { - return new Rect(); - } - } - - private static class BitmapFactoryOptionsCache extends - SoftReferenceThreadLocal { - @Override - protected BitmapFactory.Options initialValue() { - return new BitmapFactory.Options(); - } - } - private static final String TAG = "WidgetPreviewLoader"; private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version"; private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; private static final HashSet sInvalidPackages = new HashSet(); - // Used for drawing shortcut previews - private final BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache(); - private final PaintCache mCachedShortcutPreviewPaint = new PaintCache(); - private final CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache(); - - // Used for drawing widget previews - private final CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache(); - private final RectCache mCachedAppWidgetPreviewSrcRect = new RectCache(); - private final RectCache mCachedAppWidgetPreviewDestRect = new RectCache(); - private final PaintCache mCachedAppWidgetPreviewPaint = new PaintCache(); - private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache(); - private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache(); - private final HashMap> mLoadedPreviews = new HashMap<>(); private final ArrayList> mUnusedBitmaps = new ArrayList<>(); - private final Context mContext; private final int mAppIconSize; + private final int mCellWidth; + + private final Context mContext; private final IconCache mIconCache; private final AppWidgetManagerCompat mManager; private int mPreviewBitmapWidth; private int mPreviewBitmapHeight; private String mSize; - private PagedViewCellLayout mWidgetSpacingLayout; private String mCachedSelectQuery; - - private CacheDb mDb; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); @@ -157,9 +77,10 @@ public class WidgetPreviewLoader { mContext = context; mAppIconSize = grid.iconSizePx; + mCellWidth = grid.cellWidthPx; + mIconCache = app.getIconCache(); mManager = AppWidgetManagerCompat.getInstance(context); - mDb = app.getWidgetPreviewCacheDb(); SharedPreferences sp = context.getSharedPreferences( @@ -193,12 +114,10 @@ public class WidgetPreviewLoader { mDb = app.getWidgetPreviewCacheDb(); } - public void setPreviewSize(int previewWidth, int previewHeight, - PagedViewCellLayout widgetSpacingLayout) { + public void setPreviewSize(int previewWidth, int previewHeight) { mPreviewBitmapWidth = previewWidth; mPreviewBitmapHeight = previewHeight; mSize = previewWidth + "x" + previewHeight; - mWidgetSpacingLayout = widgetSpacingLayout; } public Bitmap getPreview(final Object o) { @@ -233,12 +152,6 @@ public class WidgetPreviewLoader { unusedBitmap = candidate; } } - if (unusedBitmap != null) { - final Canvas c = mCachedAppWidgetPreviewCanvas.get(); - c.setBitmap(unusedBitmap); - c.drawColor(0, PorterDuff.Mode.CLEAR); - c.setBitmap(null); - } } if (unusedBitmap == null) { @@ -468,7 +381,7 @@ public class WidgetPreviewLoader { result.moveToFirst(); byte[] blob = result.getBlob(0); result.close(); - final BitmapFactory.Options opts = mCachedBitmapFactoryOptions.get(); + final BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inBitmap = b; opts.inSampleSize = 1; try { @@ -499,26 +412,17 @@ public class WidgetPreviewLoader { public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, Bitmap preview) { int maxWidth = maxWidthForWidgetPreview(info.spanX); - int maxHeight = maxHeightForWidgetPreview(info.spanY); - return generateWidgetPreview(info, info.spanX, info.spanY, maxWidth, - maxHeight, preview, null); + return generateWidgetPreview(info, maxWidth, preview, null); } public int maxWidthForWidgetPreview(int spanX) { - return Math.min(mPreviewBitmapWidth, - mWidgetSpacingLayout.estimateCellWidth(spanX)); - } - - public int maxHeightForWidgetPreview(int spanY) { - return Math.min(mPreviewBitmapHeight, - mWidgetSpacingLayout.estimateCellHeight(spanY)); + return Math.min(mPreviewBitmapWidth, spanX * mCellWidth); } - public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, int cellHSpan, int cellVSpan, - int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) { + public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, + int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) { // Load the preview image if possible if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE; - if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE; Drawable drawable = null; if (info.previewImage != 0) { @@ -531,61 +435,23 @@ public class WidgetPreviewLoader { } } + final boolean widgetPreviewExists = (drawable != null); + final int spanX = info.spanX < 1 ? 1 : info.spanX; + final int spanY = info.spanY < 1 ? 1 : info.spanY; + int previewWidth; int previewHeight; - Bitmap defaultPreview = null; - boolean widgetPreviewExists = (drawable != null); + Bitmap tileBitmap = null; + if (widgetPreviewExists) { previewWidth = drawable.getIntrinsicWidth(); previewHeight = drawable.getIntrinsicHeight(); } else { // Generate a preview image if we couldn't load one - if (cellHSpan < 1) cellHSpan = 1; - if (cellVSpan < 1) cellVSpan = 1; - - // This Drawable is not directly drawn, so there's no need to mutate it. - BitmapDrawable previewDrawable = (BitmapDrawable) mContext.getResources() - .getDrawable(R.drawable.widget_tile); - final int previewDrawableWidth = previewDrawable - .getIntrinsicWidth(); - final int previewDrawableHeight = previewDrawable - .getIntrinsicHeight(); - previewWidth = previewDrawableWidth * cellHSpan; - previewHeight = previewDrawableHeight * cellVSpan; - - defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888); - final Canvas c = mCachedAppWidgetPreviewCanvas.get(); - c.setBitmap(defaultPreview); - Paint p = mDefaultAppWidgetPreviewPaint.get(); - if (p == null) { - p = new Paint(); - p.setShader(new BitmapShader(previewDrawable.getBitmap(), - Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)); - mDefaultAppWidgetPreviewPaint.set(p); - } - final Rect dest = mCachedAppWidgetPreviewDestRect.get(); - dest.set(0, 0, previewWidth, previewHeight); - c.drawRect(dest, p); - c.setBitmap(null); - - // Draw the icon in the top left corner - int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE); - int smallestSide = Math.min(previewWidth, previewHeight); - float iconScale = Math.min((float) smallestSide - / (mAppIconSize + 2 * minOffset), 1f); - - try { - Drawable icon = mManager.loadIcon(info, mIconCache); - if (icon != null) { - int hoffset = (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2); - int yoffset = (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2); - icon = mutateOnMainThread(icon); - renderDrawableToBitmap(icon, defaultPreview, hoffset, - yoffset, (int) (mAppIconSize * iconScale), - (int) (mAppIconSize * iconScale)); - } - } catch (Resources.NotFoundException e) { - } + tileBitmap = ((BitmapDrawable) mContext.getResources().getDrawable( + R.drawable.widget_tile)).getBitmap(); + previewWidth = tileBitmap.getWidth() * spanX; + previewHeight = tileBitmap.getHeight() * spanY; } // Scale to fit width only - let the widget preview be clipped in the @@ -603,30 +469,58 @@ public class WidgetPreviewLoader { } // If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size + final Canvas c = new Canvas(); if (preview == null) { preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888); + c.setBitmap(preview); + } else { + // Reusing bitmap. Clear it. + c.setBitmap(preview); + c.drawColor(0, PorterDuff.Mode.CLEAR); } // Draw the scaled preview into the final bitmap int x = (preview.getWidth() - previewWidth) / 2; if (widgetPreviewExists) { - renderDrawableToBitmap(drawable, preview, x, 0, previewWidth, - previewHeight); + drawable.setBounds(x, 0, x + previewWidth, previewHeight); + drawable.draw(c); } else { - final Canvas c = mCachedAppWidgetPreviewCanvas.get(); - final Rect src = mCachedAppWidgetPreviewSrcRect.get(); - final Rect dest = mCachedAppWidgetPreviewDestRect.get(); - c.setBitmap(preview); - src.set(0, 0, defaultPreview.getWidth(), defaultPreview.getHeight()); - dest.set(x, 0, x + previewWidth, previewHeight); - - Paint p = mCachedAppWidgetPreviewPaint.get(); - if (p == null) { - p = new Paint(); - p.setFilterBitmap(true); - mCachedAppWidgetPreviewPaint.set(p); + final Paint p = new Paint(); + p.setFilterBitmap(true); + + // draw the spanX x spanY tiles + final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight()); + + float tileW = scale * tileBitmap.getWidth(); + float tileH = scale * tileBitmap.getHeight(); + final RectF dst = new RectF(0, 0, tileW, tileH); + + float tx = x; + for (int i = 0; i < spanX; i++, tx += tileW) { + float ty = 0; + for (int j = 0; j < spanY; j++, ty += tileH) { + dst.offsetTo(tx, ty); + c.drawBitmap(tileBitmap, src, dst, p); + } } - c.drawBitmap(defaultPreview, src, dest, p); + + // Draw the icon in the top left corner + // TODO: use top right for RTL + int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE); + int smallestSide = Math.min(previewWidth, previewHeight); + float iconScale = Math.min((float) smallestSide / (mAppIconSize + 2 * minOffset), scale); + + try { + Drawable icon = mutateOnMainThread(mManager.loadIcon(info, mIconCache)); + if (icon != null) { + int hoffset = (int) ((tileW - mAppIconSize * iconScale) / 2) + x; + int yoffset = (int) ((tileH - mAppIconSize * iconScale) / 2); + icon.setBounds(hoffset, yoffset, + hoffset + (int) (mAppIconSize * iconScale), + yoffset + (int) (mAppIconSize * iconScale)); + icon.draw(c); + } + } catch (Resources.NotFoundException e) { } c.setBitmap(null); } return mManager.getBadgeBitmap(info, preview); @@ -634,71 +528,47 @@ public class WidgetPreviewLoader { private Bitmap generateShortcutPreview( ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) { - Bitmap tempBitmap = mCachedShortcutPreviewBitmap.get(); - final Canvas c = mCachedShortcutPreviewCanvas.get(); - if (tempBitmap == null || - tempBitmap.getWidth() != maxWidth || - tempBitmap.getHeight() != maxHeight) { - tempBitmap = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888); - mCachedShortcutPreviewBitmap.set(tempBitmap); + final Canvas c = new Canvas(); + if (preview == null) { + preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888); + c.setBitmap(preview); + } else if (preview.getWidth() != maxWidth || preview.getHeight() != maxHeight) { + throw new RuntimeException("Improperly sized bitmap passed as argument"); } else { - c.setBitmap(tempBitmap); + // Reusing bitmap. Clear it. + c.setBitmap(preview); c.drawColor(0, PorterDuff.Mode.CLEAR); - c.setBitmap(null); } - // Render the icon - Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo)); - int paddingTop = mContext. - getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top); - int paddingLeft = mContext. - getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left); - int paddingRight = mContext. - getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right); + Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo)); + icon.setFilterBitmap(true); + // Draw a desaturated/scaled version of the icon in the background as a watermark + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(0); + icon.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + icon.setAlpha((int) (255 * 0.06f)); + + Resources res = mContext.getResources(); + int paddingTop = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top); + int paddingLeft = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left); + int paddingRight = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right); int scaledIconWidth = (maxWidth - paddingLeft - paddingRight); + icon.setBounds(paddingLeft, paddingTop, + paddingLeft + scaledIconWidth, paddingTop + scaledIconWidth); + icon.draw(c); - renderDrawableToBitmap( - icon, tempBitmap, paddingLeft, paddingTop, scaledIconWidth, scaledIconWidth); + // Draw the final icon at top left corner. + // TODO: use top right for RTL + icon.setAlpha(255); + icon.setColorFilter(null); + icon.setBounds(0, 0, mAppIconSize, mAppIconSize); + icon.draw(c); - if (preview != null && - (preview.getWidth() != maxWidth || preview.getHeight() != maxHeight)) { - throw new RuntimeException("Improperly sized bitmap passed as argument"); - } else if (preview == null) { - preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888); - } - - c.setBitmap(preview); - // Draw a desaturated/scaled version of the icon in the background as a watermark - Paint p = mCachedShortcutPreviewPaint.get(); - if (p == null) { - p = new Paint(); - ColorMatrix colorMatrix = new ColorMatrix(); - colorMatrix.setSaturation(0); - p.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); - p.setAlpha((int) (255 * 0.06f)); - mCachedShortcutPreviewPaint.set(p); - } - c.drawBitmap(tempBitmap, 0, 0, p); c.setBitmap(null); - - renderDrawableToBitmap(icon, preview, 0, 0, mAppIconSize, mAppIconSize); - return preview; } - private static void renderDrawableToBitmap( - Drawable d, Bitmap bitmap, int x, int y, int w, int h) { - if (bitmap != null) { - Canvas c = new Canvas(bitmap); - Rect oldBounds = d.copyBounds(); - d.setBounds(x, y, x + w, y + h); - d.draw(c); - d.setBounds(oldBounds); // Restore the bounds - c.setBitmap(null); - } - } - private Drawable mutateOnMainThread(final Drawable drawable) { try { return mMainThreadExecutor.submit(new Callable() { -- cgit v1.2.3 From b0df1b0e1ac865818942851b6a0d1fdca4843834 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 18 Mar 2015 22:21:40 -0700 Subject: Make sure we set the QSB bar on the SearchDropTargetBar Change-Id: If106bae15b3d628d6385711bcb8059acd17d6147 --- src/com/android/launcher3/Launcher.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index bf03f745b..d64296e79 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1441,10 +1441,7 @@ public class Launcher extends Activity dragController.addDropTarget(mWorkspace); if (mSearchDropTargetBar != null) { mSearchDropTargetBar.setup(this, dragController); - if (getOrCreateQsbBar() == null) { - // Explicitly set it to null during initialization. - mSearchDropTargetBar.setQsbSearchBar(null); - } + mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); } if (getResources().getBoolean(R.bool.debug_memory_enabled)) { @@ -4089,7 +4086,7 @@ public class Launcher extends Activity mSearchDropTargetBar.removeView(mQsb); mQsb = null; } - getOrCreateQsbBar(); + mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); } /** -- cgit v1.2.3 From 15b480f234a46b80100b42bc01572be42fa0f29b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Mar 2015 16:57:19 -0700 Subject: Removing unused logging > We store some local logs which are never propagated to the server and cause unnecessary file-io during app launch Change-Id: I2b41c3af182de2a87f38ad88f3bae9d250574c26 --- src/com/android/launcher3/LauncherFiles.java | 9 +- src/com/android/launcher3/Stats.java | 142 +-------------------------- 2 files changed, 6 insertions(+), 145 deletions(-) diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index cedb3975d..ce277c1c8 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -18,9 +18,7 @@ public class LauncherFiles { public static final String DEFAULT_WALLPAPER_THUMBNAIL_OLD = "default_thumb.jpg"; public static final String LAUNCHER_DB = "launcher.db"; public static final String LAUNCHER_PREFERENCES = "launcher.preferences"; - public static final String LAUNCHES_LOG = "launches.log"; public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; - public static final String STATS_LOG = "stats.log"; public static final String WALLPAPER_CROP_PREFERENCES_KEY = WallpaperCropActivity.class.getName(); public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db"; @@ -32,11 +30,14 @@ public class LauncherFiles { DEFAULT_WALLPAPER_THUMBNAIL_OLD, LAUNCHER_DB, LAUNCHER_PREFERENCES, - LAUNCHES_LOG, SHARED_PREFERENCES_KEY + XML, - STATS_LOG, WALLPAPER_CROP_PREFERENCES_KEY + XML, WALLPAPER_IMAGES_DB, WIDGET_PREVIEWS_DB, APP_ICONS_DB)); + + // TODO: Delete these files on upgrade + public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( + "launches.log", + "stats.log")); } diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index a87986562..9d06f755f 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -22,14 +22,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.util.Log; -import java.io.*; -import java.util.ArrayList; - public class Stats { private static final boolean DEBUG_BROADCASTS = false; - private static final String TAG = "Launcher3/Stats"; - - private static final boolean LOCAL_LAUNCH_LOG = true; public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; public static final String EXTRA_INTENT = "intent"; @@ -38,54 +32,20 @@ public class Stats { public static final String EXTRA_CELLX = "cellX"; public static final String EXTRA_CELLY = "cellY"; - private static final int LOG_VERSION = 1; - private static final int LOG_TAG_VERSION = 0x1; - private static final int LOG_TAG_LAUNCH = 0x1000; - - private static final int STATS_VERSION = 1; - private static final int INITIAL_STATS_SIZE = 100; - - // TODO: delayed/batched writes - private static final boolean FLUSH_IMMEDIATELY = true; - private final Launcher mLauncher; - private final String mLaunchBroadcastPermission; - DataOutputStream mLog; - - ArrayList mIntents; - ArrayList mHistogram; - public Stats(Launcher launcher) { mLauncher = launcher; - mLaunchBroadcastPermission = launcher.getResources().getString(R.string.receive_launch_broadcasts_permission); - loadStats(); - - if (LOCAL_LAUNCH_LOG) { - try { - mLog = new DataOutputStream(mLauncher.openFileOutput( - LauncherFiles.LAUNCHES_LOG, Context.MODE_APPEND)); - mLog.writeInt(LOG_TAG_VERSION); - mLog.writeInt(LOG_VERSION); - } catch (FileNotFoundException e) { - Log.e(TAG, "unable to create stats log: " + e); - mLog = null; - } catch (IOException e) { - Log.e(TAG, "unable to write to stats log: " + e); - mLog = null; - } - } - if (DEBUG_BROADCASTS) { launcher.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - android.util.Log.v("Stats", "got broadcast: " + intent + " for launched intent: " + Log.v("Stats", "got broadcast: " + intent + " for launched intent: " + intent.getStringExtra(EXTRA_INTENT)); } }, @@ -96,16 +56,6 @@ public class Stats { } } - public void incrementLaunch(String intentStr) { - int pos = mIntents.indexOf(intentStr); - if (pos < 0) { - mIntents.add(intentStr); - mHistogram.add(1); - } else { - mHistogram.set(pos, mHistogram.get(pos) + 1); - } - } - public void recordLaunch(Intent intent) { recordLaunch(intent, null); } @@ -115,7 +65,6 @@ public class Stats { intent.setSourceBounds(null); final String flat = intent.toUri(0); - Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat); if (shortcut != null) { broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container) @@ -124,94 +73,5 @@ public class Stats { .putExtra(EXTRA_CELLY, shortcut.cellY); } mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); - - incrementLaunch(flat); - - if (FLUSH_IMMEDIATELY) { - saveStats(); - } - - if (LOCAL_LAUNCH_LOG && mLog != null) { - try { - mLog.writeInt(LOG_TAG_LAUNCH); - mLog.writeLong(System.currentTimeMillis()); - if (shortcut == null) { - mLog.writeShort(0); - mLog.writeShort(0); - mLog.writeShort(0); - mLog.writeShort(0); - } else { - mLog.writeShort((short) shortcut.container); - mLog.writeShort((short) shortcut.screenId); - mLog.writeShort((short) shortcut.cellX); - mLog.writeShort((short) shortcut.cellY); - } - mLog.writeUTF(flat); - if (FLUSH_IMMEDIATELY) { - mLog.flush(); // TODO: delayed writes - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private void saveStats() { - DataOutputStream stats = null; - try { - stats = new DataOutputStream(mLauncher.openFileOutput( - LauncherFiles.STATS_LOG + ".tmp", Context.MODE_PRIVATE)); - stats.writeInt(STATS_VERSION); - final int N = mHistogram.size(); - stats.writeInt(N); - for (int i=0; i(INITIAL_STATS_SIZE); - mHistogram = new ArrayList(INITIAL_STATS_SIZE); - DataInputStream stats = null; - try { - stats = new DataInputStream(mLauncher.openFileInput(LauncherFiles.STATS_LOG)); - final int version = stats.readInt(); - if (version == STATS_VERSION) { - final int N = stats.readInt(); - for (int i=0; i Date: Thu, 19 Mar 2015 13:18:44 -0700 Subject: Removing unnecessary intent-filter construction Change-Id: I1f831ef48ed69381ad260f7d541f39a049c498a5 --- src/com/android/launcher3/LauncherAppState.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index d8896ccd2..8c6fc609f 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -107,18 +107,14 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class)); mModel = new LauncherModel(this, mIconCache, mAppFilter); - final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext); - launcherApps.addOnAppsChangedCallback(mModel); + + LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel); // Register intent receivers IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - sContext.registerReceiver(mModel, filter); - filter = new IntentFilter(); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); - sContext.registerReceiver(mModel, filter); - filter = new IntentFilter(); filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); sContext.registerReceiver(mModel, filter); -- cgit v1.2.3 From 2dd9f018ff32a1f34023826b64422622f6610daf Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Mon, 16 Mar 2015 19:41:43 +0000 Subject: Don't clobber widgets with the same component - DO NOT MERGE By not storing them in a HashMap keyed against ComponentName (which is almost guaranteed to conflict with other widgets when managed profiles are in play). Bug: 19444068 Change-Id: I6ffce78e8aaf265196239670404cbdaff9fa09be --- src/com/android/launcher3/Launcher.java | 2 +- .../android/launcher3/LauncherBackupHelper.java | 9 ++-- src/com/android/launcher3/LauncherModel.java | 47 +++++++++++--------- src/com/android/launcher3/Workspace.java | 3 +- src/com/android/launcher3/util/ComponentKey.java | 51 ++++++++++++++++++++++ 5 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 src/com/android/launcher3/util/ComponentKey.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a7e32d31a..a8fd79e47 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4371,7 +4371,7 @@ public class Launcher extends Activity final Workspace workspace = mWorkspace; LauncherAppWidgetProviderInfo appWidgetInfo = - LauncherModel.getProviderInfo(this, item.providerName); + LauncherModel.getProviderInfo(this, item.providerName, item.user); if (!mIsSafeModeEnabled && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 32bea5baa..31b434c42 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -650,7 +650,10 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "saving widget " + backupKey); previewLoader.setPreviewSize(spanX * profile.cellWidthPx, spanY * profile.cellHeightPx, widgetSpacingLayout); - writeRowToBackup(key, packWidget(dpi, previewLoader, mIconCache, provider), data); + UserHandleCompat user = UserHandleCompat.myUserHandle(); + writeRowToBackup(key, + packWidget(dpi, previewLoader, mIconCache, provider, user), + data); mKeys.add(key); backupWidgetCount ++; } else { @@ -913,9 +916,9 @@ public class LauncherBackupHelper implements BackupHelper { /** Serialize a widget for persistence, including a checksum wrapper. */ private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache, - ComponentName provider) { + ComponentName provider, UserHandleCompat user) { final LauncherAppWidgetProviderInfo info = - LauncherModel.getProviderInfo(mContext, provider); + LauncherModel.getProviderInfo(mContext, provider, user); Widget widget = new Widget(); widget.provider = provider.flattenToShortString(); widget.label = info.label; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index c39bcee09..b19496147 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -59,6 +59,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.ComponentKey; import java.lang.ref.WeakReference; import java.net.URISyntaxException; @@ -174,7 +175,7 @@ public class LauncherModel extends BroadcastReceiver static final ArrayList sBgWorkspaceScreens = new ArrayList(); // sBgWidgetProviders is the set of widget providers including custom internal widgets - public static HashMap sBgWidgetProviders; + public static HashMap sBgWidgetProviders; // sPendingPackages is a set of packages which could be on sdcard and are not available yet static final HashMap> sPendingPackages = @@ -1958,6 +1959,7 @@ public class LauncherModel extends BroadcastReceiver LauncherAppWidgetInfo appWidgetInfo; int container; long id; + long serialNumber; Intent intent; UserHandleCompat user; @@ -1972,7 +1974,7 @@ public class LauncherModel extends BroadcastReceiver case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: id = c.getLong(idIndex); intentDescription = c.getString(intentIndex); - long serialNumber = c.getInt(profileIdIndex); + serialNumber = c.getInt(profileIdIndex); user = mUserManager.getUserForSerialNumber(serialNumber); int promiseType = c.getInt(restoredIndex); int disabledState = 0; @@ -2210,6 +2212,7 @@ public class LauncherModel extends BroadcastReceiver LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; int appWidgetId = c.getInt(appWidgetIdIndex); + serialNumber= c.getLong(profileIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); id = c.getLong(idIndex); final ComponentName component = @@ -2224,7 +2227,8 @@ public class LauncherModel extends BroadcastReceiver final LauncherAppWidgetProviderInfo provider = LauncherModel.getProviderInfo(context, - ComponentName.unflattenFromString(savedProvider)); + ComponentName.unflattenFromString(savedProvider), + mUserManager.getUserForSerialNumber(serialNumber)); final boolean isProviderReady = isValidProvider(provider); if (!isSafeMode && !customWidget && @@ -3344,33 +3348,36 @@ public class LauncherModel extends BroadcastReceiver public static List getWidgetProviders(Context context, boolean refresh) { synchronized (sBgLock) { - if (sBgWidgetProviders != null && !refresh) { - return new ArrayList(sBgWidgetProviders.values()); - } - sBgWidgetProviders = new HashMap(); - List widgets = - AppWidgetManagerCompat.getInstance(context).getAllProviders(); - LauncherAppWidgetProviderInfo info; - for (AppWidgetProviderInfo pInfo : widgets) { - info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); - sBgWidgetProviders.put(info.provider, info); - } + if (sBgWidgetProviders == null || refresh) { + sBgWidgetProviders = new HashMap<>(); + AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); + LauncherAppWidgetProviderInfo info; + + List widgets = wm.getAllProviders(); + for (AppWidgetProviderInfo pInfo : widgets) { + info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); + UserHandleCompat user = wm.getUser(info); + sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + } - Collection customWidgets = Launcher.getCustomAppWidgets().values(); - for (CustomAppWidget widget : customWidgets) { - info = new LauncherAppWidgetProviderInfo(context, widget); - sBgWidgetProviders.put(info.provider, info); + Collection customWidgets = Launcher.getCustomAppWidgets().values(); + for (CustomAppWidget widget : customWidgets) { + info = new LauncherAppWidgetProviderInfo(context, widget); + UserHandleCompat user = wm.getUser(info); + sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + } } return new ArrayList(sBgWidgetProviders.values()); } } - public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name) { + public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name, + UserHandleCompat user) { synchronized (sBgLock) { if (sBgWidgetProviders == null) { getWidgetProviders(ctx, false /* refresh */); } - return sBgWidgetProviders.get(name); + return sBgWidgetProviders.get(new ComponentKey(name, user)); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6cfb6b29b..95215402c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4974,7 +4974,8 @@ public class Workspace extends SmoothPagedView DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo, mLauncher.getAppWidgetHost()); if (LauncherModel.getProviderInfo(getContext(), - changedInfo.get(0).providerName) != null) { + changedInfo.get(0).providerName, + changedInfo.get(0).user) != null) { // Re-inflate the widgets which have changed status widgetRefresh.run(); } else { diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java new file mode 100644 index 000000000..0f17f009e --- /dev/null +++ b/src/com/android/launcher3/util/ComponentKey.java @@ -0,0 +1,51 @@ +package com.android.launcher3.util; + +/** + * 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. + */ + +import android.content.ComponentName; + +import com.android.launcher3.compat.UserHandleCompat; + +import java.util.Arrays; + +public class ComponentKey { + + public final ComponentName componentName; + public final UserHandleCompat user; + + private final int mHashCode; + + public ComponentKey(ComponentName componentName, UserHandleCompat user) { + assert (componentName != null); + assert (user != null); + this.componentName = componentName; + this.user = user; + mHashCode = Arrays.hashCode(new Object[] {componentName, user}); + + } + + @Override + public int hashCode() { + return mHashCode; + } + + @Override + public boolean equals(Object o) { + ComponentKey other = (ComponentKey) o; + return other.componentName.equals(componentName) && other.user.equals(user); + } +} \ No newline at end of file -- cgit v1.2.3 From 26ace12135a662ad864678c9fede9817e5c02b67 Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Mon, 16 Mar 2015 19:41:43 +0000 Subject: Don't clobber widgets with the same component. By not storing them in a HashMap keyed against ComponentName (which is almost guaranteed to conflict with other widgets when managed profiles are in play). Bug: 19444068 Change-Id: I6ffce78e8aaf265196239670404cbdaff9fa09be --- src/com/android/launcher3/Launcher.java | 2 +- .../android/launcher3/LauncherBackupHelper.java | 9 +++-- src/com/android/launcher3/LauncherModel.java | 47 +++++++++++++--------- src/com/android/launcher3/Workspace.java | 3 +- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d64296e79..cc63f3a4f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3872,7 +3872,7 @@ public class Launcher extends Activity final Workspace workspace = mWorkspace; LauncherAppWidgetProviderInfo appWidgetInfo = - LauncherModel.getProviderInfo(this, item.providerName); + LauncherModel.getProviderInfo(this, item.providerName, item.user); if (!mIsSafeModeEnabled && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 7849e5335..57f92bc20 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -668,7 +668,10 @@ public class LauncherBackupHelper implements BackupHelper { previewLoader.setPreviewSize( spanX * profile.cellWidthPx, spanY * profile.cellHeightPx); - writeRowToBackup(key, packWidget(dpi, previewLoader, mIconCache, provider), data); + UserHandleCompat user = UserHandleCompat.myUserHandle(); + writeRowToBackup(key, + packWidget(dpi, previewLoader, mIconCache, provider, user), + data); mKeys.add(key); backupWidgetCount ++; } else { @@ -978,9 +981,9 @@ public class LauncherBackupHelper implements BackupHelper { /** Serialize a widget for persistence, including a checksum wrapper. */ private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache, - ComponentName provider) { + ComponentName provider, UserHandleCompat user) { final LauncherAppWidgetProviderInfo info = - LauncherModel.getProviderInfo(mContext, provider); + LauncherModel.getProviderInfo(mContext, provider, user); Widget widget = new Widget(); widget.provider = provider.flattenToShortString(); widget.label = info.label; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 1e16bafe8..135930146 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -58,6 +58,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.ComponentKey; import java.lang.ref.WeakReference; import java.net.URISyntaxException; @@ -166,7 +167,7 @@ public class LauncherModel extends BroadcastReceiver static final ArrayList sBgWorkspaceScreens = new ArrayList(); // sBgWidgetProviders is the set of widget providers including custom internal widgets - public static HashMap sBgWidgetProviders; + public static HashMap sBgWidgetProviders; // sPendingPackages is a set of packages which could be on sdcard and are not available yet static final HashMap> sPendingPackages = @@ -1872,6 +1873,7 @@ public class LauncherModel extends BroadcastReceiver LauncherAppWidgetInfo appWidgetInfo; int container; long id; + long serialNumber; Intent intent; UserHandleCompat user; @@ -1886,7 +1888,7 @@ public class LauncherModel extends BroadcastReceiver case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: id = c.getLong(idIndex); intentDescription = c.getString(intentIndex); - long serialNumber = c.getInt(profileIdIndex); + serialNumber = c.getInt(profileIdIndex); user = mUserManager.getUserForSerialNumber(serialNumber); int promiseType = c.getInt(restoredIndex); int disabledState = 0; @@ -2150,6 +2152,7 @@ public class LauncherModel extends BroadcastReceiver LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; int appWidgetId = c.getInt(appWidgetIdIndex); + serialNumber= c.getLong(profileIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); id = c.getLong(idIndex); final ComponentName component = @@ -2164,7 +2167,8 @@ public class LauncherModel extends BroadcastReceiver final LauncherAppWidgetProviderInfo provider = LauncherModel.getProviderInfo(context, - ComponentName.unflattenFromString(savedProvider)); + ComponentName.unflattenFromString(savedProvider), + mUserManager.getUserForSerialNumber(serialNumber)); final boolean isProviderReady = isValidProvider(provider); if (!isSafeMode && !customWidget && @@ -3283,33 +3287,36 @@ public class LauncherModel extends BroadcastReceiver public static List getWidgetProviders(Context context, boolean refresh) { synchronized (sBgLock) { - if (sBgWidgetProviders != null && !refresh) { - return new ArrayList(sBgWidgetProviders.values()); - } - sBgWidgetProviders = new HashMap(); - List widgets = - AppWidgetManagerCompat.getInstance(context).getAllProviders(); - LauncherAppWidgetProviderInfo info; - for (AppWidgetProviderInfo pInfo : widgets) { - info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); - sBgWidgetProviders.put(info.provider, info); - } + if (sBgWidgetProviders == null || refresh) { + sBgWidgetProviders = new HashMap<>(); + AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); + LauncherAppWidgetProviderInfo info; + + List widgets = wm.getAllProviders(); + for (AppWidgetProviderInfo pInfo : widgets) { + info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); + UserHandleCompat user = wm.getUser(info); + sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + } - Collection customWidgets = Launcher.getCustomAppWidgets().values(); - for (CustomAppWidget widget : customWidgets) { - info = new LauncherAppWidgetProviderInfo(context, widget); - sBgWidgetProviders.put(info.provider, info); + Collection customWidgets = Launcher.getCustomAppWidgets().values(); + for (CustomAppWidget widget : customWidgets) { + info = new LauncherAppWidgetProviderInfo(context, widget); + UserHandleCompat user = wm.getUser(info); + sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + } } return new ArrayList(sBgWidgetProviders.values()); } } - public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name) { + public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name, + UserHandleCompat user) { synchronized (sBgLock) { if (sBgWidgetProviders == null) { getWidgetProviders(ctx, false /* refresh */); } - return sBgWidgetProviders.get(name); + return sBgWidgetProviders.get(new ComponentKey(name, user)); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 7ebdf3ace..7df801da5 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4878,7 +4878,8 @@ public class Workspace extends SmoothPagedView DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo, mLauncher.getAppWidgetHost()); if (LauncherModel.getProviderInfo(getContext(), - changedInfo.get(0).providerName) != null) { + changedInfo.get(0).providerName, + changedInfo.get(0).user) != null) { // Re-inflate the widgets which have changed status widgetRefresh.run(); } else { -- cgit v1.2.3 From 091440a9cb9d4f42406631004aa484cbb79214ca Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 18 Mar 2015 14:16:05 -0700 Subject: Reducing method count by eliminating synthetic accessors Elimates 304 methods based on dex analysis The java compiler generates sythetic accessor methods for all private fields, methods and contructors accessed from inner classes. By marking them package-private and @Thunk instead, sythentic accessor methods are no longer needeed. These annotated elements should be treated as private. Change-Id: Id0dc2c92733474250d8ff12fa793d3a8adeb1f26 --- .../gallery3d/glrenderer/UploadedTexture.java | 4 +- .../launcher3/LiveWallpaperListAdapter.java | 8 +-- .../ThirdPartyWallpaperPickerListAdapter.java | 4 +- .../android/launcher3/WallpaperCropActivity.java | 7 +-- .../android/launcher3/WallpaperPickerActivity.java | 35 +++++++------ .../android/photos/views/TiledImageRenderer.java | 21 ++++---- .../com/android/photos/views/TiledImageView.java | 7 +-- src/com/android/launcher3/AppsContainerView.java | 8 +-- .../android/launcher3/AppsCustomizePagedView.java | 15 +++--- src/com/android/launcher3/AppsGridAdapter.java | 16 +++--- src/com/android/launcher3/AutoInstallsLayout.java | 9 ++-- src/com/android/launcher3/CellLayout.java | 31 +++++------ .../android/launcher3/CheckLongPressHelper.java | 6 ++- src/com/android/launcher3/CommonAppTypeParser.java | 4 +- src/com/android/launcher3/DefaultLayoutParser.java | 9 ++-- src/com/android/launcher3/DeferredHandler.java | 6 ++- src/com/android/launcher3/DeleteDropTarget.java | 5 +- src/com/android/launcher3/DeviceProfile.java | 4 +- src/com/android/launcher3/DragController.java | 16 +++--- src/com/android/launcher3/DragLayer.java | 11 ++-- src/com/android/launcher3/DragView.java | 10 ++-- .../launcher3/FirstFrameAnimatorHelper.java | 4 +- src/com/android/launcher3/FocusHelper.java | 5 +- src/com/android/launcher3/FocusIndicatorView.java | 4 +- src/com/android/launcher3/Folder.java | 35 +++++++------ src/com/android/launcher3/FolderIcon.java | 19 +++---- src/com/android/launcher3/FolderPagedView.java | 4 +- src/com/android/launcher3/IconCache.java | 3 +- .../android/launcher3/InstallShortcutReceiver.java | 3 +- .../launcher3/InterruptibleInOutAnimator.java | 4 +- src/com/android/launcher3/Launcher.java | 61 +++++++++++----------- .../launcher3/LauncherAccessibilityDelegate.java | 7 +-- src/com/android/launcher3/LauncherAppState.java | 3 +- src/com/android/launcher3/LauncherClings.java | 8 +-- src/com/android/launcher3/LauncherModel.java | 53 ++++++++++--------- src/com/android/launcher3/LauncherProvider.java | 17 +++--- .../LauncherStateTransitionAnimation.java | 8 +-- src/com/android/launcher3/PagedView.java | 20 +++---- src/com/android/launcher3/WeightWatcher.java | 6 ++- src/com/android/launcher3/WidgetPreviewLoader.java | 7 +-- src/com/android/launcher3/Workspace.java | 57 ++++++++++---------- .../launcher3/compat/LauncherAppsCompatV16.java | 6 ++- .../launcher3/compat/PackageInstallerCompatVL.java | 11 ++-- src/com/android/launcher3/util/Thunk.java | 43 +++++++++++++++ 44 files changed, 364 insertions(+), 260 deletions(-) create mode 100644 src/com/android/launcher3/util/Thunk.java diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java index f41a979b7..67abf6564 100644 --- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java +++ b/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java @@ -20,6 +20,8 @@ import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.opengl.GLUtils; +import com.android.launcher3.util.Thunk; + import junit.framework.Assert; import java.util.HashMap; @@ -82,7 +84,7 @@ public abstract class UploadedTexture extends BasicTexture { return mIsUploading; } - private static class BorderKey implements Cloneable { + @Thunk static class BorderKey implements Cloneable { public boolean vertical; public Config config; public int length; diff --git a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java index 72f2d7e49..a5e951ce4 100644 --- a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java @@ -35,6 +35,8 @@ import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.TextView; +import com.android.launcher3.util.Thunk; + import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; @@ -50,7 +52,7 @@ public class LiveWallpaperListAdapter extends BaseAdapter implements ListAdapter private final LayoutInflater mInflater; private final PackageManager mPackageManager; - private List mWallpapers; + @Thunk List mWallpapers; @SuppressWarnings("unchecked") public LiveWallpaperListAdapter(Context context) { @@ -109,8 +111,8 @@ public class LiveWallpaperListAdapter extends BaseAdapter implements ListAdapter } public static class LiveWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo { - private Drawable mThumbnail; - private WallpaperInfo mInfo; + @Thunk Drawable mThumbnail; + @Thunk WallpaperInfo mInfo; public LiveWallpaperTile(Drawable thumbnail, WallpaperInfo info, Intent intent) { mThumbnail = thumbnail; mInfo = info; diff --git a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java index 27e65aa31..16bde3300 100644 --- a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java @@ -32,6 +32,8 @@ import android.widget.FrameLayout; import android.widget.ListAdapter; import android.widget.TextView; +import com.android.launcher3.util.Thunk; + import java.util.ArrayList; import java.util.List; @@ -46,7 +48,7 @@ public class ThirdPartyWallpaperPickerListAdapter extends BaseAdapter implements new ArrayList(); public static class ThirdPartyWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo { - private ResolveInfo mResolveInfo; + @Thunk ResolveInfo mResolveInfo; public ThirdPartyWallpaperTile(ResolveInfo resolveInfo) { mResolveInfo = resolveInfo; } diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index a3a3c537b..5b2943a65 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -44,6 +44,7 @@ import android.widget.Toast; import com.android.gallery3d.common.BitmapCropTask; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; +import com.android.launcher3.util.Thunk; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider; @@ -78,10 +79,10 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback private HandlerThread mLoaderThread; private Handler mLoaderHandler; - private LoadRequest mCurrentLoadRequest; + @Thunk LoadRequest mCurrentLoadRequest; private byte[] mTempStorageForDecoding = new byte[16 * 1024]; // A weak-set of reusable bitmaps - private Set mReusableBitmaps = + @Thunk Set mReusableBitmaps = Collections.newSetFromMap(new WeakHashMap()); @Override @@ -220,7 +221,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback return false; } - private void addReusableBitmap(TileSource src) { + @Thunk void addReusableBitmap(TileSource src) { synchronized (mReusableBitmaps) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && src instanceof BitmapRegionTileSource) { diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index d16fc31e5..1364df3ed 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -70,6 +70,7 @@ import android.widget.Toast; import com.android.gallery3d.common.BitmapCropTask; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; +import com.android.launcher3.util.Thunk; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; import com.android.photos.views.TiledImageRenderer.TileSource; @@ -88,21 +89,21 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { private static final String SELECTED_INDEX = "SELECTED_INDEX"; private static final int FLAG_POST_DELAY_MILLIS = 200; - private View mSelectedTile; - private boolean mIgnoreNextTap; - private OnClickListener mThumbnailOnClickListener; + @Thunk View mSelectedTile; + @Thunk boolean mIgnoreNextTap; + @Thunk OnClickListener mThumbnailOnClickListener; - private LinearLayout mWallpapersView; - private HorizontalScrollView mWallpaperScrollContainer; + @Thunk LinearLayout mWallpapersView; + @Thunk HorizontalScrollView mWallpaperScrollContainer; - private ActionMode.Callback mActionModeCallback; - private ActionMode mActionMode; + @Thunk ActionMode.Callback mActionModeCallback; + @Thunk ActionMode mActionMode; - private View.OnLongClickListener mLongClickListener; + @Thunk View.OnLongClickListener mLongClickListener; ArrayList mTempWallpaperTiles = new ArrayList(); private SavedWallpaperImages mSavedImages; - private int mSelectedIndex = -1; + @Thunk int mSelectedIndex = -1; public static abstract class WallpaperTileInfo { protected View mView; @@ -135,7 +136,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static class UriWallpaperInfo extends WallpaperTileInfo { private Uri mUri; private boolean mFirstClick = true; - private BitmapRegionTileSource.UriBitmapSource mBitmapSource; + @Thunk BitmapRegionTileSource.UriBitmapSource mBitmapSource; public UriWallpaperInfo(Uri uri) { mUri = uri; } @@ -337,7 +338,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { }, FLAG_POST_DELAY_MILLIS); } - private void changeWallpaperFlags(boolean visible) { + @Thunk void changeWallpaperFlags(boolean visible) { int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0; int currentWallpaperFlag = getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -635,7 +636,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { }; } - private void selectTile(View v) { + @Thunk void selectTile(View v) { if (mSelectedTile != null) { mSelectedTile.setSelected(false); mSelectedTile = null; @@ -649,7 +650,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { getString(R.string.announce_selection, v.getContentDescription())); } - private void initializeScrollForRtl() { + @Thunk void initializeScrollForRtl() { if (mWallpaperScrollContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @@ -704,7 +705,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1); } - private void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, + @Thunk void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, boolean addLongPressHandler) { for (int i = 0; i < adapter.getCount(); i++) { FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent); @@ -719,7 +720,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - private void updateTileIndices() { + @Thunk void updateTileIndices() { LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); final int childCount = masterWallpaperList.getChildCount(); final Resources res = getResources(); @@ -760,13 +761,13 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - private static Point getDefaultThumbnailSize(Resources res) { + @Thunk static Point getDefaultThumbnailSize(Resources res) { return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth), res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight)); } - private static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, + @Thunk static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, Resources res, int resId, int rotation, boolean leftAligned) { int width = size.x; int height = size.y; diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java index f9b7ab473..39a73b9d5 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java @@ -30,6 +30,7 @@ import com.android.gallery3d.common.Utils; import com.android.gallery3d.glrenderer.BasicTexture; import com.android.gallery3d.glrenderer.GLCanvas; import com.android.gallery3d.glrenderer.UploadedTexture; +import com.android.launcher3.util.Thunk; import com.android.photos.views.Pools.Pool; import com.android.photos.views.Pools.SynchronizedPool; @@ -67,12 +68,12 @@ public class TiledImageRenderer { private static final int STATE_RECYCLING = 0x20; private static final int STATE_RECYCLED = 0x40; - private static Pool sTilePool = new SynchronizedPool(64); + @Thunk static Pool sTilePool = new SynchronizedPool(64); // TILE_SIZE must be 2^N - private int mTileSize; + @Thunk int mTileSize; - private TileSource mModel; + @Thunk TileSource mModel; private BasicTexture mPreview; protected int mLevelCount; // cache the value of mScaledBitmaps.length @@ -82,7 +83,7 @@ public class TiledImageRenderer { // half size of the previous one). If the value is in [0, mLevelCount), we // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value // is mLevelCount - private int mLevel = 0; + @Thunk int mLevel = 0; private int mOffsetX; private int mOffsetY; @@ -96,10 +97,10 @@ public class TiledImageRenderer { private final LongSparseArray mActiveTiles = new LongSparseArray(); // The following three queue are guarded by mQueueLock - private final Object mQueueLock = new Object(); + @Thunk final Object mQueueLock = new Object(); private final TileQueue mRecycledQueue = new TileQueue(); private final TileQueue mUploadQueue = new TileQueue(); - private final TileQueue mDecodeQueue = new TileQueue(); + @Thunk final TileQueue mDecodeQueue = new TileQueue(); // The width and height of the full-sized bitmap protected int mImageWidth = SIZE_UNKNOWN; @@ -489,7 +490,7 @@ public class TiledImageRenderer { } } - private void decodeTile(Tile tile) { + @Thunk void decodeTile(Tile tile) { synchronized (mQueueLock) { if (tile.mTileState != STATE_IN_QUEUE) { return; @@ -556,7 +557,7 @@ public class TiledImageRenderer { mActiveTiles.put(key, tile); } - private Tile getTile(int x, int y, int level) { + @Thunk Tile getTile(int x, int y, int level) { return mActiveTiles.get(makeTileKey(x, y, level)); } @@ -748,7 +749,7 @@ public class TiledImageRenderer { } } - private static class TileQueue { + @Thunk static class TileQueue { private Tile mHead; public Tile pop() { @@ -786,7 +787,7 @@ public class TiledImageRenderer { } } - private class TileDecoder extends Thread { + @Thunk class TileDecoder extends Thread { public void finishAndWait() { interrupt(); diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java index 56ee7a658..7e3e1a936 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java @@ -33,6 +33,7 @@ import android.widget.FrameLayout; import com.android.gallery3d.glrenderer.BasicTexture; import com.android.gallery3d.glrenderer.GLES20Canvas; +import com.android.launcher3.util.Thunk; import com.android.photos.views.TiledImageRenderer.TileSource; import javax.microedition.khronos.egl.EGLConfig; @@ -43,8 +44,8 @@ import javax.microedition.khronos.opengles.GL10; */ public class TiledImageView extends FrameLayout { - private GLSurfaceView mGLSurfaceView; - private boolean mInvalPending = false; + @Thunk GLSurfaceView mGLSurfaceView; + @Thunk boolean mInvalPending = false; private FrameCallback mFrameCallback; protected static class ImageRendererWrapper { @@ -203,7 +204,7 @@ public class TiledImageView extends FrameLayout { } } - private class TileRenderer implements Renderer { + @Thunk class TileRenderer implements Renderer { private GLES20Canvas mCanvas; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index ce092bfe4..ea12fa361 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -33,6 +33,8 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.TextView; +import com.android.launcher3.util.Thunk; + import java.util.List; @@ -49,12 +51,12 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private static final int LIST_LAYOUT = 1; private static final int USE_LAYOUT = GRID_LAYOUT; - private Launcher mLauncher; - private AlphabeticalAppsList mApps; + @Thunk Launcher mLauncher; + @Thunk AlphabeticalAppsList mApps; private RecyclerView.Adapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.ItemDecoration mItemDecoration; - private AppsContainerRecyclerView mAppsListView; + @Thunk AppsContainerRecyclerView mAppsListView; private EditText mSearchBar; private int mNumAppsPerRow; private Point mLastTouchDownPos = new Point(); diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 3f2aa7036..1955547c8 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -45,6 +45,7 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.FocusHelper.PagedViewKeyListener; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.Iterator; @@ -151,7 +152,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private ContentType mContentType = ContentType.Widgets; // Refs - private Launcher mLauncher; + @Thunk Launcher mLauncher; private DragController mDragController; private final LayoutInflater mLayoutInflater; private final PackageManager mPackageManager; @@ -167,7 +168,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Dimens private int mContentWidth, mContentHeight; - private int mWidgetCountX, mWidgetCountY; + @Thunk int mWidgetCountX, mWidgetCountY; private int mNumWidgetPages; // Previews & outlines @@ -191,10 +192,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private Toast mWidgetInstructionToast; // Deferral of loading widget previews during launcher transitions - private boolean mInTransition; + @Thunk boolean mInTransition; private ArrayList mDeferredSyncWidgetPageItems = new ArrayList(); - private ArrayList mDeferredPrepareLoadWidgetPreviewsTasks = + @Thunk ArrayList mDeferredPrepareLoadWidgetPreviewsTasks = new ArrayList(); WidgetPreviewLoader mWidgetPreviewLoader; @@ -907,7 +908,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen /** * Creates and executes a new AsyncTask to load a page of widget previews. */ - private void prepareLoadWidgetPreviewsTask(int page, ArrayList widgets, + @Thunk void prepareLoadWidgetPreviewsTask(int page, ArrayList widgets, int cellWidth, int cellHeight, int cellCountX) { // Prune all tasks that are no longer needed @@ -1083,7 +1084,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } }); } - private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, + @Thunk void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { // loadWidgetPreviewsInBackground can be called without a task to load a set of widget // previews synchronously @@ -1109,7 +1110,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - private void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) { + @Thunk void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) { if (!immediatelySyncItems && mInTransition) { mDeferredSyncWidgetPageItems.add(data); return; diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 6a4495e0e..5895cbf08 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -11,7 +11,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.util.Thunk; /** @@ -108,21 +110,21 @@ class AppsGridAdapter extends RecyclerView.Adapter { } private LayoutInflater mLayoutInflater; - private AlphabeticalAppsList mApps; + @Thunk AlphabeticalAppsList mApps; private GridSpanSizer mGridSizer; private GridItemDecoration mItemDecoration; private View.OnTouchListener mTouchListener; private View.OnClickListener mIconClickListener; private View.OnLongClickListener mIconLongClickListener; - private int mAppsPerRow; - private boolean mIsRtl; + @Thunk int mAppsPerRow; + @Thunk boolean mIsRtl; private String mEmptySearchText; // Section drawing - private int mPaddingStart; - private int mStartMargin; - private Paint mSectionTextPaint; - private Rect mTmpBounds = new Rect(); + @Thunk int mPaddingStart; + @Thunk int mStartMargin; + @Thunk Paint mSectionTextPaint; + @Thunk Rect mTmpBounds = new Rect(); public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 908bd3d79..cbab08bec 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -37,6 +37,7 @@ import android.util.Patterns; import com.android.launcher3.LauncherProvider.SqlArguments; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -114,8 +115,8 @@ public class AutoInstallsLayout { private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE = "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE"; - private final Context mContext; - private final AppWidgetHost mAppWidgetHost; + @Thunk final Context mContext; + @Thunk final AppWidgetHost mAppWidgetHost; protected final LayoutParserCallback mCallback; protected final PackageManager mPackageManager; @@ -125,7 +126,7 @@ public class AutoInstallsLayout { private final int mHotseatAllAppsRank; private final long[] mTemp = new long[2]; - private final ContentValues mValues; + @Thunk final ContentValues mValues; protected final String mRootTag; protected SQLiteDatabase mDb; @@ -648,7 +649,7 @@ public class AutoInstallsLayout { long insertAndCheck(SQLiteDatabase db, ContentValues values); } - private static void copyInteger(ContentValues from, ContentValues to, String key) { + @Thunk static void copyInteger(ContentValues from, ContentValues to, String key) { to.put(key, from.getAsInteger(key)); } } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index c57090d7c..eb2aa547d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -55,6 +55,7 @@ import android.view.animation.LayoutAnimationController; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.LauncherAccessibilityDelegate.DragType; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.Arrays; @@ -68,18 +69,18 @@ public class CellLayout extends ViewGroup { static final String TAG = "CellLayout"; private Launcher mLauncher; - private int mCellWidth; - private int mCellHeight; + @Thunk int mCellWidth; + @Thunk int mCellHeight; private int mFixedCellWidth; private int mFixedCellHeight; - private int mCountX; - private int mCountY; + @Thunk int mCountX; + @Thunk int mCountY; private int mOriginalWidthGap; private int mOriginalHeightGap; - private int mWidthGap; - private int mHeightGap; + @Thunk int mWidthGap; + @Thunk int mHeightGap; private int mMaxGap; private boolean mDropPending = false; private boolean mIsDragTarget = true; @@ -87,7 +88,7 @@ public class CellLayout extends ViewGroup { // These are temporary variables to prevent having to allocate a new object just to // return an (x, y) value from helper functions. Do NOT use them to maintain other state. private final int[] mTmpXY = new int[2]; - private final int[] mTmpPoint = new int[2]; + @Thunk final int[] mTmpPoint = new int[2]; int[] mTempLocation = new int[2]; boolean[][] mOccupied; @@ -124,8 +125,8 @@ public class CellLayout extends ViewGroup { // These arrays are used to implement the drag visualization on x-large screens. // They are used as circular arrays, indexed by mDragOutlineCurrent. - private Rect[] mDragOutlines = new Rect[4]; - private float[] mDragOutlineAlphas = new float[mDragOutlines.length]; + @Thunk Rect[] mDragOutlines = new Rect[4]; + @Thunk float[] mDragOutlineAlphas = new float[mDragOutlines.length]; private InterruptibleInOutAnimator[] mDragOutlineAnims = new InterruptibleInOutAnimator[mDragOutlines.length]; @@ -135,7 +136,7 @@ public class CellLayout extends ViewGroup { private final FastBitmapView mTouchFeedbackView; - private HashMap mReorderAnimators = new + @Thunk HashMap mReorderAnimators = new HashMap(); private HashMap mShakeAnimators = new HashMap(); @@ -166,7 +167,7 @@ public class CellLayout extends ViewGroup { private static final float REORDER_PREVIEW_MAGNITUDE = 0.12f; private static final int REORDER_ANIMATION_DURATION = 150; - private float mReorderPreviewAnimationMagnitude; + @Thunk float mReorderPreviewAnimationMagnitude; private ArrayList mIntersectingViews = new ArrayList(); private Rect mOccupiedRect = new Rect(); @@ -184,8 +185,8 @@ public class CellLayout extends ViewGroup { private boolean mUseTouchHelper = false; OnClickListener mOldClickListener = null; OnClickListener mOldWorkspaceListener = null; - private int mDownX = 0; - private int mDownY = 0; + @Thunk int mDownX = 0; + @Thunk int mDownY = 0; public CellLayout(Context context) { this(context, null); @@ -2550,7 +2551,7 @@ public class CellLayout extends ViewGroup { } } - private void completeAnimationImmediately() { + @Thunk void completeAnimationImmediately() { if (a != null) { a.cancel(); } @@ -2871,7 +2872,7 @@ public class CellLayout extends ViewGroup { return mItemPlacementDirty; } - private class ItemConfiguration { + @Thunk class ItemConfiguration { HashMap map = new HashMap(); private HashMap savedMap = new HashMap(); ArrayList sortedViews = new ArrayList(); diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java index 81149793d..10ca6a371 100644 --- a/src/com/android/launcher3/CheckLongPressHelper.java +++ b/src/com/android/launcher3/CheckLongPressHelper.java @@ -18,9 +18,11 @@ package com.android.launcher3; import android.view.View; +import com.android.launcher3.util.Thunk; + public class CheckLongPressHelper { - private View mView; - private boolean mHasPerformedLongPress; + @Thunk View mView; + @Thunk boolean mHasPerformedLongPress; private CheckForLongPress mPendingCheckForLongPress; class CheckForLongPress implements Runnable { diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java index fe2fbd75f..31641799d 100644 --- a/src/com/android/launcher3/CommonAppTypeParser.java +++ b/src/com/android/launcher3/CommonAppTypeParser.java @@ -91,8 +91,8 @@ public class CommonAppTypeParser implements LayoutParserCallback { private class MyLayoutParser extends DefaultLayoutParser { public MyLayoutParser() { - super(mContext, null, CommonAppTypeParser.this, - mContext.getResources(), mResId, TAG_RESOLVE, 0); + super(CommonAppTypeParser.this.mContext, null, CommonAppTypeParser.this, + CommonAppTypeParser.this.mContext.getResources(), mResId, TAG_RESOLVE, 0); } @Override diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index 6c3008b6e..7b91c675b 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -13,6 +13,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -57,7 +58,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { return getFolderElementsMap(mSourceRes); } - private HashMap getFolderElementsMap(Resources res) { + @Thunk HashMap getFolderElementsMap(Resources res) { HashMap parsers = new HashMap(); parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser()); parsers.put(TAG_SHORTCUT, new UriShortcutParser(res)); @@ -89,7 +90,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * AppShortcutParser which also supports adding URI based intents */ - private class AppShortcutWithUriParser extends AppShortcutParser { + @Thunk class AppShortcutWithUriParser extends AppShortcutParser { @Override protected long invalidPackageOrClass(XmlResourceParser parser) { @@ -231,7 +232,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * A parser which adds a folder whose contents come from partner apk. */ - private class PartnerFolderParser implements TagParser { + @Thunk class PartnerFolderParser implements TagParser { @Override public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException, @@ -257,7 +258,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * An extension of FolderParser which allows adding items from a different xml. */ - private class MyFolderParser extends FolderParser { + @Thunk class MyFolderParser extends FolderParser { @Override public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException, diff --git a/src/com/android/launcher3/DeferredHandler.java b/src/com/android/launcher3/DeferredHandler.java index a2d121d63..eb7c26a28 100644 --- a/src/com/android/launcher3/DeferredHandler.java +++ b/src/com/android/launcher3/DeferredHandler.java @@ -22,6 +22,8 @@ import android.os.Message; import android.os.MessageQueue; import android.util.Pair; +import com.android.launcher3.util.Thunk; + import java.util.LinkedList; import java.util.ListIterator; @@ -33,11 +35,11 @@ import java.util.ListIterator; * This class is fifo. */ public class DeferredHandler { - private LinkedList> mQueue = new LinkedList>(); + @Thunk LinkedList> mQueue = new LinkedList>(); private MessageQueue mMessageQueue = Looper.myQueue(); private Impl mHandler = new Impl(); - private class Impl extends Handler implements MessageQueue.IdleHandler { + @Thunk class Impl extends Handler implements MessageQueue.IdleHandler { public void handleMessage(Message msg) { Pair p; Runnable r; diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 1ada1a912..1f0dad221 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -41,6 +41,7 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.Thunk; public class DeleteDropTarget extends ButtonDropTarget { private static int DELETE_ANIMATION_DURATION = 285; @@ -56,7 +57,7 @@ public class DeleteDropTarget extends ButtonDropTarget { private TransitionDrawable mRemoveDrawable; private TransitionDrawable mCurrentDrawable; - private boolean mWaitingForUninstall = false; + @Thunk boolean mWaitingForUninstall = false; public DeleteDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -272,7 +273,7 @@ public class DeleteDropTarget extends ButtonDropTarget { return false; } - private void completeDrop(DragObject d) { + @Thunk void completeDrop(DragObject d) { ItemInfo item = (ItemInfo) d.dragInfo; boolean wasWaitingForUninstall = mWaitingForUninstall; mWaitingForUninstall = false; diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index bc9ef763d..b4d225e8b 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -38,6 +38,8 @@ import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; +import com.android.launcher3.util.Thunk; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -449,7 +451,7 @@ public class DeviceProfile { updateAvailableDimensions(context); } - private float dist(PointF p0, PointF p1) { + @Thunk float dist(PointF p0, PointF p1) { return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) + (p1.y-p0.y)*(p1.y-p0.y)); } diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 8dc6e185c..eb1686182 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -34,6 +34,8 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.inputmethod.InputMethodManager; +import com.android.launcher3.util.Thunk; + import java.util.ArrayList; import java.util.HashSet; @@ -63,7 +65,7 @@ public class DragController { private static final float MAX_FLING_DEGREES = 35f; - private Launcher mLauncher; + @Thunk Launcher mLauncher; private Handler mHandler; // temporaries to avoid gc thrash @@ -102,17 +104,17 @@ public class DragController { private View mMoveTarget; - private DragScroller mDragScroller; - private int mScrollState = SCROLL_OUTSIDE_ZONE; + @Thunk DragScroller mDragScroller; + @Thunk int mScrollState = SCROLL_OUTSIDE_ZONE; private ScrollRunnable mScrollRunnable = new ScrollRunnable(); private DropTarget mLastDropTarget; private InputMethodManager mInputMethodManager; - private int mLastTouch[] = new int[2]; - private long mLastTouchUpTime = -1; - private int mDistanceSinceScroll = 0; + @Thunk int mLastTouch[] = new int[2]; + @Thunk long mLastTouchUpTime = -1; + @Thunk int mDistanceSinceScroll = 0; private int mTmpPoint[] = new int[2]; private Rect mDragLayerRect = new Rect(); @@ -543,7 +545,7 @@ public class DragController { mLastDropTarget = dropTarget; } - private void checkScrollState(int x, int y) { + @Thunk void checkScrollState(int x, int y) { final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop(); final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY; final DragLayer dragLayer = mLauncher.getDragLayer(); diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index a352b7914..ab2e094cb 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -39,6 +39,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.InsettableFrameLayout.LayoutParams; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -46,7 +47,7 @@ import java.util.ArrayList; * A ViewGroup that coordinates dragging across its descendants */ public class DragLayer extends InsettableFrameLayout { - private DragController mDragController; + @Thunk DragController mDragController; private int[] mTmpXY = new int[2]; private int mXDown, mYDown; @@ -61,9 +62,9 @@ public class DragLayer extends InsettableFrameLayout { private ValueAnimator mDropAnim = null; private ValueAnimator mFadeOutAnim = null; private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); - private DragView mDropView = null; - private int mAnchorViewInitialScrollX = 0; - private View mAnchorView = null; + @Thunk DragView mDropView = null; + @Thunk int mAnchorViewInitialScrollX = 0; + @Thunk View mAnchorView = null; private boolean mHoverPointClosesFolder = false; private Rect mHitRect = new Rect(); @@ -779,7 +780,7 @@ public class DragLayer extends InsettableFrameLayout { return mDropView; } - private void fadeOutDragView() { + @Thunk void fadeOutDragView() { mFadeOutAnim = new ValueAnimator(); mFadeOutAnim.setDuration(150); mFadeOutAnim.setFloatValues(0f, 1f); diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index 78d72b30f..b1a6266cc 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -29,8 +29,10 @@ import android.graphics.Rect; import android.view.View; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.util.Thunk; + public class DragView extends View { - private static float sDragAlpha = 1f; + @Thunk static float sDragAlpha = 1f; private Bitmap mBitmap; private Bitmap mCrossFadeBitmap; @@ -42,11 +44,11 @@ public class DragView extends View { private Rect mDragRegion = null; private DragLayer mDragLayer = null; private boolean mHasDrawn = false; - private float mCrossFadeProgress = 0f; + @Thunk float mCrossFadeProgress = 0f; ValueAnimator mAnim; - private float mOffsetX = 0.0f; - private float mOffsetY = 0.0f; + @Thunk float mOffsetX = 0.0f; + @Thunk float mOffsetY = 0.0f; private float mInitialScale = 1f; // The intrinsic icon scale factor is the scale factor for a drag icon over the workspace // size. This is ignored for non-icons. diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java index 095c5631d..a51ddd4b8 100644 --- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java +++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java @@ -24,6 +24,8 @@ import android.view.View; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; +import com.android.launcher3.util.Thunk; + /* * This is a helper class that listens to updates from the corresponding animation. * For the first two frames, it adjusts the current play time of the animation to @@ -41,7 +43,7 @@ public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter private boolean mAdjustedSecondFrameTime; private static ViewTreeObserver.OnDrawListener sGlobalDrawListener; - private static long sGlobalFrameCounter; + @Thunk static long sGlobalFrameCounter; private static boolean sVisible; public FirstFrameAnimatorHelper(ValueAnimator animator, View target) { diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index fc6895201..327fac460 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -25,6 +25,7 @@ import android.view.ViewGroup; import com.android.launcher3.FocusHelper.PagedViewKeyListener; import com.android.launcher3.util.FocusLogic; +import com.android.launcher3.util.Thunk; /** * A keyboard listener we set on all the workspace icons. @@ -480,7 +481,7 @@ public class FocusHelper { /** * Returns the Viewgroup containing page contents for the page at the index specified. */ - private static ViewGroup getAppsCustomizePage(ViewGroup container, int index) { + @Thunk static ViewGroup getAppsCustomizePage(ViewGroup container, int index) { ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index); if (page instanceof CellLayout) { // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren @@ -510,7 +511,7 @@ public class FocusHelper { /** * Helper method to be used for playing sound effects. */ - private static void playSoundEffect(int keyCode, View v) { + @Thunk static void playSoundEffect(int keyCode, View v) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java index af3b97634..ab21c90e6 100644 --- a/src/com/android/launcher3/FocusIndicatorView.java +++ b/src/com/android/launcher3/FocusIndicatorView.java @@ -24,6 +24,8 @@ import android.util.AttributeSet; import android.util.Pair; import android.view.View; +import com.android.launcher3.util.Thunk; + public class FocusIndicatorView extends View implements View.OnFocusChangeListener { // It can be any number >0. The view is resized using scaleX and scaleY. @@ -176,7 +178,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen } } - private static final class ViewAnimState { + @Thunk static final class ViewAnimState { float x, y, scaleX, scaleY; } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 5d8a865f7..23582cec8 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -51,6 +51,7 @@ import android.widget.TextView; import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.Collections; @@ -106,7 +107,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private final Alarm mReorderAlarm = new Alarm(); private final Alarm mOnExitAlarm = new Alarm(); - private final ArrayList mItemsInReadingOrder = new ArrayList(); + @Thunk final ArrayList mItemsInReadingOrder = new ArrayList(); private final int mExpandDuration; private final int mMaterialExpandDuration; @@ -118,19 +119,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList protected DragController mDragController; protected FolderInfo mInfo; - private FolderIcon mFolderIcon; + @Thunk FolderIcon mFolderIcon; - private FolderContent mContent; - private View mContentWrapper; + @Thunk FolderContent mContent; + @Thunk View mContentWrapper; FolderEditText mFolderName; private View mFooter; private int mFooterHeight; // Cell ranks used for drag and drop - private int mTargetRank, mPrevTargetRank, mEmptyCellRank; + @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank; - private int mState = STATE_NONE; + @Thunk int mState = STATE_NONE; private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; private ShortcutInfo mCurrentDragInfo; @@ -141,26 +142,26 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private boolean mDeleteFolderOnDropCompleted = false; private boolean mSuppressFolderDeletion = false; private boolean mItemAddedBackToSelfViaIcon = false; - private float mFolderIconPivotX; - private float mFolderIconPivotY; + @Thunk float mFolderIconPivotX; + @Thunk float mFolderIconPivotY; private boolean mIsEditingName = false; private boolean mDestroyed; - private Runnable mDeferredAction; + @Thunk Runnable mDeferredAction; private boolean mDeferDropAfterUninstall; private boolean mUninstallSuccessful; // Folder scrolling private int mScrollAreaOffset; private Alarm mOnScrollHintAlarm; - private Alarm mScrollPauseAlarm; + @Thunk Alarm mScrollPauseAlarm; // TODO: Use {@link #mContent} once {@link #ALLOW_FOLDER_SCROLL} is removed. - private FolderPagedView mPagedView; + @Thunk FolderPagedView mPagedView; - private int mScrollHintDir = DragController.SCROLL_NONE; - private int mCurrentScrollDir = DragController.SCROLL_NONE; + @Thunk int mScrollHintDir = DragController.SCROLL_NONE; + @Thunk int mCurrentScrollDir = DragController.SCROLL_NONE; /** * Used to inflate the Workspace from XML. @@ -556,7 +557,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } - private void sendCustomAccessibilityEvent(int type, String text) { + @Thunk void sendCustomAccessibilityEvent(int type, String text) { AccessibilityManager accessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (accessibilityManager.isEnabled()) { @@ -635,7 +636,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList (int) recycle[0] - getPaddingLeft(), (int) recycle[1] - getPaddingTop()); } - private void onDragOver(DragObject d, int reorderDelay) { + @Thunk void onDragOver(DragObject d, int reorderDelay) { if (ALLOW_FOLDER_SCROLL && mScrollPauseAlarm.alarmPending()) { return; } @@ -997,7 +998,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mContent.getItemCount(); } - private void onCloseComplete() { + @Thunk void onCloseComplete() { DragLayer parent = (DragLayer) getParent(); if (parent != null) { parent.removeView(this); @@ -1020,7 +1021,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mSuppressFolderDeletion = false; } - private void replaceFolderWithFinalItem() { + @Thunk void replaceFolderWithFinalItem() { // Add the last remaining child to the workspace in place of the folder Runnable onCompleteRunnable = new Runnable() { @Override diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index dbfedaafa..f5836c295 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -43,6 +43,7 @@ import android.widget.TextView; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -50,10 +51,10 @@ import java.util.ArrayList; * An icon that can appear on in the workspace representing an {@link UserFolder}. */ public class FolderIcon extends FrameLayout implements FolderListener { - private Launcher mLauncher; - private Folder mFolder; + @Thunk Launcher mLauncher; + @Thunk Folder mFolder; private FolderInfo mInfo; - private static boolean sStaticValuesDirty = true; + @Thunk static boolean sStaticValuesDirty = true; private CheckLongPressHelper mLongPressHelper; @@ -88,8 +89,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { public static Drawable sSharedFolderLeaveBehind = null; - private ImageView mPreviewBackground; - private BubbleTextView mFolderName; + @Thunk ImageView mPreviewBackground; + @Thunk BubbleTextView mFolderName; FolderRingAnimator mFolderRingAnimator = null; @@ -109,11 +110,11 @@ public class FolderIcon extends FrameLayout implements FolderListener { private float mSlop; private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0); - private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0); - private ArrayList mHiddenItems = new ArrayList(); + @Thunk PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0); + @Thunk ArrayList mHiddenItems = new ArrayList(); private Alarm mOpenAlarm = new Alarm(); - private ItemInfo mDragInfo; + @Thunk ItemInfo mDragInfo; public FolderIcon(Context context, AttributeSet attrs) { super(context, attrs); @@ -192,7 +193,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { public static class FolderRingAnimator { public int mCellX; public int mCellY; - private CellLayout mCellLayout; + @Thunk CellLayout mCellLayout; public float mOuterRingSize; public float mInnerRingSize; public FolderIcon mFolderIcon = null; diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 21158b450..9f3126c27 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -31,6 +31,7 @@ import android.widget.Switch; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; import com.android.launcher3.PageIndicator.PageMarkerResources; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.util.Thunk; import java.text.Collator; import java.util.ArrayList; @@ -59,7 +60,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { private final LayoutInflater mInflater; private final IconCache mIconCache; - private final HashMap mPendingAnimations = new HashMap<>(); + + @Thunk final HashMap mPendingAnimations = new HashMap<>(); private final int mMaxCountX; private final int mMaxCountY; diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 39a80be78..3c7adbe8d 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -43,6 +43,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.Thunk; import java.util.HashMap; import java.util.HashSet; @@ -66,7 +67,7 @@ public class IconCache { private static final int LOW_RES_SCALE_FACTOR = 8; - private static class CacheEntry { + @Thunk static class CacheEntry { public Bitmap icon; public CharSequence title; public CharSequence contentDescription; diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 201531ea8..4349e16c1 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -32,6 +32,7 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.Thunk; import org.json.JSONException; import org.json.JSONObject; @@ -240,7 +241,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { * Ensures that we have a valid, non-null name. If the provided name is null, we will return * the application name instead. */ - private static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) { + @Thunk static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) { if (name == null) { try { PackageManager pm = context.getPackageManager(); diff --git a/src/com/android/launcher3/InterruptibleInOutAnimator.java b/src/com/android/launcher3/InterruptibleInOutAnimator.java index 2898b347d..29df38bae 100644 --- a/src/com/android/launcher3/InterruptibleInOutAnimator.java +++ b/src/com/android/launcher3/InterruptibleInOutAnimator.java @@ -21,6 +21,8 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.view.View; +import com.android.launcher3.util.Thunk; + /** * A convenience class for two-way animations, e.g. a fadeIn/fadeOut animation. * With a regular ValueAnimator, if you call reverse to show the 'out' animation, you'll get @@ -43,7 +45,7 @@ public class InterruptibleInOutAnimator { private static final int OUT = 2; // TODO: This isn't really necessary, but is here to help diagnose a bug in the drag viz - private int mDirection = STOPPED; + @Thunk int mDirection = STOPPED; public InterruptibleInOutAnimator(View view, long duration, float fromValue, float toValue) { mAnimator = LauncherAnimUtils.ofFloat(view, fromValue, toValue).setDuration(duration); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index cc63f3a4f..f2610d6e4 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -101,6 +101,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.Thunk; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -209,9 +210,9 @@ public class Launcher extends Activity /** The different states that Launcher can be in. */ enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }; - private State mState = State.WORKSPACE; - private AnimatorSet mStateAnimation; - private LauncherStateTransitionAnimation mStateTransitionAnimation; + @Thunk State mState = State.WORKSPACE; + @Thunk AnimatorSet mStateAnimation; + @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -230,7 +231,7 @@ public class Launcher extends Activity // How long to wait before the new-shortcut animation automatically pans the workspace private static int NEW_APPS_PAGE_MOVE_DELAY = 500; private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; - private static int NEW_APPS_ANIMATION_DELAY = 500; + @Thunk static int NEW_APPS_ANIMATION_DELAY = 500; private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); @@ -238,17 +239,17 @@ public class Launcher extends Activity private LayoutInflater mInflater; - private Workspace mWorkspace; + @Thunk Workspace mWorkspace; private View mLauncherView; private View mPageIndicators; - private DragLayer mDragLayer; + @Thunk DragLayer mDragLayer; private DragController mDragController; private View mWeightWatcher; private AppWidgetManagerCompat mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; - private ItemInfo mPendingAddInfo = new ItemInfo(); + @Thunk ItemInfo mPendingAddInfo = new ItemInfo(); private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo; private int mPendingAddWidgetId = -1; @@ -262,8 +263,8 @@ public class Launcher extends Activity private View mAllAppsButton; private SearchDropTargetBar mSearchDropTargetBar; - private AppsContainerView mAppsView; - private AppsCustomizeTabHost mAppsCustomizeTabHost; + @Thunk AppsContainerView mAppsView; + @Thunk AppsCustomizeTabHost mAppsCustomizeTabHost; private AppsCustomizePagedView mAppsCustomizeContent; private boolean mAutoAdvanceRunning = false; private AppWidgetHostView mQsb; @@ -276,7 +277,7 @@ public class Launcher extends Activity private SpannableStringBuilder mDefaultKeySsb = null; - private boolean mWorkspaceLoading = true; + @Thunk boolean mWorkspaceLoading = true; private boolean mPaused = true; private boolean mRestoring; @@ -290,12 +291,12 @@ public class Launcher extends Activity private LauncherModel mModel; private IconCache mIconCache; - private boolean mUserPresent = true; + @Thunk boolean mUserPresent = true; private boolean mVisible = false; private boolean mHasFocus = false; private boolean mAttached = false; - private static LocaleConfiguration sLocaleConfiguration = null; + @Thunk static LocaleConfiguration sLocaleConfiguration = null; private static HashMap sFolders = new HashMap(); @@ -307,14 +308,14 @@ public class Launcher extends Activity private final int mAdvanceStagger = 250; private long mAutoAdvanceSentTime; private long mAutoAdvanceTimeLeft = -1; - private HashMap mWidgetsToAdvance = + @Thunk HashMap mWidgetsToAdvance = new HashMap(); // Determines how long to wait after a rotation before restoring the screen orientation to // match the sensor state. private final int mRestoreScreenOrientationDelay = 500; - private Drawable mWorkspaceBackgroundDrawable; + @Thunk Drawable mWorkspaceBackgroundDrawable; private final ArrayList mSynchronouslyBoundPages = new ArrayList(); private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false; @@ -332,7 +333,7 @@ public class Launcher extends Activity // Holds the page that we need to animate to, and the icon views that we need to animate up // when we scroll to that page on resume. - private ImageView mFolderIconImageView; + @Thunk ImageView mFolderIconImageView; private Bitmap mFolderIconBitmap; private Canvas mFolderIconCanvas; private Rect mRectForFolderAnimation = new Rect(); @@ -361,7 +362,7 @@ public class Launcher extends Activity } } - private Runnable mBuildLayersRunnable = new Runnable() { + @Thunk Runnable mBuildLayersRunnable = new Runnable() { public void run() { if (mWorkspace != null) { mWorkspace.buildPageHardwareLayers(); @@ -371,7 +372,7 @@ public class Launcher extends Activity private static PendingAddArguments sPendingAddItem; - private static class PendingAddArguments { + @Thunk static class PendingAddArguments { int requestCode; Intent intent; long container; @@ -560,7 +561,7 @@ public class Launcher extends Activity } } - private void checkForLocaleChange() { + @Thunk void checkForLocaleChange() { if (sLocaleConfiguration == null) { new AsyncTask() { @Override @@ -609,13 +610,13 @@ public class Launcher extends Activity } } - private static class LocaleConfiguration { + @Thunk static class LocaleConfiguration { public String locale; public int mcc = -1; public int mnc = -1; } - private static void readConfiguration(Context context, LocaleConfiguration configuration) { + @Thunk static void readConfiguration(Context context, LocaleConfiguration configuration) { DataInputStream in = null; try { in = new DataInputStream(context.openFileInput(LauncherFiles.LAUNCHER_PREFERENCES)); @@ -637,7 +638,7 @@ public class Launcher extends Activity } } - private static void writeConfiguration(Context context, LocaleConfiguration configuration) { + @Thunk static void writeConfiguration(Context context, LocaleConfiguration configuration) { DataOutputStream out = null; try { out = new DataOutputStream(context.openFileOutput( @@ -914,7 +915,7 @@ public class Launcher extends Activity } } - private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) { + @Thunk void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) { CellLayout cellLayout = (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId); Runnable onCompleteRunnable = null; @@ -1590,7 +1591,7 @@ public class Launcher extends Activity * * @param appWidgetId The app widget id */ - private void completeAddAppWidget(int appWidgetId, long container, long screenId, + @Thunk void completeAddAppWidget(int appWidgetId, long container, long screenId, AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) { ItemInfo info = mPendingAddInfo; @@ -1765,14 +1766,14 @@ public class Launcher extends Activity } } - private void sendAdvanceMessage(long delay) { + @Thunk void sendAdvanceMessage(long delay) { mHandler.removeMessages(ADVANCE_MSG); Message msg = mHandler.obtainMessage(ADVANCE_MSG); mHandler.sendMessageDelayed(msg, delay); mAutoAdvanceSentTime = System.currentTimeMillis(); } - private void updateAutoAdvanceState() { + @Thunk void updateAutoAdvanceState() { boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty(); if (autoAdvanceRunning != mAutoAdvanceRunning) { mAutoAdvanceRunning = autoAdvanceRunning; @@ -2482,7 +2483,7 @@ public class Launcher extends Activity /** * Re-listen when widgets are reset. */ - private void onAppWidgetReset() { + @Thunk void onAppWidgetReset() { if (mAppWidgetHost != null) { mAppWidgetHost.startListening(); } @@ -2680,7 +2681,7 @@ public class Launcher extends Activity } } - private void startAppShortcutOrInfoActivity(View v) { + @Thunk void startAppShortcutOrInfoActivity(View v) { Object tag = v.getTag(); final ShortcutInfo shortcut; final Intent intent; @@ -3507,7 +3508,7 @@ public class Launcher extends Activity /** * Receives notifications when system dialogs are to be closed. */ - private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver { + @Thunk class CloseSystemDialogsIntentReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { closeSystemDialogs(); @@ -4245,7 +4246,7 @@ public class Launcher extends Activity /** * A number of packages were updated. */ - private ArrayList mWidgetsAndShortcuts; + @Thunk ArrayList mWidgetsAndShortcuts; private Runnable mBindPackagesUpdatedRunnable = new Runnable() { public void run() { bindPackagesUpdated(mWidgetsAndShortcuts); @@ -4470,7 +4471,7 @@ public class Launcher extends Activity editor.apply(); } - private void showFirstRunClings() { + @Thunk void showFirstRunClings() { // The two first run cling paths are mutually exclusive, if the launcher is preinstalled // on the device, then we always show the first run cling experience (or if there is no // launcher2). Otherwise, we prompt the user upon started for migration diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index 0ae1c0e90..42f1914c6 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -15,6 +15,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.launcher3.LauncherModel.ScreenPosProvider; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -43,7 +44,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { private final SparseArray mActions = new SparseArray(); - private final Launcher mLauncher; + @Thunk final Launcher mLauncher; public LauncherAccessibilityDelegate(Launcher launcher) { mLauncher = launcher; @@ -139,11 +140,11 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { return false; } - private void announceConfirmation(int resId) { + @Thunk void announceConfirmation(int resId) { announceConfirmation(mLauncher.getResources().getString(resId)); } - private void announceConfirmation(String confirmation) { + @Thunk void announceConfirmation(String confirmation) { mLauncher.getDragLayer().announceForAccessibility(confirmation); } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index d8896ccd2..d5d78e97d 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -38,6 +38,7 @@ import android.view.WindowManager; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; +import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -46,7 +47,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private final AppFilter mAppFilter; private final BuildInfo mBuildInfo; - private final LauncherModel mModel; + @Thunk final LauncherModel mModel; private final IconCache mIconCache; private final boolean mIsScreenLarge; diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index ef8e8abcf..2ce8b1c59 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -35,6 +35,8 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.accessibility.AccessibilityManager; +import com.android.launcher3.util.Thunk; + class LauncherClings implements OnClickListener { private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed"; private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed"; @@ -49,7 +51,7 @@ class LauncherClings implements OnClickListener { // New Secure Setting in L private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints"; - private Launcher mLauncher; + @Thunk Launcher mLauncher; private LayoutInflater mInflater; /** Ctor */ @@ -174,7 +176,7 @@ class LauncherClings implements OnClickListener { }); } - private void dismissLongPressCling() { + @Thunk void dismissLongPressCling() { Runnable dismissCb = new Runnable() { public void run() { dismissCling(mLauncher.findViewById(R.id.longpress_cling), null, @@ -185,7 +187,7 @@ class LauncherClings implements OnClickListener { } /** Hides the specified Cling */ - private void dismissCling(final View cling, final Runnable postAnimationCb, + @Thunk void dismissCling(final View cling, final Runnable postAnimationCb, final String flag, int duration) { // To catch cases where siblings of top-level views are made invisible, just check whether // the cling is directly set to GONE before dismissing it. diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 135930146..8cedcc572 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -59,6 +59,7 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; import java.net.URISyntaxException; @@ -97,14 +98,14 @@ public class LauncherModel extends BroadcastReceiver private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons private static final long INVALID_SCREEN_ID = -1L; - private final boolean mAppsCanBeOnRemoveableStorage; + @Thunk final boolean mAppsCanBeOnRemoveableStorage; private final boolean mOldContentProviderExists; - private final LauncherAppState mApp; - private final Object mLock = new Object(); - private DeferredHandler mHandler = new DeferredHandler(); - private LoaderTask mLoaderTask; - private boolean mIsLoaderTaskRunning; + @Thunk final LauncherAppState mApp; + @Thunk final Object mLock = new Object(); + @Thunk DeferredHandler mHandler = new DeferredHandler(); + @Thunk LoaderTask mLoaderTask; + @Thunk boolean mIsLoaderTaskRunning; /** * Maintain a set of packages per user, for which we added a shortcut on the workspace. @@ -118,17 +119,17 @@ public class LauncherModel extends BroadcastReceiver private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings"; - static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); + @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); static { sWorkerThread.start(); } - private static final Handler sWorker = new Handler(sWorkerThread.getLooper()); + @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper()); // We start off with everything not loaded. After that, we assume that // our monitoring of the package manager provides all updates and we never // need to do a requery. These are only ever touched from the loader thread. - private boolean mWorkspaceLoaded; - private boolean mAllAppsLoaded; + @Thunk boolean mWorkspaceLoaded; + @Thunk boolean mAllAppsLoaded; // When we are loading pages synchronously, we can't just post the binding of items on the side // pages as this delays the rotation process. Instead, we wait for a callback from the first @@ -136,7 +137,7 @@ public class LauncherModel extends BroadcastReceiver // a normal load, we also clear this set of Runnables. static final ArrayList mDeferredBindRunnables = new ArrayList(); - private WeakReference mCallbacks; + @Thunk WeakReference mCallbacks; // < only access in worker thread > AllAppsList mBgAllAppsList; @@ -175,12 +176,12 @@ public class LauncherModel extends BroadcastReceiver // - private IconCache mIconCache; + @Thunk IconCache mIconCache; protected int mPreviousConfigMcc; - private final LauncherAppsCompat mLauncherApps; - private final UserManagerCompat mUserManager; + @Thunk final LauncherAppsCompat mLauncherApps; + @Thunk final UserManagerCompat mUserManager; public interface Callbacks { public boolean setLoadOnResume(); @@ -258,10 +259,10 @@ public class LauncherModel extends BroadcastReceiver /** Runs the specified runnable immediately if called from the main thread, otherwise it is * posted on the main thread handler. */ - private void runOnMainThread(Runnable r) { + @Thunk void runOnMainThread(Runnable r) { runOnMainThread(r, 0); } - private void runOnMainThread(Runnable r, int type) { + @Thunk void runOnMainThread(Runnable r, int type) { if (sWorkerThread.getThreadId() == Process.myTid()) { // If we are on the worker thread, post onto the main handler mHandler.post(r); @@ -372,7 +373,7 @@ public class LauncherModel extends BroadcastReceiver * Find a position on the screen for the given size or adds a new screen. * @return screenId and the coordinates for the item. */ - private static Pair findSpaceForItem( + @Thunk static Pair findSpaceForItem( Context context, ScreenPosProvider preferredScreen, int fallbackStartScreen, @@ -1425,7 +1426,7 @@ public class LauncherModel extends BroadcastReceiver /** * Loads the workspace screen ids in an ordered list. */ - private static ArrayList loadWorkspaceScreensDb(Context context) { + @Thunk static ArrayList loadWorkspaceScreensDb(Context context) { final ContentResolver contentResolver = context.getContentResolver(); final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; @@ -1471,9 +1472,9 @@ public class LauncherModel extends BroadcastReceiver private class LoaderTask implements Runnable { private Context mContext; private boolean mIsLaunching; - private boolean mIsLoadingAndBindingWorkspace; + @Thunk boolean mIsLoadingAndBindingWorkspace; private boolean mStopped; - private boolean mLoadAndBindStepFinished; + @Thunk boolean mLoadAndBindStepFinished; private int mFlags; LoaderTask(Context context, boolean isLaunching, int flags) { @@ -2904,7 +2905,7 @@ public class LauncherModel extends BroadcastReceiver sWorker.post(task); } - private class AppsAvailabilityCheck extends BroadcastReceiver { + @Thunk class AppsAvailabilityCheck extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -3331,7 +3332,7 @@ public class LauncherModel extends BroadcastReceiver return widgetsAndShortcuts; } - private static boolean isPackageDisabled(Context context, String packageName, + @Thunk static boolean isPackageDisabled(Context context, String packageName, UserHandleCompat user) { final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); return !launcherApps.isPackageEnabledForProfile(packageName, user); @@ -3394,7 +3395,7 @@ public class LauncherModel extends BroadcastReceiver * Make an Intent object for a restored application or shortcut item that points * to the market page for the item. */ - private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) { + @Thunk Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) { ComponentName componentName = intent.getComponent(); return getMarketIntent(componentName.getPackageName()); } @@ -3489,7 +3490,7 @@ public class LauncherModel extends BroadcastReceiver return new ArrayList(filtered); } - private ArrayList getItemInfoForComponentName(final ComponentName cname, + @Thunk ArrayList getItemInfoForComponentName(final ComponentName cname, final UserHandleCompat user) { ItemInfoFilter filter = new ItemInfoFilter() { @Override @@ -3507,7 +3508,7 @@ public class LauncherModel extends BroadcastReceiver /** * Make an ShortcutInfo object for a shortcut that isn't an application. */ - private ShortcutInfo getShortcutInfo(Cursor c, Context context, + @Thunk ShortcutInfo getShortcutInfo(Cursor c, Context context, int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex, int titleIndex) { @@ -3611,7 +3612,7 @@ public class LauncherModel extends BroadcastReceiver * Return an existing FolderInfo object if we have encountered this ID previously, * or make a new one. */ - private static FolderInfo findOrMakeFolder(HashMap folders, long id) { + @Thunk static FolderInfo findOrMakeFolder(HashMap folders, long id) { // See if a placeholder was created for us already FolderInfo folderInfo = folders.get(id); if (folderInfo == null) { diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 59c8d929a..6dd130505 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -46,6 +46,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.util.Thunk; import java.io.File; import java.net.URISyntaxException; @@ -124,7 +125,7 @@ public class LauncherProvider extends ContentProvider { return result; } - private static long dbInsertAndCheck(DatabaseHelper helper, + @Thunk static long dbInsertAndCheck(DatabaseHelper helper, SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) { if (values == null) { throw new RuntimeException("Error: attempting to insert null values"); @@ -233,7 +234,7 @@ public class LauncherProvider extends ContentProvider { } } - private static void addModifiedTime(ContentValues values) { + @Thunk static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -342,7 +343,7 @@ public class LauncherProvider extends ContentProvider { private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback { private final Context mContext; - private final AppWidgetHost mAppWidgetHost; + @Thunk final AppWidgetHost mAppWidgetHost; private long mMaxItemId = -1; private long mMaxScreenId = -1; @@ -647,7 +648,7 @@ public class LauncherProvider extends ContentProvider { return true; } - private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { + @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { db.beginTransaction(); try { if (addRankColumn) { @@ -758,7 +759,7 @@ public class LauncherProvider extends ContentProvider { return getMaxId(db, TABLE_WORKSPACE_SCREENS); } - private boolean initializeExternalAdd(ContentValues values) { + @Thunk boolean initializeExternalAdd(ContentValues values) { // 1. Ensure that externally added items have a valid item id long id = generateNewItemId(); values.put(LauncherSettings.Favorites._ID, id); @@ -845,7 +846,7 @@ public class LauncherProvider extends ContentProvider { return rank; } - private int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) { + @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) { ArrayList screenIds = new ArrayList(); // TODO: Use multiple loaders with fall-back and transaction. int count = loader.loadLayout(db, screenIds); @@ -872,7 +873,7 @@ public class LauncherProvider extends ContentProvider { return count; } - private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) { + @Thunk void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) { final ContentResolver resolver = mContext.getContentResolver(); Cursor c = null; int count = 0; @@ -1171,7 +1172,7 @@ public class LauncherProvider extends ContentProvider { /** * @return the max _id in the provided table. */ - private static long getMaxId(SQLiteDatabase db, String table) { + @Thunk static long getMaxId(SQLiteDatabase db, String table) { Cursor c = db.rawQuery("SELECT MAX(_id) FROM " + table, null); // get the result long id = -1; diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 484ed5c30..4a0aaf3f4 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -29,6 +29,8 @@ import android.view.ViewAnimationUtils; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.util.Thunk; + import java.util.HashMap; /** @@ -112,9 +114,9 @@ public class LauncherStateTransitionAnimation { public static final int BUILD_AND_SET_LAYER = 1; public static final int SINGLE_FRAME_DELAY = 16; - private Launcher mLauncher; - private Callbacks mCb; - private AnimatorSet mStateAnimation; + @Thunk Launcher mLauncher; + @Thunk Callbacks mCb; + @Thunk AnimatorSet mStateAnimation; public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) { mLauncher = l; diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 7d65f4686..e7049e2ac 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -51,6 +51,8 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; +import com.android.launcher3.util.Thunk; + import java.util.ArrayList; interface Page { @@ -124,7 +126,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected LauncherScroller mScroller; private Interpolator mDefaultInterpolator; private VelocityTracker mVelocityTracker; - private int mPageSpacing = 0; + @Thunk int mPageSpacing = 0; private float mParentDownMotionX; private float mParentDownMotionY; @@ -207,8 +209,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private boolean mWasInOverscroll = false; // Page Indicator - private int mPageIndicatorViewId; - private PageIndicator mPageIndicator; + @Thunk int mPageIndicatorViewId; + @Thunk PageIndicator mPageIndicator; private boolean mAllowPagedViewAnimations = true; // The viewport whether the pages are to be contained (the actual view may be larger than the @@ -227,7 +229,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected View mDragView; protected AnimatorSet mZoomInOutAnim; private Runnable mSidePageHoverRunnable; - private int mSidePageHoverIndex = -1; + @Thunk int mSidePageHoverIndex = -1; // This variable's scope is only for the duration of startReordering() and endReordering() private boolean mReorderingStarted = false; // This variable's scope is for the duration of startReordering() and after the zoomIn() @@ -246,14 +248,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private Rect mAltTmpRect = new Rect(); // Fling to delete - private int FLING_TO_DELETE_FADE_OUT_DURATION = 350; + @Thunk int FLING_TO_DELETE_FADE_OUT_DURATION = 350; private float FLING_TO_DELETE_FRICTION = 0.035f; // The degrees specifies how much deviation from the up vector to still consider a fling "up" private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f; protected int mFlingToDeleteThresholdVelocity = -1400; // Drag to delete - private boolean mDeferringForDelete = false; - private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250; + @Thunk boolean mDeferringForDelete = false; + @Thunk int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250; private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350; // Drop to delete @@ -2356,7 +2358,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc super(superState); } - private SavedState(Parcel in) { + @Thunk SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @@ -2514,7 +2516,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc invalidate(); } - private void onPostReorderingAnimationCompleted() { + @Thunk void onPostReorderingAnimationCompleted() { // Trigger the callback when reordering has settled --mPostReorderingPreZoomInRemainingAnimationCount; if (mPostReorderingPreZoomInRunnable != null && diff --git a/src/com/android/launcher3/WeightWatcher.java b/src/com/android/launcher3/WeightWatcher.java index 70b8afea8..75684797f 100644 --- a/src/com/android/launcher3/WeightWatcher.java +++ b/src/com/android/launcher3/WeightWatcher.java @@ -34,6 +34,8 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.launcher3.util.Thunk; + public class WeightWatcher extends LinearLayout { private static final int RAM_GRAPH_RSS_COLOR = 0xFF990000; private static final int RAM_GRAPH_PSS_COLOR = 0xFF99CC00; @@ -81,7 +83,7 @@ public class WeightWatcher extends LinearLayout { } } }; - private MemoryTracker mMemoryService; + @Thunk MemoryTracker mMemoryService; public WeightWatcher(Context context, AttributeSet attrs) { super(context, attrs); @@ -134,7 +136,7 @@ public class WeightWatcher extends LinearLayout { GraphView mRamGraph; TextView mText; int mPid; - private MemoryTracker.ProcessMemInfo mMemInfo; + @Thunk MemoryTracker.ProcessMemInfo mMemInfo; public ProcessWatcher(Context context) { this(context, null); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 3d57acca4..0a5f0af4e 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -30,6 +30,7 @@ import android.os.Build; import android.util.Log; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.util.Thunk; import java.io.ByteArrayOutputStream; import java.io.File; @@ -50,7 +51,7 @@ public class WidgetPreviewLoader { private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version"; private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; - private static final HashSet sInvalidPackages = new HashSet(); + @Thunk static final HashSet sInvalidPackages = new HashSet(); private final HashMap> mLoadedPreviews = new HashMap<>(); private final ArrayList> mUnusedBitmaps = new ArrayList<>(); @@ -275,7 +276,7 @@ public class WidgetPreviewLoader { } } - private void writeToDb(Object o, Bitmap preview) { + @Thunk void writeToDb(Object o, Bitmap preview) { String name = getObjectName(o); SQLiteDatabase db = mDb.getWritableDatabase(); ContentValues values = new ContentValues(); @@ -590,7 +591,7 @@ public class WidgetPreviewLoader { /** * Dumps all files that are open in this process without allocating a file descriptor. */ - private static void dumpOpenFiles() { + @Thunk static void dumpOpenFiles() { try { Log.i(TAG, "DUMP OF OPEN FILES (sample rate: 1 every " + SAMPLE_RATE + "):"); final String TYPE_APK = "apk"; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 7df801da5..37265fe47 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -69,6 +69,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.HashMap; @@ -117,24 +118,24 @@ public class Workspace extends SmoothPagedView private long mCustomContentShowTime = -1; private LayoutTransition mLayoutTransition; - private final WallpaperManager mWallpaperManager; - private IBinder mWindowToken; + @Thunk final WallpaperManager mWallpaperManager; + @Thunk IBinder mWindowToken; private int mOriginalDefaultPage; private int mDefaultPage; private ShortcutAndWidgetContainer mDragSourceInternal; - private static boolean sAccessibilityEnabled; + @Thunk static boolean sAccessibilityEnabled; // The screen id used for the empty screen always present to the right. final static long EXTRA_EMPTY_SCREEN_ID = -201; private final static long CUSTOM_CONTENT_SCREEN_ID = -301; - private HashMap mWorkspaceScreens = new HashMap(); - private ArrayList mScreenOrder = new ArrayList(); + @Thunk HashMap mWorkspaceScreens = new HashMap(); + @Thunk ArrayList mScreenOrder = new ArrayList(); - private Runnable mRemoveEmptyScreenRunnable; - private boolean mDeferRemoveExtraEmptyScreen = false; + @Thunk Runnable mRemoveEmptyScreenRunnable; + @Thunk boolean mDeferRemoveExtraEmptyScreen = false; /** * CellInfo for the cell that is currently being dragged @@ -144,7 +145,7 @@ public class Workspace extends SmoothPagedView /** * Target drop area calculated during last acceptDrop call. */ - private int[] mTargetCell = new int[2]; + @Thunk int[] mTargetCell = new int[2]; private int mDragOverX = -1; private int mDragOverY = -1; @@ -159,7 +160,7 @@ public class Workspace extends SmoothPagedView /** * The CellLayout that is currently being dragged over */ - private CellLayout mDragTargetLayout = null; + @Thunk CellLayout mDragTargetLayout = null; /** * The CellLayout that we will show as glowing */ @@ -170,16 +171,16 @@ public class Workspace extends SmoothPagedView */ private CellLayout mDropToLayout = null; - private Launcher mLauncher; - private IconCache mIconCache; - private DragController mDragController; + @Thunk Launcher mLauncher; + @Thunk IconCache mIconCache; + @Thunk DragController mDragController; // These are temporary variables to prevent having to allocate a new object just to // return an (x, y) value from helper functions. Do NOT use them to maintain other state. private int[] mTempCell = new int[2]; private int[] mTempPt = new int[2]; private int[] mTempEstimate = new int[2]; - private float[] mDragViewVisualCenter = new float[2]; + @Thunk float[] mDragViewVisualCenter = new float[2]; private float[] mTempCellLayoutCenterCoordinates = new float[2]; private Matrix mTempInverseMatrix = new Matrix(); @@ -204,7 +205,7 @@ public class Workspace extends SmoothPagedView private boolean mInScrollArea = false; private HolographicOutlineHelper mOutlineHelper; - private Bitmap mDragOutline = null; + @Thunk Bitmap mDragOutline = null; private static final Rect sTempRect = new Rect(); private final int[] mTempXY = new int[2]; private int[] mTempVisiblePagesRange = new int[2]; @@ -213,11 +214,11 @@ public class Workspace extends SmoothPagedView private boolean mWorkspaceFadeInAdjacentScreens; WallpaperOffsetInterpolator mWallpaperOffset; - private boolean mWallpaperIsLiveWallpaper; - private int mNumPagesForWallpaperParallax; - private float mLastSetWallpaperOffsetSteps = 0; + @Thunk boolean mWallpaperIsLiveWallpaper; + @Thunk int mNumPagesForWallpaperParallax; + @Thunk float mLastSetWallpaperOffsetSteps = 0; - private Runnable mDelayedResizeRunnable; + @Thunk Runnable mDelayedResizeRunnable; private Runnable mDelayedSnapToPageRunnable; private Point mDisplaySize = new Point(); @@ -226,7 +227,7 @@ public class Workspace extends SmoothPagedView public static final int REORDER_TIMEOUT = 350; private final Alarm mFolderCreationAlarm = new Alarm(); private final Alarm mReorderAlarm = new Alarm(); - private FolderRingAnimator mDragFolderRingAnimator = null; + @Thunk FolderRingAnimator mDragFolderRingAnimator = null; private FolderIcon mDragOverFolderIcon = null; private boolean mCreateUserFolderOnDrop = false; private boolean mAddToExistingFolderOnDrop = false; @@ -255,8 +256,8 @@ public class Workspace extends SmoothPagedView private static final int DRAG_MODE_ADD_TO_FOLDER = 2; private static final int DRAG_MODE_REORDER = 3; private int mDragMode = DRAG_MODE_NONE; - private int mLastReorderX = -1; - private int mLastReorderY = -1; + @Thunk int mLastReorderX = -1; + @Thunk int mLastReorderY = -1; private SparseArray mSavedStates; private final ArrayList mRestoredPages = new ArrayList(); @@ -268,17 +269,17 @@ public class Workspace extends SmoothPagedView private float mCurrentScale; private float mNewScale; - private float[] mOldBackgroundAlphas; + @Thunk float[] mOldBackgroundAlphas; private float[] mOldAlphas; - private float[] mNewBackgroundAlphas; + @Thunk float[] mNewBackgroundAlphas; private float[] mNewAlphas; private int mLastChildCount = -1; private float mTransitionProgress; - private Animator mStateAnimator = null; + @Thunk Animator mStateAnimator = null; float mOverScrollEffect = 0f; - private Runnable mDeferredAction; + @Thunk Runnable mDeferredAction; private boolean mDeferDropAfterUninstall; private boolean mUninstallSuccessful; @@ -1882,7 +1883,7 @@ public class Workspace extends SmoothPagedView } } - private void updateChildrenLayersEnabled(boolean force) { + @Thunk void updateChildrenLayersEnabled(boolean force) { boolean small = mState == State.OVERVIEW || mIsSwitchingState; boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving(); @@ -2571,7 +2572,7 @@ public class Workspace extends SmoothPagedView } } - private void onTransitionEnd() { + @Thunk void onTransitionEnd() { mIsSwitchingState = false; updateChildrenLayersEnabled(false); showCustomContentIfNecessary(); @@ -4186,7 +4187,7 @@ public class Workspace extends SmoothPagedView * * pixelX and pixelY should be in the coordinate system of layout */ - private int[] findNearestArea(int pixelX, int pixelY, + @Thunk int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, CellLayout layout, int[] recycle) { return layout.findNearestArea( pixelX, pixelY, spanX, spanY, recycle); diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java index e47b9a58d..ac3d252f5 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java @@ -31,6 +31,8 @@ import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import com.android.launcher3.util.Thunk; + import java.util.ArrayList; import java.util.List; @@ -139,11 +141,11 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat { mContext.registerReceiver(mPackageMonitor, filter); } - private synchronized List getCallbacks() { + @Thunk synchronized List getCallbacks() { return new ArrayList(mCallbacks); } - private class PackageMonitor extends BroadcastReceiver { + @Thunk class PackageMonitor extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final UserHandleCompat user = UserHandleCompat.myUserHandle(); diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index 601f04cea..d6d4b8287 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -26,6 +26,7 @@ import android.util.SparseArray; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.HashSet; @@ -36,10 +37,10 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements private static final boolean DEBUG = false; // All updates to these sets must happen on the {@link #mWorker} thread. - private final SparseArray mPendingReplays = new SparseArray(); - private final HashSet mPendingBadgeUpdates = new HashSet(); + @Thunk final SparseArray mPendingReplays = new SparseArray(); + @Thunk final HashSet mPendingBadgeUpdates = new HashSet(); - private final PackageInstaller mInstaller; + @Thunk final PackageInstaller mInstaller; private final IconCache mCache; private final Handler mWorker; @@ -82,7 +83,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements return activePackages; } - private void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) { + @Thunk void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) { String packageName = info.getAppPackageName(); if (packageName != null) { mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(), @@ -123,7 +124,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements replayUpdates(null); } - private void replayUpdates(PackageInstallInfo newInfo) { + @Thunk void replayUpdates(PackageInstallInfo newInfo) { if (DEBUG) Log.d(TAG, "updates resumed"); if (!mResumed || !mBound) { // Not yet ready diff --git a/src/com/android/launcher3/util/Thunk.java b/src/com/android/launcher3/util/Thunk.java new file mode 100644 index 000000000..de350b068 --- /dev/null +++ b/src/com/android/launcher3/util/Thunk.java @@ -0,0 +1,43 @@ +/* + * 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.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the given field or method has package visibility solely to prevent the creation + * of a synthetic method. In practice, you should treat this field/method as if it were private. + *

+ * + * When a private method is called from an inner class, the Java compiler generates a simple + * package private shim method that the class generated from the inner class can call. This results + * in unnecessary bloat and runtime method call overhead. It also gets us closer to the dex method + * count limit. + *

+ * + * If you'd like to see warnings for these synthetic methods in eclipse, turn on: + * Window > Preferences > Java > Compiler > Errors/Warnings > "Access to a non-accessible member + * of an enclosing type". + *

+ * + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE}) +public @interface Thunk { } \ No newline at end of file -- cgit v1.2.3 From 5b0e669169ea2c951bf2f6f71faf793b24db3c23 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 19 Mar 2015 14:31:19 -0700 Subject: Rewrite: Widget preview loader logic > Widget previews are saved in data dir instead of cache dir > Expiring widget previews similar to IconCache > Removed support for setting thread priorities Bug: 19865031 Change-Id: Ib6033c2b1ff8ae61bba8762ca994ccd8217d3c75 --- AndroidManifest.xml | 9 - .../android/launcher3/AppsCustomizePagedView.java | 388 ++--------- src/com/android/launcher3/IconCache.java | 4 +- .../android/launcher3/InstallShortcutReceiver.java | 2 +- src/com/android/launcher3/ItemInfo.java | 21 +- src/com/android/launcher3/LauncherAppState.java | 21 +- .../android/launcher3/LauncherBackupHelper.java | 14 +- src/com/android/launcher3/LauncherModel.java | 9 +- .../android/launcher3/PackageChangedReceiver.java | 10 +- src/com/android/launcher3/PagedViewCellLayout.java | 476 ------------- .../launcher3/PagedViewCellLayoutChildren.java | 161 ----- src/com/android/launcher3/PagedViewWidget.java | 83 ++- src/com/android/launcher3/Utilities.java | 21 + src/com/android/launcher3/WidgetPreviewLoader.java | 758 ++++++++++----------- src/com/android/launcher3/Workspace.java | 10 +- 15 files changed, 524 insertions(+), 1463 deletions(-) delete mode 100644 src/com/android/launcher3/PagedViewCellLayout.java delete mode 100644 src/com/android/launcher3/PagedViewCellLayoutChildren.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fb7ac3fab..b0c77792e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -216,15 +216,6 @@ - - - - - - - - - diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 1955547c8..58bcf1dbe 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -29,10 +29,8 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.Process; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; @@ -48,90 +46,6 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.util.Thunk; import java.util.ArrayList; -import java.util.Iterator; - -/** - * A simple callback interface which also provides the results of the task. - */ -interface AsyncTaskCallback { - void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data); -} - -/** - * The data needed to perform either of the custom AsyncTasks. - */ -class AsyncTaskPageData { - enum Type { - LoadWidgetPreviewData - } - - AsyncTaskPageData(int p, ArrayList l, int cw, int ch, AsyncTaskCallback bgR, - AsyncTaskCallback postR, WidgetPreviewLoader w) { - page = p; - items = l; - generatedImages = new ArrayList(); - maxImageWidth = cw; - maxImageHeight = ch; - doInBackgroundCallback = bgR; - postExecuteCallback = postR; - widgetPreviewLoader = w; - } - void cleanup(boolean cancelled) { - // Clean up any references to source/generated bitmaps - if (generatedImages != null) { - if (cancelled) { - for (int i = 0; i < generatedImages.size(); i++) { - widgetPreviewLoader.recycleBitmap(items.get(i), generatedImages.get(i)); - } - } - generatedImages.clear(); - } - } - int page; - ArrayList items; - ArrayList sourceImages; - ArrayList generatedImages; - int maxImageWidth; - int maxImageHeight; - AsyncTaskCallback doInBackgroundCallback; - AsyncTaskCallback postExecuteCallback; - WidgetPreviewLoader widgetPreviewLoader; -} - -/** - * A generic template for an async task used in AppsCustomize. - */ -class AppsCustomizeAsyncTask extends AsyncTask { - AppsCustomizeAsyncTask(int p, AsyncTaskPageData.Type ty) { - page = p; - threadPriority = Process.THREAD_PRIORITY_DEFAULT; - dataType = ty; - } - @Override - protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) { - if (params.length != 1) return null; - // Load each of the widget previews in the background - params[0].doInBackgroundCallback.run(this, params[0]); - return params[0]; - } - @Override - protected void onPostExecute(AsyncTaskPageData result) { - // All the widget previews are loaded, so we can just callback to inflate the page - result.postExecuteCallback.run(this, result); - } - - void setThreadPriority(int p) { - threadPriority = p; - } - void syncThreadPriority() { - Process.setThreadPriority(threadPriority); - } - - // The page that this async task is associated with - AsyncTaskPageData.Type dataType; - int page; - int threadPriority; -} /** * The Apps/Customize page that displays all the applications, widgets, and shortcuts. @@ -142,6 +56,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen static final String TAG = "AppsCustomizePagedView"; private static Rect sTmpRect = new Rect(); + private static final int[] sTempPosArray = new int[2]; /** * The different content types that this paged view can show. @@ -171,10 +86,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen @Thunk int mWidgetCountX, mWidgetCountY; private int mNumWidgetPages; - // Previews & outlines - ArrayList mRunningTasks; - private static final int sPageSleepDelay = 200; - private final PagedViewKeyListener mKeyListener = new PagedViewKeyListener(); private Runnable mInflateWidgetRunnable = null; @@ -192,11 +103,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private Toast mWidgetInstructionToast; // Deferral of loading widget previews during launcher transitions - @Thunk boolean mInTransition; - private ArrayList mDeferredSyncWidgetPageItems = - new ArrayList(); - @Thunk ArrayList mDeferredPrepareLoadWidgetPreviewsTasks = - new ArrayList(); + private boolean mInTransition; WidgetPreviewLoader mWidgetPreviewLoader; @@ -209,7 +116,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mPackageManager = context.getPackageManager(); mWidgets = new ArrayList<>(); mIconCache = (LauncherAppState.getInstance()).getIconCache(); - mRunningTasks = new ArrayList<>(); // Save the default widget preview background TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); @@ -253,7 +159,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen WidgetPreviewLoader getWidgetPreviewLoader() { if (mWidgetPreviewLoader == null) { - mWidgetPreviewLoader = new WidgetPreviewLoader(mLauncher); + mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache(); } return mWidgetPreviewLoader; } @@ -484,8 +390,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen info.boundWidget = hostView; mWidgetCleanupState = WIDGET_INFLATED; hostView.setVisibility(INVISIBLE); - int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info.spanX, - info.spanY, info, false); + int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false); // We want the first widget layout to be the correct size. This will be important // for width size reporting to the AppWidgetManager. @@ -554,7 +459,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - private boolean beginDraggingWidget(View v) { + private boolean beginDraggingWidget(PagedViewWidget v) { mDraggingWidget = true; // Get the widget preview as the drag representation ImageView image = (ImageView) v.findViewById(R.id.widget_preview); @@ -582,10 +487,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo; createItemInfo = createWidgetInfo; - int spanX = createItemInfo.spanX; - int spanY = createItemInfo.spanY; - int[] size = mLauncher.getWorkspace().estimateItemSize(spanX, spanY, - createWidgetInfo, true); + int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true); FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); float minScale = 1.25f; @@ -594,10 +496,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int[] previewSizeBeforeScale = new int[1]; preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale); - // Compare the size of the drag preview to the preview in the AppsCustomize tray int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], - getWidgetPreviewLoader().maxWidthForWidgetPreview(spanX)); + v.getActualItemWidth()); scale = previewWidthInAppsCustomize / (float) preview.getWidth(); // The bitmap in the AppsCustomize tray is always the the same size, so there @@ -637,7 +538,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen if (!super.beginDragging(v)) return false; if (v instanceof PagedViewWidget) { - if (!beginDraggingWidget(v)) { + if (!beginDraggingWidget((PagedViewWidget) v)) { return false; } } else { @@ -690,7 +591,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { mInTransition = true; if (toWorkspace) { - cancelAllTasks(); + cancelAllTasks(false); } } @@ -705,15 +606,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { mInTransition = false; - for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) { - onSyncWidgetPageItems(d, false); - } - mDeferredSyncWidgetPageItems.clear(); - for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) { - r.run(); - } - mDeferredPrepareLoadWidgetPreviewsTasks.clear(); mForceDrawAllChildrenNextFrame = !toWorkspace; + if (!toWorkspace) { + loadPreviewsForPage(getNextPage()); + } } @Override @@ -782,45 +678,26 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - cancelAllTasks(); + cancelAllTasks(true); } @Override public void trimMemory() { super.trimMemory(); - clearAllWidgetPages(); + cancelAllTasks(true); } - public void clearAllWidgetPages() { - cancelAllTasks(); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View v = getPageAt(i); - if (v instanceof PagedViewGridLayout) { - ((PagedViewGridLayout) v).removeAllViewsOnPage(); - mDirtyPageContent.set(i, true); + private void cancelAllTasks(boolean clearCompletedTasks) { + for (int page = getPageCount() - 1; page >= 0; page--) { + final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); + if (layout != null) { + for (int i = 0; i < layout.getChildCount(); i++) { + ((PagedViewWidget) layout.getChildAt(i)).deletePreview(clearCompletedTasks); + } } } } - private void cancelAllTasks() { - // Clean up all the async tasks - Iterator iter = mRunningTasks.iterator(); - while (iter.hasNext()) { - AppsCustomizeAsyncTask task = iter.next(); - task.cancel(false); - iter.remove(); - mDirtyPageContent.set(task.page, true); - - // We've already preallocated the views for the data to load into, so clear them as well - View v = getPageAt(task.page); - if (v instanceof PagedViewGridLayout) { - ((PagedViewGridLayout) v).removeAllViewsOnPage(); - } - } - mDeferredSyncWidgetPageItems.clear(); - mDeferredPrepareLoadWidgetPreviewsTasks.clear(); - } public void setContentType(ContentType type) { // Widgets appear to be cleared every time you leave, always force invalidate for them @@ -835,23 +712,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen return mContentType; } - protected void snapToPage(int whichPage, int delta, int duration) { - super.snapToPage(whichPage, delta, duration); - - // Update the thread priorities given the direction lookahead - Iterator iter = mRunningTasks.iterator(); - while (iter.hasNext()) { - AppsCustomizeAsyncTask task = iter.next(); - int pageIndex = task.page; - if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) || - (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) { - task.setThreadPriority(getThreadPriorityForPage(pageIndex)); - } else { - task.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); - } - } - } - public void setPageBackgroundsVisible(boolean visible) { mPageBackgroundsVisible = visible; int childCount = getChildCount(); @@ -863,104 +723,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - /** - * A helper to return the priority for loading of the specified widget page. - */ - private int getWidgetPageLoadPriority(int page) { - // If we are snapping to another page, use that index as the target page index - int toPage = mCurrentPage; - if (mNextPage > -1) { - toPage = mNextPage; - } - - // We use the distance from the target page as an initial guess of priority, but if there - // are no pages of higher priority than the page specified, then bump up the priority of - // the specified page. - Iterator iter = mRunningTasks.iterator(); - int minPageDiff = Integer.MAX_VALUE; - while (iter.hasNext()) { - AppsCustomizeAsyncTask task = iter.next(); - minPageDiff = Math.abs(task.page - toPage); - } - - int rawPageDiff = Math.abs(page - toPage); - return rawPageDiff - Math.min(rawPageDiff, minPageDiff); - } - /** - * Return the appropriate thread priority for loading for a given page (we give the current - * page much higher priority) - */ - private int getThreadPriorityForPage(int page) { - // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below - int pageDiff = getWidgetPageLoadPriority(page); - if (pageDiff <= 0) { - return Process.THREAD_PRIORITY_LESS_FAVORABLE; - } else if (pageDiff <= 1) { - return Process.THREAD_PRIORITY_LOWEST; - } else { - return Process.THREAD_PRIORITY_LOWEST; - } - } - private int getSleepForPage(int page) { - int pageDiff = getWidgetPageLoadPriority(page); - return Math.max(0, pageDiff * sPageSleepDelay); - } - /** - * Creates and executes a new AsyncTask to load a page of widget previews. - */ - @Thunk void prepareLoadWidgetPreviewsTask(int page, ArrayList widgets, - int cellWidth, int cellHeight, int cellCountX) { - - // Prune all tasks that are no longer needed - Iterator iter = mRunningTasks.iterator(); - while (iter.hasNext()) { - AppsCustomizeAsyncTask task = iter.next(); - int taskPage = task.page; - if (taskPage < getAssociatedLowerPageBound(mCurrentPage) || - taskPage > getAssociatedUpperPageBound(mCurrentPage)) { - task.cancel(false); - iter.remove(); - } else { - task.setThreadPriority(getThreadPriorityForPage(taskPage)); - } - } - - // We introduce a slight delay to order the loading of side pages so that we don't thrash - final int sleepMs = getSleepForPage(page); - AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight, - new AsyncTaskCallback() { - @Override - public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { - try { - try { - Thread.sleep(sleepMs); - } catch (Exception e) {} - loadWidgetPreviewsInBackground(task, data); - } finally { - if (task.isCancelled()) { - data.cleanup(true); - } - } - } - }, - new AsyncTaskCallback() { - @Override - public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { - mRunningTasks.remove(task); - if (task.isCancelled()) return; - // do cleanup inside onSyncWidgetPageItems - onSyncWidgetPageItems(data, false); - } - }, getWidgetPreviewLoader()); - - // Ensure that the task is appropriately prioritized and runs in parallel - AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page, - AsyncTaskPageData.Type.LoadWidgetPreviewData); - t.setThreadPriority(getThreadPriorityForPage(page)); - t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData); - mRunningTasks.add(t); - } - /* * Widgets PagedView implementation */ @@ -1051,95 +813,18 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen layout.addView(widget, lp); } - // wait until a call on onLayout to start loading, because - // PagedViewWidget.getPreviewSize() will return 0 if it hasn't been laid out - // TODO: can we do a measure/layout immediately? - layout.setOnLayoutListener(new Runnable() { - public void run() { - // Load the widget previews - int maxPreviewWidth = cellWidth; - int maxPreviewHeight = cellHeight; - if (layout.getChildCount() > 0) { - PagedViewWidget w = (PagedViewWidget) layout.getChildAt(0); - int[] maxSize = w.getPreviewSize(); - maxPreviewWidth = maxSize[0]; - maxPreviewHeight = maxSize[1]; - } - - getWidgetPreviewLoader().setPreviewSize(maxPreviewWidth, maxPreviewHeight); - if (immediate) { - AsyncTaskPageData data = new AsyncTaskPageData(page, items, - maxPreviewWidth, maxPreviewHeight, null, null, getWidgetPreviewLoader()); - loadWidgetPreviewsInBackground(null, data); - onSyncWidgetPageItems(data, immediate); - } else { - if (mInTransition) { - mDeferredPrepareLoadWidgetPreviewsTasks.add(this); - } else { - prepareLoadWidgetPreviewsTask(page, items, - maxPreviewWidth, maxPreviewHeight, mWidgetCountX); - } - } - layout.setOnLayoutListener(null); - } - }); - } - @Thunk void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, - AsyncTaskPageData data) { - // loadWidgetPreviewsInBackground can be called without a task to load a set of widget - // previews synchronously - if (task != null) { - // Ensure that this task starts running at the correct priority - task.syncThreadPriority(); - } - - // Load each of the widget/shortcut previews - ArrayList items = data.items; - ArrayList images = data.generatedImages; - int count = items.size(); - for (int i = 0; i < count; ++i) { - if (task != null) { - // Ensure we haven't been cancelled yet - if (task.isCancelled()) break; - // Before work on each item, ensure that this task is running at the correct - // priority - task.syncThreadPriority(); - } - - images.add(getWidgetPreviewLoader().getPreview(items.get(i))); + if (immediate && !mInTransition) { + loadPreviewsForPage(page); } } - @Thunk void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) { - if (!immediatelySyncItems && mInTransition) { - mDeferredSyncWidgetPageItems.add(data); - return; - } - try { - int page = data.page; - PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - - ArrayList items = data.items; - int count = items.size(); - for (int i = 0; i < count; ++i) { - PagedViewWidget widget = (PagedViewWidget) layout.getChildAt(i); - if (widget != null) { - Bitmap preview = data.generatedImages.get(i); - widget.applyPreview(new FastBitmapDrawable(preview), i); - } - } - - enableHwLayersOnVisiblePages(); + private void loadPreviewsForPage(int page) { + final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - // Update all thread priorities - Iterator iter = mRunningTasks.iterator(); - while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); - int pageIndex = task.page; - task.setThreadPriority(getThreadPriorityForPage(pageIndex)); + if (layout != null) { + for (int i = 0; i < layout.getChildCount(); i++) { + ((PagedViewWidget) layout.getChildAt(i)).ensurePreview(); } - } finally { - data.cleanup(false); } } @@ -1148,7 +833,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen disablePagedViewAnimations(); removeAllViews(); - cancelAllTasks(); + cancelAllTasks(true); Context context = getContext(); if (mContentType == ContentType.Widgets) { @@ -1254,6 +939,17 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mSaveInstanceStateItemIndex = -1; } + @Override + protected void onPageBeginMoving() { + super.onPageBeginMoving(); + if (!mInTransition) { + getVisiblePages(sTempPosArray); + for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) { + loadPreviewsForPage(i); + } + } + } + /* * AllAppsView implementation */ @@ -1274,7 +970,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // request a layout to trigger the page data when ready. requestLayout(); } else { - cancelAllTasks(); + cancelAllTasks(false); invalidatePageData(); } } @@ -1324,7 +1020,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // should stop this now. // Stop all background tasks - cancelAllTasks(); + cancelAllTasks(true); } /* diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 3c7adbe8d..9b2119e0b 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -656,8 +656,8 @@ public class IconCache { public ContentValues newContentValues(Bitmap icon, String label) { ContentValues values = new ContentValues(); - values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon)); - values.put(IconDB.COLUMN_ICON_LOW_RES, ItemInfo.flattenBitmap( + values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon)); + values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap( Bitmap.createScaledBitmap(icon, icon.getWidth() / LOW_RES_SCALE_FACTOR, icon.getHeight() / LOW_RES_SCALE_FACTOR, true))); diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 4349e16c1..0db22a499 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -331,7 +331,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0)) .key(NAME_KEY).value(name); if (icon != null) { - byte[] iconByteArray = ItemInfo.flattenBitmap(icon); + byte[] iconByteArray = Utilities.flattenBitmap(icon); json = json.key(ICON_KEY).value( Base64.encodeToString( iconByteArray, 0, iconByteArray.length, Base64.DEFAULT)); diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index aff8323c2..f114de221 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -20,13 +20,10 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.util.Log; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.util.Arrays; /** @@ -177,25 +174,9 @@ public class ItemInfo { } } - static byte[] flattenBitmap(Bitmap bitmap) { - // Try go guesstimate how much space the icon will take when serialized - // to avoid unnecessary allocations/copies during the write. - int size = bitmap.getWidth() * bitmap.getHeight() * 4; - ByteArrayOutputStream out = new ByteArrayOutputStream(size); - try { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - out.flush(); - out.close(); - return out.toByteArray(); - } catch (IOException e) { - Log.w("Favorite", "Could not write icon"); - return null; - } - } - static void writeBitmap(ContentValues values, Bitmap bitmap) { if (bitmap != null) { - byte[] data = flattenBitmap(bitmap); + byte[] data = Utilities.flattenBitmap(bitmap); values.put(LauncherSettings.Favorites.ICON, data); } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 9082276e4..555b1ccad 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -32,7 +32,6 @@ import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; -import android.view.View.AccessibilityDelegate; import android.view.WindowManager; import com.android.launcher3.compat.LauncherAppsCompat; @@ -49,12 +48,12 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private final BuildInfo mBuildInfo; @Thunk final LauncherModel mModel; private final IconCache mIconCache; + private final WidgetPreviewLoader mWidgetCache; private final boolean mIsScreenLarge; private final float mScreenDensity; private final int mLongPressTimeout = 300; - private WidgetPreviewLoader.CacheDb mWidgetPreviewCacheDb; private boolean mWallpaperChangedSinceLastCheck; private static WeakReference sLauncherProvider; @@ -101,9 +100,8 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { // set sIsScreenXLarge and mScreenDensity *before* creating icon cache mIsScreenLarge = isScreenLarge(sContext.getResources()); mScreenDensity = sContext.getResources().getDisplayMetrics().density; - - recreateWidgetPreviewDb(); mIconCache = new IconCache(sContext); + mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache); mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class)); @@ -125,13 +123,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { mFavoritesObserver); } - public void recreateWidgetPreviewDb() { - if (mWidgetPreviewCacheDb != null) { - mWidgetPreviewCacheDb.close(); - } - mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(sContext); - } - /** * Call from Application.onTerminate(), which is not guaranteed to ever be called. */ @@ -181,10 +172,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mAppFilter == null || mAppFilter.shouldShowApp(componentName); } - WidgetPreviewLoader.CacheDb getWidgetPreviewCacheDb() { - return mWidgetPreviewCacheDb; - } - static void setLauncherProvider(LauncherProvider provider) { sLauncherProvider = new WeakReference(provider); } @@ -240,6 +227,10 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mDynamicGrid; } + public WidgetPreviewLoader getWidgetCache() { + return mWidgetCache; + } + public boolean isScreenLarge() { return mIsScreenLarge; } diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 57f92bc20..c03480065 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -630,7 +630,7 @@ public class LauncherBackupHelper implements BackupHelper { return; } final ContentResolver cr = mContext.getContentResolver(); - final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(mContext); + final WidgetPreviewLoader previewLoader = appState.getWidgetCache(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile(); if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx); @@ -646,7 +646,6 @@ public class LauncherBackupHelper implements BackupHelper { final long id = cursor.getLong(ID_INDEX); final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX); final int spanX = cursor.getInt(SPANX_INDEX); - final int spanY = cursor.getInt(SPANY_INDEX); final ComponentName provider = ComponentName.unflattenFromString(providerName); Key key = null; String backupKey = null; @@ -665,12 +664,10 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount); if (backupWidgetCount < MAX_WIDGETS_PER_PASS) { if (DEBUG) Log.d(TAG, "saving widget " + backupKey); - previewLoader.setPreviewSize( - spanX * profile.cellWidthPx, - spanY * profile.cellHeightPx); UserHandleCompat user = UserHandleCompat.myUserHandle(); writeRowToBackup(key, - packWidget(dpi, previewLoader, mIconCache, provider, user), + packWidget(dpi, previewLoader,spanX * profile.cellWidthPx, + mIconCache, provider, user), data); mKeys.add(key); backupWidgetCount ++; @@ -980,7 +977,8 @@ public class LauncherBackupHelper implements BackupHelper { } /** Serialize a widget for persistence, including a checksum wrapper. */ - private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache, + private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, + int previewWidth, IconCache iconCache, ComponentName provider, UserHandleCompat user) { final LauncherAppWidgetProviderInfo info = LauncherModel.getProviderInfo(mContext, provider, user); @@ -1000,7 +998,7 @@ public class LauncherBackupHelper implements BackupHelper { } if (info.previewImage != 0) { widget.preview = new Resource(); - Bitmap preview = previewLoader.generateWidgetPreview(info, null); + Bitmap preview = previewLoader.generateWidgetPreview(info, previewWidth, null); ByteArrayOutputStream os = new ByteArrayOutputStream(); if (preview.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) { widget.preview.data = os.toByteArray(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 8cedcc572..dcb375928 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1622,6 +1622,9 @@ public class LauncherModel extends BroadcastReceiver if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); loadAndBindAllApps(); + // Remove entries for packages which changed while the launcher was dead. + LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(); + // Restore the default thread priority after we are done loading items synchronized (mLock) { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); @@ -3007,8 +3010,7 @@ public class LauncherModel extends BroadcastReceiver if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); mIconCache.updateIconsForPkg(packages[i], mUser); mBgAllAppsList.updatePackage(context, packages[i], mUser); - WidgetPreviewLoader.removePackageFromDb( - mApp.getWidgetPreviewCacheDb(), packages[i]); + mApp.getWidgetCache().removePackage(packages[i], mUser); } break; case OP_REMOVE: @@ -3034,8 +3036,7 @@ public class LauncherModel extends BroadcastReceiver for (int i=0; i= 0 && lp.cellX <= (mCellCountX - 1) && - lp.cellY >= 0 && (lp.cellY <= mCellCountY - 1)) { - // If the horizontal or vertical span is set to -1, it is taken to - // mean that it spans the extent of the CellLayout - if (lp.cellHSpan < 0) lp.cellHSpan = mCellCountX; - if (lp.cellVSpan < 0) lp.cellVSpan = mCellCountY; - - child.setId(childId); - mChildren.addView(child, index, lp); - - return true; - } - return false; - } - - @Override - public void removeAllViewsOnPage() { - mChildren.removeAllViews(); - setLayerType(LAYER_TYPE_NONE, null); - } - - @Override - public void removeViewOnPageAt(int index) { - mChildren.removeViewAt(index); - } - - /** - * Clears all the key listeners for the individual icons. - */ - public void resetChildrenOnKeyListeners() { - int childCount = mChildren.getChildCount(); - for (int j = 0; j < childCount; ++j) { - mChildren.getChildAt(j).setOnKeyListener(null); - } - } - - @Override - public int getPageChildCount() { - return mChildren.getChildCount(); - } - - public PagedViewCellLayoutChildren getChildrenLayout() { - return mChildren; - } - - @Override - public View getChildOnPageAt(int i) { - return mChildren.getChildAt(i); - } - - @Override - public int indexOfChildOnPage(View v) { - return mChildren.indexOfChild(v); - } - - public int getCellCountX() { - return mCellCountX; - } - - public int getCellCountY() { - return mCellCountY; - } - - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { - throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions"); - } - - int numWidthGaps = mCellCountX - 1; - int numHeightGaps = mCellCountY - 1; - - if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) { - int hSpace = widthSpecSize - getPaddingLeft() - getPaddingRight(); - int vSpace = heightSpecSize - getPaddingTop() - getPaddingBottom(); - int hFreeSpace = hSpace - (mCellCountX * mOriginalCellWidth); - int vFreeSpace = vSpace - (mCellCountY * mOriginalCellHeight); - mWidthGap = numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0; - mHeightGap = numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0; - - mChildren.setGap(mWidthGap, mHeightGap); - } else { - mWidthGap = mOriginalWidthGap; - mHeightGap = mOriginalHeightGap; - } - - // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY - int newWidth = widthSpecSize; - int newHeight = heightSpecSize; - if (widthSpecMode == MeasureSpec.AT_MOST) { - newWidth = getPaddingLeft() + getPaddingRight() + (mCellCountX * mCellWidth) + - ((mCellCountX - 1) * mWidthGap); - newHeight = getPaddingTop() + getPaddingBottom() + (mCellCountY * mCellHeight) + - ((mCellCountY - 1) * mHeightGap); - setMeasuredDimension(newWidth, newHeight); - } - - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - int childWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(newWidth - getPaddingLeft() - - getPaddingRight(), MeasureSpec.EXACTLY); - int childheightMeasureSpec = - MeasureSpec.makeMeasureSpec(newHeight - getPaddingTop() - - getPaddingBottom(), MeasureSpec.EXACTLY); - child.measure(childWidthMeasureSpec, childheightMeasureSpec); - } - - setMeasuredDimension(newWidth, newHeight); - } - - int getContentWidth() { - return getWidthBeforeFirstLayout() + getPaddingLeft() + getPaddingRight(); - } - - int getContentHeight() { - if (mCellCountY > 0) { - return mCellCountY * mCellHeight + (mCellCountY - 1) * Math.max(0, mHeightGap); - } - return 0; - } - - int getWidthBeforeFirstLayout() { - if (mCellCountX > 0) { - return mCellCountX * mCellWidth + (mCellCountX - 1) * Math.max(0, mWidthGap); - } - return 0; - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - child.layout(getPaddingLeft(), getPaddingTop(), - r - l - getPaddingRight(), b - t - getPaddingBottom()); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean result = super.onTouchEvent(event); - int count = getPageChildCount(); - if (count > 0) { - // We only intercept the touch if we are tapping in empty space after the final row - View child = getChildOnPageAt(count - 1); - int bottom = child.getBottom(); - int numRows = (int) Math.ceil((float) getPageChildCount() / getCellCountX()); - if (numRows < getCellCountY()) { - // Add a little bit of buffer if there is room for another row - bottom += mCellHeight / 2; - } - result = result || (event.getY() < bottom); - } - return result; - } - - public void enableCenteredContent(boolean enabled) { - mChildren.enableCenteredContent(enabled); - } - - @Override - protected void setChildrenDrawingCacheEnabled(boolean enabled) { - mChildren.setChildrenDrawingCacheEnabled(enabled); - } - - public void setCellCount(int xCount, int yCount) { - mCellCountX = xCount; - mCellCountY = yCount; - requestLayout(); - } - - public void setGap(int widthGap, int heightGap) { - mOriginalWidthGap = mWidthGap = widthGap; - mOriginalHeightGap = mHeightGap = heightGap; - mChildren.setGap(widthGap, heightGap); - } - - public int[] getCellCountForDimensions(int width, int height) { - // Always assume we're working with the smallest span to make sure we - // reserve enough space in both orientations - int smallerSize = Math.min(mCellWidth, mCellHeight); - - // Always round up to next largest cell - int spanX = (width + smallerSize) / smallerSize; - int spanY = (height + smallerSize) / smallerSize; - - return new int[] { spanX, spanY }; - } - - /** - * Start dragging the specified child - * - * @param child The child that is being dragged - */ - void onDragChild(View child) { - PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); - lp.isDragging = true; - } - - /** - * Estimates the number of cells that the specified width would take up. - */ - public int estimateCellHSpan(int width) { - // We don't show the next/previous pages any more, so we use the full width, minus the - // padding - int availWidth = width - (getPaddingLeft() + getPaddingRight()); - - // We know that we have to fit N cells with N-1 width gaps, so we just juggle to solve for N - int n = Math.max(1, (availWidth + mWidthGap) / (mCellWidth + mWidthGap)); - - // We don't do anything fancy to determine if we squeeze another row in. - return n; - } - - /** - * Estimates the number of cells that the specified height would take up. - */ - public int estimateCellVSpan(int height) { - // The space for a page is the height - top padding (current page) - bottom padding (current - // page) - int availHeight = height - (getPaddingTop() + getPaddingBottom()); - - // We know that we have to fit N cells with N-1 height gaps, so we juggle to solve for N - int n = Math.max(1, (availHeight + mHeightGap) / (mCellHeight + mHeightGap)); - - // We don't do anything fancy to determine if we squeeze another row in. - return n; - } - - /** Returns an estimated center position of the cell at the specified index */ - public int[] estimateCellPosition(int x, int y) { - return new int[] { - getPaddingLeft() + (x * mCellWidth) + (x * mWidthGap) + (mCellWidth / 2), - getPaddingTop() + (y * mCellHeight) + (y * mHeightGap) + (mCellHeight / 2) - }; - } - - public void calculateCellCount(int width, int height, int maxCellCountX, int maxCellCountY) { - mCellCountX = Math.min(maxCellCountX, estimateCellHSpan(width)); - mCellCountY = Math.min(maxCellCountY, estimateCellVSpan(height)); - requestLayout(); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new PagedViewCellLayout.LayoutParams(getContext(), attrs); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof PagedViewCellLayout.LayoutParams; - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new PagedViewCellLayout.LayoutParams(p); - } - - public static class LayoutParams extends ViewGroup.MarginLayoutParams { - /** - * Horizontal location of the item in the grid. - */ - @ViewDebug.ExportedProperty - public int cellX; - - /** - * Vertical location of the item in the grid. - */ - @ViewDebug.ExportedProperty - public int cellY; - - /** - * Number of cells spanned horizontally by the item. - */ - @ViewDebug.ExportedProperty - public int cellHSpan; - - /** - * Number of cells spanned vertically by the item. - */ - @ViewDebug.ExportedProperty - public int cellVSpan; - - /** - * Is this item currently being dragged - */ - public boolean isDragging; - - // a data object that you can bind to this layout params - private Object mTag; - - // X coordinate of the view in the layout. - @ViewDebug.ExportedProperty - int x; - // Y coordinate of the view in the layout. - @ViewDebug.ExportedProperty - int y; - - public LayoutParams() { - super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - cellHSpan = 1; - cellVSpan = 1; - } - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - cellHSpan = 1; - cellVSpan = 1; - } - - public LayoutParams(ViewGroup.LayoutParams source) { - super(source); - cellHSpan = 1; - cellVSpan = 1; - } - - public LayoutParams(LayoutParams source) { - super(source); - this.cellX = source.cellX; - this.cellY = source.cellY; - this.cellHSpan = source.cellHSpan; - this.cellVSpan = source.cellVSpan; - } - - public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) { - super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - this.cellX = cellX; - this.cellY = cellY; - this.cellHSpan = cellHSpan; - this.cellVSpan = cellVSpan; - } - - public void setup(Context context, - int cellWidth, int cellHeight, int widthGap, int heightGap, - int hStartPadding, int vStartPadding) { - - final int myCellHSpan = cellHSpan; - final int myCellVSpan = cellVSpan; - final int myCellX = cellX; - final int myCellY = cellY; - - width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) - - leftMargin - rightMargin; - height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) - - topMargin - bottomMargin; - - if (LauncherAppState.getInstance().isScreenLarge()) { - x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin; - y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin; - } else { - x = myCellX * (cellWidth + widthGap) + leftMargin; - y = myCellY * (cellHeight + heightGap) + topMargin; - } - } - - public Object getTag() { - return mTag; - } - - public void setTag(Object tag) { - mTag = tag; - } - - public String toString() { - return "(" + this.cellX + ", " + this.cellY + ", " + - this.cellHSpan + ", " + this.cellVSpan + ")"; - } - } -} \ No newline at end of file diff --git a/src/com/android/launcher3/PagedViewCellLayoutChildren.java b/src/com/android/launcher3/PagedViewCellLayoutChildren.java deleted file mode 100644 index 84d2b1dd3..000000000 --- a/src/com/android/launcher3/PagedViewCellLayoutChildren.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2010 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.Context; -import android.graphics.Rect; -import android.view.View; -import android.view.ViewGroup; - -/** - * An abstraction of the original CellLayout which supports laying out items - * which span multiple cells into a grid-like layout. Also supports dimming - * to give a preview of its contents. - */ -public class PagedViewCellLayoutChildren extends ViewGroup { - static final String TAG = "PagedViewCellLayout"; - - private boolean mCenterContent; - - private int mCellWidth; - private int mCellHeight; - private int mWidthGap; - private int mHeightGap; - - public PagedViewCellLayoutChildren(Context context) { - super(context); - } - - @Override - public void cancelLongPress() { - super.cancelLongPress(); - - // Cancel long press for all children - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - child.cancelLongPress(); - } - } - - public void setGap(int widthGap, int heightGap) { - mWidthGap = widthGap; - mHeightGap = heightGap; - requestLayout(); - } - - public void setCellDimensions(int width, int height) { - mCellWidth = width; - mCellHeight = height; - requestLayout(); - } - - @Override - public void requestChildFocus(View child, View focused) { - super.requestChildFocus(child, focused); - if (child != null) { - Rect r = new Rect(); - child.getDrawingRect(r); - requestRectangleOnScreen(r); - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { - throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions"); - } - - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - PagedViewCellLayout.LayoutParams lp = - (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); - lp.setup(getContext(), - mCellWidth, mCellHeight, mWidthGap, mHeightGap, - getPaddingLeft(), - getPaddingTop()); - - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, - MeasureSpec.EXACTLY); - int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, - MeasureSpec.EXACTLY); - - child.measure(childWidthMeasureSpec, childheightMeasureSpec); - } - - setMeasuredDimension(widthSpecSize, heightSpecSize); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int count = getChildCount(); - - int offsetX = 0; - if (mCenterContent && count > 0) { - // determine the max width of all the rows and center accordingly - int maxRowX = 0; - int minRowX = Integer.MAX_VALUE; - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() != GONE) { - PagedViewCellLayout.LayoutParams lp = - (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); - minRowX = Math.min(minRowX, lp.x); - maxRowX = Math.max(maxRowX, lp.x + lp.width); - } - } - int maxRowWidth = maxRowX - minRowX; - offsetX = (getMeasuredWidth() - maxRowWidth) / 2; - } - - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if (child.getVisibility() != GONE) { - PagedViewCellLayout.LayoutParams lp = - (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); - - int childLeft = offsetX + lp.x; - int childTop = lp.y; - child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height); - } - } - } - - public void enableCenteredContent(boolean enabled) { - mCenterContent = enabled; - } - - @Override - protected void setChildrenDrawingCacheEnabled(boolean enabled) { - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View view = getChildAt(i); - view.setDrawingCacheEnabled(enabled); - // Update the drawing caches - if (!view.isHardwareAccelerated()) { - view.buildDrawingCache(true); - } - } - } -} diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java index 107069b78..d9ca7be87 100644 --- a/src/com/android/launcher3/PagedViewWidget.java +++ b/src/com/android/launcher3/PagedViewWidget.java @@ -20,35 +20,38 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.Rect; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; +import android.view.View.OnLayoutChangeListener; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; import com.android.launcher3.compat.AppWidgetManagerCompat; /** * The linear layout used strictly for the widget/wallpaper tab of the customization tray */ -public class PagedViewWidget extends LinearLayout { - static final String TAG = "PagedViewWidgetLayout"; +public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListener { - private static boolean sDeletePreviewsWhenDetachedFromWindow = true; - private static boolean sRecyclePreviewsWhenDetachedFromWindow = true; + private static PagedViewWidget sShortpressTarget = null; - private String mDimensionsFormatString; - CheckForShortPress mPendingCheckForShortPress = null; - ShortPressListener mShortPressListener = null; - boolean mShortPressTriggered = false; - static PagedViewWidget sShortpressTarget = null; - boolean mIsAppWidget; private final Rect mOriginalImagePadding = new Rect(); + + private String mDimensionsFormatString; + private CheckForShortPress mPendingCheckForShortPress = null; + private ShortPressListener mShortPressListener = null; + private boolean mShortPressTriggered = false; + private boolean mIsAppWidget; private Object mInfo; + private WidgetPreviewLoader mWidgetPreviewLoader; + private PreviewLoadRequest mActiveRequest; public PagedViewWidget(Context context) { this(context, null); @@ -92,29 +95,24 @@ public class PagedViewWidget extends LinearLayout { } } - public static void setDeletePreviewsWhenDetachedFromWindow(boolean value) { - sDeletePreviewsWhenDetachedFromWindow = value; - } - - public static void setRecyclePreviewsWhenDetachedFromWindow(boolean value) { - sRecyclePreviewsWhenDetachedFromWindow = value; - } - @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + deletePreview(true); + } - if (sDeletePreviewsWhenDetachedFromWindow) { + public void deletePreview(boolean recycleImage) { + if (recycleImage) { final ImageView image = (ImageView) findViewById(R.id.widget_preview); if (image != null) { - FastBitmapDrawable preview = (FastBitmapDrawable) image.getDrawable(); - if (sRecyclePreviewsWhenDetachedFromWindow && - mInfo != null && preview != null && preview.getBitmap() != null) { - mWidgetPreviewLoader.recycleBitmap(mInfo, preview.getBitmap()); - } image.setImageDrawable(null); } } + + if (mActiveRequest != null) { + mActiveRequest.cancel(recycleImage); + mActiveRequest = null; + } } public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, @@ -161,7 +159,8 @@ public class PagedViewWidget extends LinearLayout { return maxSize; } - void applyPreview(FastBitmapDrawable preview, int index) { + public void applyPreview(Bitmap bitmap) { + FastBitmapDrawable preview = new FastBitmapDrawable(bitmap); final PagedViewWidgetImageView image = (PagedViewWidgetImageView) findViewById(R.id.widget_preview); if (preview != null) { @@ -259,4 +258,38 @@ public class PagedViewWidget extends LinearLayout { // we just always mark the touch event as handled. return true; } + + public void ensurePreview() { + if (mActiveRequest != null) { + return; + } + int[] size = getPreviewSize(); + + if (size[0] <= 0 || size[1] <= 0) { + addOnLayoutChangeListener(this); + return; + } + Bitmap[] immediateResult = new Bitmap[1]; + mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this, + immediateResult); + if (immediateResult[0] != null) { + applyPreview(immediateResult[0]); + } + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + removeOnLayoutChangeListener(this); + ensurePreview(); + } + + public int getActualItemWidth() { + ItemInfo info = (ItemInfo) getTag(); + int[] size = getPreviewSize(); + int cellWidth = LauncherAppState.getInstance() + .getDynamicGrid().getDeviceProfile().cellWidthPx; + + return Math.min(size[0], info.spanX * cellWidth); + } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 497b43874..22677c8ea 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -50,6 +50,8 @@ import android.util.SparseArray; import android.view.View; import android.widget.Toast; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; @@ -555,6 +557,25 @@ public final class Utilities { return defaultWidgetForSearchPackage; } + /** + * Compresses the bitmap to a byte array for serialization. + */ + public static byte[] flattenBitmap(Bitmap bitmap) { + // Try go guesstimate how much space the icon will take when serialized + // to avoid unnecessary allocations/copies during the write. + int size = bitmap.getWidth() * bitmap.getHeight() * 4; + ByteArrayOutputStream out = new ByteArrayOutputStream(size); + try { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } + } + public static final Comparator RANK_COMPARATOR = new Comparator() { @Override diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 0a5f0af4e..1043e2ee0 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -1,18 +1,16 @@ package com.android.launcher3; -import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; -import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.database.Cursor; -import android.database.sqlite.SQLiteCantOpenDatabaseException; +import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDiskIOException; import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteReadOnlyDatabaseException; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; @@ -26,400 +24,332 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.os.Build; import android.util.Log; +import android.util.LongSparseArray; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; public class WidgetPreviewLoader { private static final String TAG = "WidgetPreviewLoader"; - private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version"; private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; - @Thunk static final HashSet sInvalidPackages = new HashSet(); - private final HashMap> mLoadedPreviews = new HashMap<>(); - private final ArrayList> mUnusedBitmaps = new ArrayList<>(); - - private final int mAppIconSize; - private final int mCellWidth; + private final HashMap mPackageVersions = new HashMap<>(); + private final HashMap> mLoadedPreviews = new HashMap<>(); + private Set mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap()); private final Context mContext; private final IconCache mIconCache; + private final UserManagerCompat mUserManager; private final AppWidgetManagerCompat mManager; - - private int mPreviewBitmapWidth; - private int mPreviewBitmapHeight; - private String mSize; - - private String mCachedSelectQuery; - private CacheDb mDb; + private final CacheDb mDb; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - public WidgetPreviewLoader(Context context) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - + public WidgetPreviewLoader(Context context, IconCache iconCache) { mContext = context; - mAppIconSize = grid.iconSizePx; - mCellWidth = grid.cellWidthPx; - - mIconCache = app.getIconCache(); + mIconCache = iconCache; mManager = AppWidgetManagerCompat.getInstance(context); - mDb = app.getWidgetPreviewCacheDb(); - - SharedPreferences sp = context.getSharedPreferences( - LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); - final String lastVersionName = sp.getString(ANDROID_INCREMENTAL_VERSION_NAME_KEY, null); - final String versionName = android.os.Build.VERSION.INCREMENTAL; - final boolean isLollipopOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - if (!versionName.equals(lastVersionName)) { - try { - // clear all the previews whenever the system version changes, to ensure that - // previews are up-to-date for any apps that might have been updated with the system - clearDb(); - } catch (SQLiteReadOnlyDatabaseException e) { - if (isLollipopOrGreater) { - // Workaround for Bug. 18554839, if we fail to clear the db due to the read-only - // issue, then ignore this error and leave the old previews - } else { - throw e; - } - } finally { - SharedPreferences.Editor editor = sp.edit(); - editor.putString(ANDROID_INCREMENTAL_VERSION_NAME_KEY, versionName); - editor.commit(); - } - } + mUserManager = UserManagerCompat.getInstance(context); + mDb = new CacheDb(context); } - public void recreateDb() { - LauncherAppState app = LauncherAppState.getInstance(); - app.recreateWidgetPreviewDb(); - mDb = app.getWidgetPreviewCacheDb(); - } - - public void setPreviewSize(int previewWidth, int previewHeight) { - mPreviewBitmapWidth = previewWidth; - mPreviewBitmapHeight = previewHeight; - mSize = previewWidth + "x" + previewHeight; - } - - public Bitmap getPreview(final Object o) { - final String name = getObjectName(o); - final String packageName = getObjectPackage(o); - // check if the package is valid - synchronized(sInvalidPackages) { - boolean packageValid = !sInvalidPackages.contains(packageName); - if (!packageValid) { - return null; - } - } - synchronized(mLoadedPreviews) { - // check if it exists in our existing cache - if (mLoadedPreviews.containsKey(name)) { - WeakReference bitmapReference = mLoadedPreviews.get(name); - Bitmap bitmap = bitmapReference.get(); - if (bitmap != null) { - return bitmap; - } - } - } - - Bitmap unusedBitmap = null; - synchronized(mUnusedBitmaps) { - // not in cache; we need to load it from the db - while (unusedBitmap == null && mUnusedBitmaps.size() > 0) { - Bitmap candidate = mUnusedBitmaps.remove(0).get(); - if (candidate != null && candidate.isMutable() && - candidate.getWidth() == mPreviewBitmapWidth && - candidate.getHeight() == mPreviewBitmapHeight) { - unusedBitmap = candidate; - } - } - } - - if (unusedBitmap == null) { - unusedBitmap = Bitmap.createBitmap(mPreviewBitmapWidth, mPreviewBitmapHeight, - Bitmap.Config.ARGB_8888); - } - Bitmap preview = readFromDb(name, unusedBitmap); - - if (preview != null) { - synchronized(mLoadedPreviews) { - mLoadedPreviews.put(name, new WeakReference(preview)); - } - return preview; - } else { - // it's not in the db... we need to generate it - final Bitmap generatedPreview = generatePreview(o, unusedBitmap); - preview = generatedPreview; - if (preview != unusedBitmap) { - throw new RuntimeException("generatePreview is not recycling the bitmap " + o); - } - - synchronized(mLoadedPreviews) { - mLoadedPreviews.put(name, new WeakReference(preview)); - } - - // write to db on a thread pool... this can be done lazily and improves the performance - // of the first time widget previews are loaded - new AsyncTask() { - public Void doInBackground(Void ... args) { - writeToDb(o, generatedPreview); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); - - return preview; - } - } + /** + * Generates the widget preview on {@link AsyncTask#THREAD_POOL_EXECUTOR}. Must be + * called on UI thread + * + * @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo} + * @param immediateResult A bitmap array of size 1. If the result is already cached, it is + * set to the final result. + * @return a request id which can be used to cancel the request. + */ + public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight, + PagedViewWidget caller, Bitmap[] immediateResult) { + String size = previewWidth + "x" + previewHeight; + WidgetCacheKey key = getObjectKey(o, size); - public void recycleBitmap(Object o, Bitmap bitmapToRecycle) { - String name = getObjectName(o); + // Check if we have the preview loaded or not. synchronized (mLoadedPreviews) { - if (mLoadedPreviews.containsKey(name)) { - Bitmap b = mLoadedPreviews.get(name).get(); - if (b == bitmapToRecycle) { - mLoadedPreviews.remove(name); - if (bitmapToRecycle.isMutable()) { - synchronized (mUnusedBitmaps) { - mUnusedBitmaps.add(new SoftReference(b)); - } - } - } else { - throw new RuntimeException("Bitmap passed in doesn't match up"); - } + WeakReference ref = mLoadedPreviews.get(key); + if (ref != null && ref.get() != null) { + immediateResult[0] = ref.get(); + return new PreviewLoadRequest(null, key); } } + + PreviewLoadTask task = new PreviewLoadTask(key, o, previewWidth, previewHeight, caller); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return new PreviewLoadRequest(task, key); } - static class CacheDb extends SQLiteOpenHelper { - final static int DB_VERSION = 2; - final static String TABLE_NAME = "shortcut_and_widget_previews"; - final static String COLUMN_NAME = "name"; - final static String COLUMN_SIZE = "size"; - final static String COLUMN_PREVIEW_BITMAP = "preview_bitmap"; - Context mContext; + /** + * The DB holds the generated previews for various components. Previews can also have different + * sizes (landscape vs portrait). + */ + private static class CacheDb extends SQLiteOpenHelper { + private static final int DB_VERSION = 3; + + private static final String TABLE_NAME = "shortcut_and_widget_previews"; + private static final String COLUMN_COMPONENT = "componentName"; + private static final String COLUMN_USER = "profileId"; + private static final String COLUMN_SIZE = "size"; + private static final String COLUMN_PACKAGE = "packageName"; + private static final String COLUMN_LAST_UPDATED = "lastUpdated"; + private static final String COLUMN_VERSION = "version"; + private static final String COLUMN_PREVIEW_BITMAP = "preview_bitmap"; public CacheDb(Context context) { - super(context, new File(context.getCacheDir(), - LauncherFiles.WIDGET_PREVIEWS_DB).getPath(), null, DB_VERSION); - // Store the context for later use - mContext = context; + super(context, LauncherFiles.WIDGET_PREVIEWS_DB, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase database) { database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + - COLUMN_NAME + " TEXT NOT NULL, " + + COLUMN_COMPONENT + " TEXT NOT NULL, " + + COLUMN_USER + " INTEGER NOT NULL, " + COLUMN_SIZE + " TEXT NOT NULL, " + - COLUMN_PREVIEW_BITMAP + " BLOB NOT NULL, " + - "PRIMARY KEY (" + COLUMN_NAME + ", " + COLUMN_SIZE + ") " + + COLUMN_PACKAGE + " TEXT NOT NULL, " + + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_PREVIEW_BITMAP + " BLOB, " + + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ", " + COLUMN_SIZE + ") " + ");"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion != newVersion) { - // Delete all the records; they'll be repopulated as this is a cache - db.execSQL("DELETE FROM " + TABLE_NAME); + clearDB(db); } } - } - private static final String WIDGET_PREFIX = "Widget:"; - private static final String SHORTCUT_PREFIX = "Shortcut:"; + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } - private static String getObjectName(Object o) { - // should cache the string builder - StringBuilder sb = new StringBuilder(); - String output; - if (o instanceof AppWidgetProviderInfo) { - sb.append(WIDGET_PREFIX); - sb.append(((AppWidgetProviderInfo) o).toString()); - output = sb.toString(); - sb.setLength(0); - } else { - sb.append(SHORTCUT_PREFIX); - ResolveInfo info = (ResolveInfo) o; - sb.append(new ComponentName(info.activityInfo.packageName, - info.activityInfo.name).flattenToString()); - output = sb.toString(); - sb.setLength(0); + private void clearDB(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(db); } - return output; } - private String getObjectPackage(Object o) { - if (o instanceof AppWidgetProviderInfo) { - return ((AppWidgetProviderInfo) o).provider.getPackageName(); + private WidgetCacheKey getObjectKey(Object o, String size) { + // should cache the string builder + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) o; + return new WidgetCacheKey(info.provider, mManager.getUser(info), size); } else { ResolveInfo info = (ResolveInfo) o; - return info.activityInfo.packageName; + return new WidgetCacheKey( + new ComponentName(info.activityInfo.packageName, info.activityInfo.name), + UserHandleCompat.myUserHandle(), size); } } - @Thunk void writeToDb(Object o, Bitmap preview) { - String name = getObjectName(o); - SQLiteDatabase db = mDb.getWritableDatabase(); + @Thunk void writeToDb(WidgetCacheKey key, long[] versions, Bitmap preview) { ContentValues values = new ContentValues(); + values.put(CacheDb.COLUMN_COMPONENT, key.componentName.flattenToShortString()); + values.put(CacheDb.COLUMN_USER, mUserManager.getSerialNumberForUser(key.user)); + values.put(CacheDb.COLUMN_SIZE, key.size); + values.put(CacheDb.COLUMN_PACKAGE, key.componentName.getPackageName()); + values.put(CacheDb.COLUMN_VERSION, versions[0]); + values.put(CacheDb.COLUMN_LAST_UPDATED, versions[1]); + values.put(CacheDb.COLUMN_PREVIEW_BITMAP, Utilities.flattenBitmap(preview)); - values.put(CacheDb.COLUMN_NAME, name); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - preview.compress(Bitmap.CompressFormat.PNG, 100, stream); - values.put(CacheDb.COLUMN_PREVIEW_BITMAP, stream.toByteArray()); - values.put(CacheDb.COLUMN_SIZE, mSize); try { - db.insert(CacheDb.TABLE_NAME, null, values); - } catch (SQLiteDiskIOException e) { - recreateDb(); - } catch (SQLiteCantOpenDatabaseException e) { - dumpOpenFiles(); - throw e; + mDb.getWritableDatabase().insertWithOnConflict(CacheDb.TABLE_NAME, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + } catch (SQLException e) { + Log.e(TAG, "Error saving image to DB", e); } } - private void clearDb() { - SQLiteDatabase db = mDb.getWritableDatabase(); - // Delete everything + public void removePackage(String packageName, UserHandleCompat user) { + removePackage(packageName, user, mUserManager.getSerialNumberForUser(user)); + } + + private void removePackage(String packageName, UserHandleCompat user, long userSerial) { + synchronized(mPackageVersions) { + mPackageVersions.remove(packageName); + } + + synchronized (mLoadedPreviews) { + Set keysToRemove = new HashSet<>(); + for (WidgetCacheKey key : mLoadedPreviews.keySet()) { + if (key.componentName.getPackageName().equals(packageName) && key.user.equals(user)) { + keysToRemove.add(key); + } + } + + for (WidgetCacheKey key : keysToRemove) { + WeakReference req = mLoadedPreviews.remove(key); + if (req != null && req.get() != null) { + mUnusedBitmaps.add(req.get()); + } + } + } + try { - db.delete(CacheDb.TABLE_NAME, null, null); - } catch (SQLiteDiskIOException e) { - } catch (SQLiteCantOpenDatabaseException e) { - dumpOpenFiles(); - throw e; + mDb.getWritableDatabase().delete(CacheDb.TABLE_NAME, + CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?", + new String[] {packageName, Long.toString(userSerial)}); + } catch (SQLException e) { + Log.e(TAG, "Unable to delete items from DB", e); } } - public static void removePackageFromDb(final CacheDb cacheDb, final String packageName) { - synchronized(sInvalidPackages) { - sInvalidPackages.add(packageName); + /** + * Updates the persistent DB: + * 1. Any preview generated for an old package version is removed + * 2. Any preview for an absent package is removed + * This ensures that we remove entries for packages which changed while the launcher was dead. + */ + public void removeObsoletePreviews() { + LongSparseArray userIdCache = new LongSparseArray<>(); + LongSparseArray> validPackages = new LongSparseArray<>(); + + for (Object obj : LauncherModel.getSortedWidgetsAndShortcuts(mContext, false)) { + final UserHandleCompat user; + final String pkg; + if (obj instanceof ResolveInfo) { + user = UserHandleCompat.myUserHandle(); + pkg = ((ResolveInfo) obj).activityInfo.packageName; + } else { + LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) obj; + user = mManager.getUser(info); + pkg = info.provider.getPackageName(); + } + + int userIdIndex = userIdCache.indexOfValue(user); + final long userId; + if (userIdIndex < 0) { + userId = mUserManager.getSerialNumberForUser(user); + userIdCache.put(userId, user); + } else { + userId = userIdCache.keyAt(userIdIndex); + } + + HashSet packages = validPackages.get(userId); + if (packages == null) { + packages = new HashSet<>(); + validPackages.put(userId, packages); + } + packages.add(pkg); } - new AsyncTask() { - public Void doInBackground(Void ... args) { - SQLiteDatabase db = cacheDb.getWritableDatabase(); - try { - db.delete(CacheDb.TABLE_NAME, - CacheDb.COLUMN_NAME + " LIKE ? OR " + - CacheDb.COLUMN_NAME + " LIKE ?", // SELECT query - new String[] { - WIDGET_PREFIX + packageName + "/%", - SHORTCUT_PREFIX + packageName + "/%" - } // args to SELECT query - ); - } catch (SQLiteDiskIOException e) { - } catch (SQLiteCantOpenDatabaseException e) { - dumpOpenFiles(); - throw e; + + LongSparseArray> packagesToDelete = new LongSparseArray<>(); + Cursor c = null; + try { + c = mDb.getReadableDatabase().query(CacheDb.TABLE_NAME, + new String[] {CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE, + CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION}, + null, null, null, null, null); + while (c.moveToNext()) { + long userId = c.getLong(0); + String pkg = c.getString(1); + long lastUpdated = c.getLong(2); + long version = c.getLong(3); + + HashSet packages = validPackages.get(userId); + if (packages != null && packages.contains(pkg)) { + long[] versions = getPackageVersion(pkg); + if (versions[0] == version && versions[1] == lastUpdated) { + // Every thing checks out + continue; + } } - synchronized(sInvalidPackages) { - sInvalidPackages.remove(packageName); + + // We need to delete this package. + packages = packagesToDelete.get(userId); + if (packages == null) { + packages = new HashSet<>(); + packagesToDelete.put(userId, packages); } - return null; + packages.add(pkg); } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); - } - private static void removeItemFromDb(final CacheDb cacheDb, final String objectName) { - new AsyncTask() { - public Void doInBackground(Void ... args) { - SQLiteDatabase db = cacheDb.getWritableDatabase(); - try { - db.delete(CacheDb.TABLE_NAME, - CacheDb.COLUMN_NAME + " = ? ", // SELECT query - new String[] { objectName }); // args to SELECT query - } catch (SQLiteDiskIOException e) { - } catch (SQLiteCantOpenDatabaseException e) { - dumpOpenFiles(); - throw e; + for (int i = 0; i < packagesToDelete.size(); i++) { + long userId = packagesToDelete.keyAt(i); + UserHandleCompat user = mUserManager.getUserForSerialNumber(userId); + for (String pkg : packagesToDelete.valueAt(i)) { + removePackage(pkg, user, userId); } - return null; } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + } catch (SQLException e) { + Log.e(TAG, "Error updatating widget previews", e); + } finally { + if (c != null) { + c.close(); + } + } } - private Bitmap readFromDb(String name, Bitmap b) { - if (mCachedSelectQuery == null) { - mCachedSelectQuery = CacheDb.COLUMN_NAME + " = ? AND " + - CacheDb.COLUMN_SIZE + " = ?"; - } - SQLiteDatabase db = mDb.getReadableDatabase(); - Cursor result; + private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle) { + Cursor cursor = null; try { - result = db.query(CacheDb.TABLE_NAME, - new String[] { CacheDb.COLUMN_PREVIEW_BITMAP }, // cols to return - mCachedSelectQuery, // select query - new String[] { name, mSize }, // args to select query - null, - null, - null, - null); - } catch (SQLiteDiskIOException e) { - recreateDb(); - return null; - } catch (SQLiteCantOpenDatabaseException e) { - dumpOpenFiles(); - throw e; - } - if (result.getCount() > 0) { - result.moveToFirst(); - byte[] blob = result.getBlob(0); - result.close(); - final BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inBitmap = b; - opts.inSampleSize = 1; - try { - return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts); - } catch (IllegalArgumentException e) { - removeItemFromDb(mDb, name); - return null; + cursor = mDb.getReadableDatabase().query( + CacheDb.TABLE_NAME, + new String[] { CacheDb.COLUMN_PREVIEW_BITMAP }, + CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND " + CacheDb.COLUMN_SIZE + " = ?", + new String[] { + key.componentName.flattenToString(), + Long.toString(mUserManager.getSerialNumberForUser(key.user)), + key.size + }, + null, null, null); + if (cursor.moveToNext()) { + byte[] blob = cursor.getBlob(0); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inBitmap = recycle; + try { + return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts); + } catch (Exception e) { + return null; + } + } + } catch (SQLException e) { + Log.w(TAG, "Error loading preview from DB", e); + } finally { + if (cursor != null) { + cursor.close(); } - } else { - result.close(); - return null; } + return null; } - private Bitmap generatePreview(Object info, Bitmap preview) { - if (preview != null && - (preview.getWidth() != mPreviewBitmapWidth || - preview.getHeight() != mPreviewBitmapHeight)) { - throw new RuntimeException("Improperly sized bitmap passed as argument"); - } + private Bitmap generatePreview(Object info, Bitmap recycle, int previewWidth, int previewHeight) { if (info instanceof LauncherAppWidgetProviderInfo) { - return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, preview); + return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, previewWidth, recycle); } else { return generateShortcutPreview( - (ResolveInfo) info, mPreviewBitmapWidth, mPreviewBitmapHeight, preview); + (ResolveInfo) info, previewWidth, previewHeight, recycle); } } - public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, Bitmap preview) { - int maxWidth = maxWidthForWidgetPreview(info.spanX); + public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, + int previewWidth, Bitmap preview) { + int maxWidth = Math.min(previewWidth, info.spanX + * LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().cellWidthPx); return generateWidgetPreview(info, maxWidth, preview, null); } - public int maxWidthForWidgetPreview(int spanX) { - return Math.min(mPreviewBitmapWidth, spanX * mCellWidth); - } - public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) { // Load the preview image if possible @@ -488,6 +418,8 @@ public class WidgetPreviewLoader { } else { final Paint p = new Paint(); p.setFilterBitmap(true); + int appIconSize = LauncherAppState.getInstance().getDynamicGrid() + .getDeviceProfile().iconSizePx; // draw the spanX x spanY tiles final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight()); @@ -507,18 +439,18 @@ public class WidgetPreviewLoader { // Draw the icon in the top left corner // TODO: use top right for RTL - int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE); + int minOffset = (int) (appIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE); int smallestSide = Math.min(previewWidth, previewHeight); - float iconScale = Math.min((float) smallestSide / (mAppIconSize + 2 * minOffset), scale); + float iconScale = Math.min((float) smallestSide / (appIconSize + 2 * minOffset), scale); try { Drawable icon = mutateOnMainThread(mManager.loadIcon(info, mIconCache)); if (icon != null) { - int hoffset = (int) ((tileW - mAppIconSize * iconScale) / 2) + x; - int yoffset = (int) ((tileH - mAppIconSize * iconScale) / 2); + int hoffset = (int) ((tileW - appIconSize * iconScale) / 2) + x; + int yoffset = (int) ((tileH - appIconSize * iconScale) / 2); icon.setBounds(hoffset, yoffset, - hoffset + (int) (mAppIconSize * iconScale), - yoffset + (int) (mAppIconSize * iconScale)); + hoffset + (int) (appIconSize * iconScale), + yoffset + (int) (appIconSize * iconScale)); icon.draw(c); } } catch (Resources.NotFoundException e) { } @@ -561,9 +493,11 @@ public class WidgetPreviewLoader { // Draw the final icon at top left corner. // TODO: use top right for RTL + int appIconSize = LauncherAppState.getInstance().getDynamicGrid() + .getDeviceProfile().iconSizePx; icon.setAlpha(255); icon.setColorFilter(null); - icon.setBounds(0, 0, mAppIconSize, mAppIconSize); + icon.setBounds(0, 0, appIconSize, appIconSize); icon.draw(c); c.setBitmap(null); @@ -586,82 +520,144 @@ public class WidgetPreviewLoader { } } - private static final int MAX_OPEN_FILES = 1024; - private static final int SAMPLE_RATE = 23; /** - * Dumps all files that are open in this process without allocating a file descriptor. + * @return an array of containing versionCode and lastUpdatedTime for the package. */ - @Thunk static void dumpOpenFiles() { - try { - Log.i(TAG, "DUMP OF OPEN FILES (sample rate: 1 every " + SAMPLE_RATE + "):"); - final String TYPE_APK = "apk"; - final String TYPE_JAR = "jar"; - final String TYPE_PIPE = "pipe"; - final String TYPE_SOCKET = "socket"; - final String TYPE_DB = "db"; - final String TYPE_ANON_INODE = "anon_inode"; - final String TYPE_DEV = "dev"; - final String TYPE_NON_FS = "non-fs"; - final String TYPE_OTHER = "other"; - List types = Arrays.asList(TYPE_APK, TYPE_JAR, TYPE_PIPE, TYPE_SOCKET, TYPE_DB, - TYPE_ANON_INODE, TYPE_DEV, TYPE_NON_FS, TYPE_OTHER); - int[] count = new int[types.size()]; - int[] duplicates = new int[types.size()]; - HashSet files = new HashSet(); - int total = 0; - for (int i = 0; i < MAX_OPEN_FILES; i++) { - // This is a gigantic hack but unfortunately the only way to resolve an fd - // to a file name. Note that we have to loop over all possible fds because - // reading the directory would require allocating a new fd. The kernel is - // currently implemented such that no fd is larger then the current rlimit, - // which is why it's safe to loop over them in such a way. - String fd = "/proc/self/fd/" + i; + private long[] getPackageVersion(String packageName) { + synchronized (mPackageVersions) { + long[] versions = mPackageVersions.get(packageName); + if (versions == null) { + versions = new long[2]; try { - // getCanonicalPath() uses readlink behind the scene which doesn't require - // a file descriptor. - String resolved = new File(fd).getCanonicalPath(); - int type = types.indexOf(TYPE_OTHER); - if (resolved.startsWith("/dev/")) { - type = types.indexOf(TYPE_DEV); - } else if (resolved.endsWith(".apk")) { - type = types.indexOf(TYPE_APK); - } else if (resolved.endsWith(".jar")) { - type = types.indexOf(TYPE_JAR); - } else if (resolved.contains("/fd/pipe:")) { - type = types.indexOf(TYPE_PIPE); - } else if (resolved.contains("/fd/socket:")) { - type = types.indexOf(TYPE_SOCKET); - } else if (resolved.contains("/fd/anon_inode:")) { - type = types.indexOf(TYPE_ANON_INODE); - } else if (resolved.endsWith(".db") || resolved.contains("/databases/")) { - type = types.indexOf(TYPE_DB); - } else if (resolved.startsWith("/proc/") && resolved.contains("/fd/")) { - // Those are the files that don't point anywhere on the file system. - // getCanonicalPath() wrongly interprets these as relative symlinks and - // resolves them within /proc//fd/. - type = types.indexOf(TYPE_NON_FS); - } - count[type]++; - total++; - if (files.contains(resolved)) { - duplicates[type]++; + PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0); + versions[0] = info.versionCode; + versions[1] = info.lastUpdateTime; + } catch (NameNotFoundException e) { + Log.e(TAG, "PackageInfo not found", e); + } + mPackageVersions.put(packageName, versions); + } + return versions; + } + } + + /** + * A request Id which can be used by the client to cancel any request. + */ + public class PreviewLoadRequest { + + private final PreviewLoadTask mTask; + private final WidgetCacheKey mKey; + + public PreviewLoadRequest(PreviewLoadTask task, WidgetCacheKey key) { + mTask = task; + mKey = key; + } + + public void cancel(boolean recycleImage) { + if (mTask != null) { + mTask.cancel(true); + } + + if (recycleImage) { + synchronized(mLoadedPreviews) { + WeakReference result = mLoadedPreviews.remove(mKey); + if (result != null && result.get() != null) { + mUnusedBitmaps.add(result.get()); } - files.add(resolved); - if (total % SAMPLE_RATE == 0) { - Log.i(TAG, " fd " + i + ": " + resolved - + " (" + types.get(type) + ")"); + } + } + } + } + + public class PreviewLoadTask extends AsyncTask { + + private final WidgetCacheKey mKey; + private final Object mInfo; + private final int mPreviewHeight; + private final int mPreviewWidth; + private final PagedViewWidget mCaller; + + PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, + int previewHeight, PagedViewWidget caller) { + mKey = key; + mInfo = info; + mPreviewHeight = previewHeight; + mPreviewWidth = previewWidth; + mCaller = caller; + } + + + @Override + protected Bitmap doInBackground(Void... params) { + Bitmap unusedBitmap = null; + synchronized (mUnusedBitmaps) { + // Check if we can use a bitmap + for (Bitmap candidate : mUnusedBitmaps) { + if (candidate != null && candidate.isMutable() && + candidate.getWidth() == mPreviewWidth && + candidate.getHeight() == mPreviewHeight) { + unusedBitmap = candidate; + break; } - } catch (IOException e) { - // Ignoring exceptions for non-existing file descriptors. + } + + if (unusedBitmap == null) { + unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888); + } else { + mUnusedBitmaps.remove(unusedBitmap); + } + } + + if (isCancelled()) { + return null; + } + Bitmap preview = readFromDb(mKey, unusedBitmap); + if (!isCancelled() && preview == null) { + // Fetch the version info before we generate the preview, so that, in-case the + // app was updated while we are generating the preview, we use the old version info, + // which would gets re-written next time. + long[] versions = getPackageVersion(mKey.componentName.getPackageName()); + + // it's not in the db... we need to generate it + preview = generatePreview(mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight); + + if (!isCancelled()) { + writeToDb(mKey, versions, preview); } } - for (int i = 0; i < types.size(); i++) { - Log.i(TAG, String.format("Open %10s files: %4d total, %4d duplicates", - types.get(i), count[i], duplicates[i])); + + return preview; + } + + @Override + protected void onPostExecute(Bitmap result) { + synchronized(mLoadedPreviews) { + mLoadedPreviews.put(mKey, new WeakReference(result)); } - } catch (Throwable t) { - // Catch everything. This is called from an exception handler that we shouldn't upset. - Log.e(TAG, "Unable to log open files.", t); + + mCaller.applyPreview(result); + } + } + + private static final class WidgetCacheKey extends ComponentKey { + + // TODO: remove dependency on size + private final String size; + + public WidgetCacheKey(ComponentName componentName, UserHandleCompat user, String size) { + super(componentName, user); + this.size = size; + } + + @Override + public int hashCode() { + return super.hashCode() ^ size.hashCode(); + } + + @Override + public boolean equals(Object o) { + return super.equals(o) && ((WidgetCacheKey) o).size.equals(size); } } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 37265fe47..c274e4d4e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -366,13 +366,12 @@ public class Workspace extends SmoothPagedView // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each // dimension if unsuccessful - public int[] estimateItemSize(int hSpan, int vSpan, - ItemInfo itemInfo, boolean springLoaded) { + public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) { int[] size = new int[2]; if (getChildCount() > 0) { // Use the first non-custom page to estimate the child position CellLayout cl = (CellLayout) getChildAt(numCustomPages()); - Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan); + Rect r = estimateItemPosition(cl, itemInfo, 0, 0, itemInfo.spanX, itemInfo.spanY); size[0] = r.width(); size[1] = r.height(); if (springLoaded) { @@ -2065,7 +2064,7 @@ public class Workspace extends SmoothPagedView } public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) { - int[] size = estimateItemSize(info.spanX, info.spanY, info, false); + int[] size = estimateItemSize(info, false); // The outline is used to visualize where the item will land if dropped mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha); @@ -4032,8 +4031,7 @@ public class Workspace extends SmoothPagedView } public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) { - int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX, - widgetInfo.spanY, widgetInfo, false); + int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo, false); int visibility = layout.getVisibility(); layout.setVisibility(VISIBLE); -- cgit v1.2.3 From e85d7145222ba1311cd9280726d8419b84f2f94b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 20 Mar 2015 14:48:18 -0700 Subject: Fixing getItemCount in FolderPagedView > When a folder is created, it initially has no item, and then two shortcut info are added to it. Initial logic assumed that the folder it created with on item already present Bug: 19868273 Change-Id: I8850b71d2bace835f94e3cd791c173d40752d2e6 --- src/com/android/launcher3/FolderPagedView.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 9f3126c27..6a13a1385 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -511,13 +511,13 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { @Override public int getItemCount() { - int lastPage = getChildCount() - 1; - if (lastPage < 0) { - // If there are no pages, there must be only one icon in the folder. - return 1; + int lastPageIndex = getChildCount() - 1; + if (lastPageIndex < 0) { + // If there are no pages, nothing has yet been added to the folder. + return 0; } - return getPageAt(lastPage).getShortcutsAndWidgets().getChildCount() - + lastPage * mMaxItemsPerPage; + return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount() + + lastPageIndex * mMaxItemsPerPage; } @Override -- cgit v1.2.3 From 6a1e95a3dfb5eb65cc2c682c5ede9e22cc7ff7d9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 20 Mar 2015 17:26:30 -0700 Subject: Removing all dependencies of launcher code on wallpaper-picker > WallpaperPicker can still depend on launcher code, but not the other way round. Change-Id: I93a07f985c3955c638a68a5fad9c09b184a879b9 --- AndroidManifest.xml | 2 +- .../com/android/gallery3d/common/BitmapUtils.java | 94 ---------------- .../launcher3/LauncherWallpaperPickerActivity.java | 21 ++++ .../android/launcher3/WallpaperCropActivity.java | 46 ++------ .../android/launcher3/WallpaperPickerActivity.java | 12 +- src/com/android/launcher3/Launcher.java | 12 +- src/com/android/launcher3/LauncherFiles.java | 3 +- .../launcher3/LauncherWallpaperPickerActivity.java | 30 ----- src/com/android/launcher3/Workspace.java | 5 +- src/com/android/launcher3/util/WallpaperUtils.java | 123 +++++++++++++++++++++ 10 files changed, 168 insertions(+), 180 deletions(-) create mode 100644 WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java delete mode 100644 src/com/android/launcher3/LauncherWallpaperPickerActivity.java create mode 100644 src/com/android/launcher3/util/WallpaperUtils.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fb7ac3fab..82ee8f2bd 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -128,7 +128,7 @@ = android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { - Point realSize = new Point(); - windowManager.getDefaultDisplay().getRealSize(realSize); - maxDim = Math.max(realSize.x, realSize.y); - minDim = Math.min(realSize.x, realSize.y); - } - - // We need to ensure that there is enough extra space in the wallpaper - // for the intended parallax effects - final int defaultWidth, defaultHeight; - if (res.getConfiguration().smallestScreenWidthDp >= 720) { - defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); - defaultHeight = maxDim; - } else { - defaultWidth = Math.max((int) (minDim * WallpaperCropActivity.WALLPAPER_SCREENS_SPAN), maxDim); - defaultHeight = maxDim; - } - sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight); - } - return sDefaultWallpaperSize; - } - public static int getRotationFromExif(Context context, Uri uri) { return BitmapUtils.getRotationFromExifHelper(null, 0, context, uri); } diff --git a/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java new file mode 100644 index 000000000..091c05462 --- /dev/null +++ b/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 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; + +// TODO: Remove this class +public class LauncherWallpaperPickerActivity extends WallpaperPickerActivity { +} \ No newline at end of file diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index 5b2943a65..c238390e3 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -38,13 +38,13 @@ import android.os.Message; import android.util.Log; import android.view.Display; import android.view.View; -import android.view.WindowManager; import android.widget.Toast; import com.android.gallery3d.common.BitmapCropTask; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.WallpaperUtils; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider; @@ -57,8 +57,8 @@ import java.util.WeakHashMap; public class WallpaperCropActivity extends Activity implements Handler.Callback { private static final String LOGTAG = "Launcher3.CropActivity"; - protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; - protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; + protected static final String WALLPAPER_WIDTH_KEY = WallpaperUtils.WALLPAPER_WIDTH_KEY; + protected static final String WALLPAPER_HEIGHT_KEY = WallpaperUtils.WALLPAPER_HEIGHT_KEY; /** * The maximum bitmap size we allow to be returned through the intent. @@ -68,7 +68,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback * array instead of a Bitmap instance to avoid overhead. */ public static final int MAX_BMAP_IN_INTENT = 750000; - public static final float WALLPAPER_SCREENS_SPAN = 2f; + public static final float WALLPAPER_SCREENS_SPAN = WallpaperUtils.WALLPAPER_SCREENS_SPAN; private static final int MSG_LOAD_IMAGE = 1; @@ -290,10 +290,6 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback return getResources().getBoolean(R.bool.allow_rotation); } - public static String getSharedPreferencesKey() { - return LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; - } - protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) { int rotation = BitmapUtils.getRotationFromExif(this, uri); BitmapCropTask cropTask = new BitmapCropTask( @@ -319,7 +315,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback // this device int rotation = BitmapUtils.getRotationFromExif(res, resId); Point inSize = mCropView.getSourceDimensions(); - Point outSize = BitmapUtils.getDefaultWallpaperSize(getResources(), + Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), getWindowManager()); RectF crop = Utils.getMaxCropRect( inSize.x, inSize.y, outSize.x, outSize.y, false); @@ -352,7 +348,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback d.getSize(displaySize); boolean isPortrait = displaySize.x < displaySize.y; - Point defaultWallpaperSize = BitmapUtils.getDefaultWallpaperSize(getResources(), + Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), getWindowManager()); // Get the crop RectF cropRect = mCropView.getCrop(); @@ -435,7 +431,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } protected void updateWallpaperDimensions(int width, int height) { - String spKey = getSharedPreferencesKey(); + String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); SharedPreferences.Editor editor = sp.edit(); if (width != 0 && height != 0) { @@ -446,36 +442,10 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback editor.remove(WALLPAPER_HEIGHT_KEY); } editor.commit(); - - suggestWallpaperDimension(getResources(), + WallpaperUtils.suggestWallpaperDimension(getResources(), sp, getWindowManager(), WallpaperManager.getInstance(this), true); } - public static void suggestWallpaperDimension(Resources res, - final SharedPreferences sharedPrefs, - WindowManager windowManager, - final WallpaperManager wallpaperManager, boolean fallBackToDefaults) { - final Point defaultWallpaperSize = BitmapUtils.getDefaultWallpaperSize(res, windowManager); - // If we have saved a wallpaper width/height, use that instead - - int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1); - int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1); - - if (savedWidth == -1 || savedHeight == -1) { - if (!fallBackToDefaults) { - return; - } else { - savedWidth = defaultWallpaperSize.x; - savedHeight = defaultWallpaperSize.y; - } - } - - if (savedWidth != wallpaperManager.getDesiredMinimumWidth() || - savedHeight != wallpaperManager.getDesiredMinimumHeight()) { - wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); - } - } - static class LoadRequest { BitmapSource src; boolean touchEnabled; diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 1364df3ed..152cac4cc 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -71,6 +71,7 @@ import com.android.gallery3d.common.BitmapCropTask; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.WallpaperUtils; import com.android.photos.BitmapRegionTileSource; import com.android.photos.BitmapRegionTileSource.BitmapSource; import com.android.photos.views.TiledImageRenderer.TileSource; @@ -239,7 +240,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public float getScale(TileSource src) { - Point wallpaperSize = BitmapUtils.getDefaultWallpaperSize( + Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize( a.getResources(), a.getWindowManager()); RectF crop = Utils.getMaxCropRect( src.getImageWidth(), src.getImageHeight(), @@ -1109,9 +1110,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return view; } - // In Launcher3, we override this with a method that catches exceptions - // from starting activities; didn't want to copy and paste code into here public void startActivityForResultSafely(Intent intent, int requestCode) { - startActivityForResult(intent, requestCode); + Utilities.startActivityForResultSafely(this, intent, requestCode); + } + + @Override + public boolean enableRotation() { + return Utilities.isRotationEnabled(this); } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index f2610d6e4..58c4cf7c2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2409,13 +2409,6 @@ public class Launcher extends Activity sFolders.remove(folder.id); } - protected ComponentName getWallpaperPickerComponent() { - if (mLauncherCallbacks != null) { - return mLauncherCallbacks.getWallpaperPickerComponent(); - } - return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName()); - } - /** * Registers various content observers. The current implementation registers * only a favorites observer to keep track of the favorites applications. @@ -2780,9 +2773,8 @@ public class Launcher extends Activity */ protected void onClickWallpaperPicker(View v) { if (LOGD) Log.d(TAG, "onClickWallpaperPicker"); - final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); - pickWallpaper.setComponent(getWallpaperPickerComponent()); - startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER); + startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName()), + REQUEST_PICK_WALLPAPER); if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickWallpaperPicker(v); diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index ce277c1c8..699cb37ff 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -20,7 +20,8 @@ public class LauncherFiles { public static final String LAUNCHER_PREFERENCES = "launcher.preferences"; public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; public static final String WALLPAPER_CROP_PREFERENCES_KEY = - WallpaperCropActivity.class.getName(); + "com.android.launcher3.WallpaperCropActivity"; + public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db"; public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; public static final String APP_ICONS_DB = "app_icons.db"; diff --git a/src/com/android/launcher3/LauncherWallpaperPickerActivity.java b/src/com/android/launcher3/LauncherWallpaperPickerActivity.java deleted file mode 100644 index 10fe013ee..000000000 --- a/src/com/android/launcher3/LauncherWallpaperPickerActivity.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2013 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.Intent; - -public class LauncherWallpaperPickerActivity extends WallpaperPickerActivity { - @Override - public void startActivityForResultSafely(Intent intent, int requestCode) { - Utilities.startActivityForResultSafely(this, intent, requestCode); - } - @Override - public boolean enableRotation() { - return Utilities.isRotationEnabled(this); - } -} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 37265fe47..7c12d4c6c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -70,6 +70,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.WallpaperUtils; import java.util.ArrayList; import java.util.HashMap; @@ -1315,10 +1316,10 @@ public class Workspace extends SmoothPagedView protected void setWallpaperDimension() { new AsyncTask() { public Void doInBackground(Void ... args) { - String spKey = WallpaperCropActivity.getSharedPreferencesKey(); + String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); - LauncherWallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(), + WallpaperUtils.suggestWallpaperDimension(mLauncher.getResources(), sp, mLauncher.getWindowManager(), mWallpaperManager, mLauncher.overrideWallpaperDimensions()); return null; diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java new file mode 100644 index 000000000..53b2acd84 --- /dev/null +++ b/src/com/android/launcher3/util/WallpaperUtils.java @@ -0,0 +1,123 @@ +/* + * 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.util; + +import android.annotation.TargetApi; +import android.app.WallpaperManager; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.Point; +import android.os.Build; +import android.view.WindowManager; + +/** + * Utility methods for wallpaper management. + */ +public final class WallpaperUtils { + + public static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; + public static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; + public static final float WALLPAPER_SCREENS_SPAN = 2f; + + public static void suggestWallpaperDimension(Resources res, + final SharedPreferences sharedPrefs, + WindowManager windowManager, + final WallpaperManager wallpaperManager, boolean fallBackToDefaults) { + final Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(res, windowManager); + // If we have saved a wallpaper width/height, use that instead + + int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1); + int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1); + + if (savedWidth == -1 || savedHeight == -1) { + if (!fallBackToDefaults) { + return; + } else { + savedWidth = defaultWallpaperSize.x; + savedHeight = defaultWallpaperSize.y; + } + } + + if (savedWidth != wallpaperManager.getDesiredMinimumWidth() || + savedHeight != wallpaperManager.getDesiredMinimumHeight()) { + wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); + } + } + + /** + * As a ratio of screen height, the total distance we want the parallax effect to span + * horizontally + */ + public static float wallpaperTravelToScreenWidthRatio(int width, int height) { + float aspectRatio = width / (float) height; + + // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width + // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width + // We will use these two data points to extrapolate how much the wallpaper parallax effect + // to span (ie travel) at any aspect ratio: + + final float ASPECT_RATIO_LANDSCAPE = 16/10f; + final float ASPECT_RATIO_PORTRAIT = 10/16f; + final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; + final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; + + // To find out the desired width at different aspect ratios, we use the following two + // formulas, where the coefficient on x is the aspect ratio (width/height): + // (16/10)x + y = 1.5 + // (10/16)x + y = 1.2 + // We solve for x and y and end up with a final formula: + final float x = + (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / + (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); + final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; + return x * aspectRatio + y; + } + + private static Point sDefaultWallpaperSize; + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { + if (sDefaultWallpaperSize == null) { + Point minDims = new Point(); + Point maxDims = new Point(); + windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); + + int maxDim = Math.max(maxDims.x, maxDims.y); + int minDim = Math.max(minDims.x, minDims.y); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + Point realSize = new Point(); + windowManager.getDefaultDisplay().getRealSize(realSize); + maxDim = Math.max(realSize.x, realSize.y); + minDim = Math.min(realSize.x, realSize.y); + } + + // We need to ensure that there is enough extra space in the wallpaper + // for the intended parallax effects + final int defaultWidth, defaultHeight; + if (res.getConfiguration().smallestScreenWidthDp >= 720) { + defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); + defaultHeight = maxDim; + } else { + defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); + defaultHeight = maxDim; + } + sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight); + } + return sDefaultWallpaperSize; + } +} -- cgit v1.2.3 From 2670980ab7e5c575e3beef3f220331256fd66c3e Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Sat, 21 Mar 2015 09:18:40 -0700 Subject: Remove dublicate BubbleTextView styleable Change-Id: Ib1db985515194dfeebe2dfa934b29023ae0a3075 --- res/values/attrs.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 845b18230..a1f28452a 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -25,6 +25,7 @@ + @@ -89,11 +90,6 @@ - - - - - -- cgit v1.2.3 From 1d0b093a6e384f5ade340c2b404b324bb6002c97 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 20 Mar 2015 18:51:38 -0700 Subject: Adding BaseActivity which can be overriden for custom behavior Change-Id: I6707aa99f5d33dceede226a6367e24482f5157f6 --- .../launcher3/LiveWallpaperListAdapter.java | 1 - .../android/launcher3/SavedWallpaperImages.java | 5 +- .../ThirdPartyWallpaperPickerListAdapter.java | 5 +- .../android/launcher3/WallpaperCropActivity.java | 26 +++---- .../android/launcher3/WallpaperPickerActivity.java | 81 ++++++++++++---------- .../com/android/launcher3/base/BaseActivity.java | 21 ++++++ 6 files changed, 80 insertions(+), 59 deletions(-) create mode 100644 WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java diff --git a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java index a5e951ce4..b53fce119 100644 --- a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java @@ -30,7 +30,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.TextView; diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java index 9f92bc105..64b0ac466 100644 --- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java +++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -60,13 +59,13 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { } } - public SavedWallpaperImages(Activity context) { + public SavedWallpaperImages(Context context) { // We used to store the saved images in the cache directory, but that meant they'd get // deleted sometimes-- move them to the data directory ImageDb.moveFromCacheDirectoryIfNecessary(context); mDb = new ImageDb(context); mContext = context; - mLayoutInflater = context.getLayoutInflater(); + mLayoutInflater = LayoutInflater.from(context); } public void loadThumbnailsAndImageIdList() { diff --git a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java index 16bde3300..f46da53ec 100644 --- a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java +++ b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java @@ -28,7 +28,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.FrameLayout; import android.widget.ListAdapter; import android.widget.TextView; @@ -38,8 +37,6 @@ import java.util.ArrayList; import java.util.List; public class ThirdPartyWallpaperPickerListAdapter extends BaseAdapter implements ListAdapter { - private static final String LOG_TAG = "LiveWallpaperListAdapter"; - private final LayoutInflater mInflater; private final PackageManager mPackageManager; private final int mIconSize; @@ -64,7 +61,7 @@ public class ThirdPartyWallpaperPickerListAdapter extends BaseAdapter implements } public ThirdPartyWallpaperPickerListAdapter(Context context) { - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mInflater = LayoutInflater.from(context); mPackageManager = context.getPackageManager(); mIconSize = context.getResources().getDimensionPixelSize(R.dimen.wallpaperItemIconSize); final PackageManager pm = mPackageManager; diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index c238390e3..142a9cb10 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -43,6 +43,7 @@ import android.widget.Toast; import com.android.gallery3d.common.BitmapCropTask; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; +import com.android.launcher3.base.BaseActivity; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; import com.android.photos.BitmapRegionTileSource; @@ -54,7 +55,7 @@ import java.util.Collections; import java.util.Set; import java.util.WeakHashMap; -public class WallpaperCropActivity extends Activity implements Handler.Callback { +public class WallpaperCropActivity extends BaseActivity implements Handler.Callback { private static final String LOGTAG = "Launcher3.CropActivity"; protected static final String WALLPAPER_WIDTH_KEY = WallpaperUtils.WALLPAPER_WIDTH_KEY; @@ -86,7 +87,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback Collections.newSetFromMap(new WeakHashMap()); @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLoaderThread = new HandlerThread("wallpaper_loader"); @@ -130,13 +131,12 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback // Load image in background final BitmapRegionTileSource.UriBitmapSource bitmapSource = - new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024); + new BitmapRegionTileSource.UriBitmapSource(getContext(), imageUri, 1024); mSetWallpaperButton.setEnabled(false); Runnable onLoad = new Runnable() { public void run() { if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { - Toast.makeText(WallpaperCropActivity.this, - getString(R.string.wallpaper_load_fail), + Toast.makeText(getContext(), R.string.wallpaper_load_fail, Toast.LENGTH_LONG).show(); finish(); } else { @@ -148,7 +148,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } @Override - protected void onDestroy() { + public void onDestroy() { if (mCropView != null) { mCropView.destroy(); } @@ -203,7 +203,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } } - req.result = new BitmapRegionTileSource(this, req.src, mTempStorageForDecoding); + req.result = new BitmapRegionTileSource(getContext(), req.src, mTempStorageForDecoding); runOnUiThread(new Runnable() { @Override @@ -291,9 +291,9 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) { - int rotation = BitmapUtils.getRotationFromExif(this, uri); + int rotation = BitmapUtils.getRotationFromExif(getContext(), uri); BitmapCropTask cropTask = new BitmapCropTask( - this, uri, null, rotation, 0, 0, true, false, null); + getContext(), uri, null, rotation, 0, 0, true, false, null); final Point bounds = cropTask.getImageBounds(); Runnable onEndCrop = new Runnable() { public void run() { @@ -330,7 +330,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } } }; - BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, + BitmapCropTask cropTask = new BitmapCropTask(getContext(), res, resId, crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); cropTask.execute(); } @@ -422,7 +422,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } } }; - BitmapCropTask cropTask = new BitmapCropTask(this, uri, + BitmapCropTask cropTask = new BitmapCropTask(getContext(), uri, cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); if (onBitmapCroppedHandler != null) { cropTask.setOnBitmapCropped(onBitmapCroppedHandler); @@ -432,7 +432,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback protected void updateWallpaperDimensions(int width, int height) { String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; - SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); + SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); SharedPreferences.Editor editor = sp.edit(); if (width != 0 && height != 0) { editor.putInt(WALLPAPER_WIDTH_KEY, width); @@ -443,7 +443,7 @@ public class WallpaperCropActivity extends Activity implements Handler.Callback } editor.commit(); WallpaperUtils.suggestWallpaperDimension(getResources(), - sp, getWindowManager(), WallpaperManager.getInstance(this), true); + sp, getWindowManager(), WallpaperManager.getInstance(getContext()), true); } static class LoadRequest { diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 152cac4cc..c49286a75 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -21,6 +21,7 @@ import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; import android.app.WallpaperManager; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -159,8 +160,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { ViewGroup parent = (ViewGroup) mView.getParent(); if (parent != null) { parent.removeView(mView); - Toast.makeText(a, - a.getString(R.string.image_load_fail), + Toast.makeText(a.getContext(), R.string.image_load_fail, Toast.LENGTH_SHORT).show(); } } @@ -168,7 +168,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { }; } mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( - a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); + a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad); } @Override @@ -205,7 +205,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(WallpaperPickerActivity a) { BitmapRegionTileSource.UriBitmapSource bitmapSource = - new BitmapRegionTileSource.UriBitmapSource(a, Uri.fromFile(mFile), 1024); + new BitmapRegionTileSource.UriBitmapSource(a.getContext(), + Uri.fromFile(mFile), 1024); a.setCropViewTileSource(bitmapSource, false, true, null, null); } @Override @@ -272,8 +273,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(WallpaperPickerActivity a) { CropView c = a.getCropView(); - Drawable defaultWallpaper = WallpaperManager.getInstance(a).getBuiltInDrawable( - c.getWidth(), c.getHeight(), false, 0.5f, 0.5f); + Drawable defaultWallpaper = WallpaperManager.getInstance(a.getContext()) + .getBuiltInDrawable(c.getWidth(), c.getHeight(), false, 0.5f, 0.5f); if (defaultWallpaper == null) { Log.w(TAG, "Null default wallpaper encountered."); c.setTileSource(null, null); @@ -290,14 +291,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return 1f; } }; - req.result = new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE); + req.result = new DrawableTileSource(a.getContext(), + defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE); a.onLoadRequestComplete(req, true); } @Override public void onSave(WallpaperPickerActivity a) { try { - WallpaperManager.getInstance(a).clear(); - a.setResult(RESULT_OK); + WallpaperManager.getInstance(a.getContext()).clear(); + a.setResult(Activity.RESULT_OK); } catch (IOException e) { Log.w("Setting wallpaper to default threw exception", e); } @@ -449,18 +451,18 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { // Populate the built-in wallpapers ArrayList wallpapers = findBundledWallpapers(); mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list); - SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(this, wallpapers); + SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(getContext(), wallpapers); populateWallpapersFromAdapter(mWallpapersView, ia, false); // Populate the saved wallpapers - mSavedImages = new SavedWallpaperImages(this); + mSavedImages = new SavedWallpaperImages(getContext()); mSavedImages.loadThumbnailsAndImageIdList(); populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true); // Populate the live wallpapers final LinearLayout liveWallpapersView = (LinearLayout) findViewById(R.id.live_wallpaper_list); - final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(this); + final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(getContext()); a.registerDataSetObserver(new DataSetObserver() { public void onChanged() { liveWallpapersView.removeAllViews(); @@ -474,7 +476,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { final LinearLayout thirdPartyWallpapersView = (LinearLayout) findViewById(R.id.third_party_wallpaper_list); final ThirdPartyWallpaperPickerListAdapter ta = - new ThirdPartyWallpaperPickerListAdapter(this); + new ThirdPartyWallpaperPickerListAdapter(getContext()); populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false); // Add a tile for the Gallery @@ -648,7 +650,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { // TODO: Remove this once the accessibility framework and // services have better support for selection state. v.announceForAccessibility( - getString(R.string.announce_selection, v.getContentDescription())); + getContext().getString(R.string.announce_selection, v.getContentDescription())); } @Thunk void initializeScrollForRtl() { @@ -666,7 +668,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } protected Bitmap getThumbnailOfLastPhoto() { - Cursor cursor = MediaStore.Images.Media.query(getContentResolver(), + Cursor cursor = MediaStore.Images.Media.query(getContext().getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATE_TAKEN}, @@ -676,7 +678,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (cursor != null) { if (cursor.moveToNext()) { int id = cursor.getInt(0); - thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), + thumb = MediaStore.Images.Thumbnails.getThumbnail(getContext().getContentResolver(), id, MediaStore.Images.Thumbnails.MINI_KIND, null); } cursor.close(); @@ -684,7 +686,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return thumb; } - protected void onStop() { + public void onStop() { super.onStop(); mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); if (mWallpaperScrollContainer.getAlpha() < 1f) { @@ -693,7 +695,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - protected void onSaveInstanceState(Bundle outState) { + public void onSaveInstanceState(Bundle outState) { outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles); outState.putInt(SELECTED_INDEX, mSelectedIndex); } @@ -818,7 +820,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { // Load the thumbnail final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image); final Point defaultSize = getDefaultThumbnailSize(this.getResources()); - final Context context = this; + final Context context = getContext(); new AsyncTask() { protected Bitmap doInBackground(Void...args) { try { @@ -862,15 +864,16 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) { if (data != null && data.getData() != null) { Uri uri = data.getData(); addTemporaryWallpaperTile(uri, false); } - } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY && resultCode == RESULT_OK) { + } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY + && resultCode == Activity.RESULT_OK) { // Something was set on the third-party activity. - setResult(RESULT_OK); + setResult(Activity.RESULT_OK); finish(); } } @@ -880,7 +883,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } private ArrayList findBundledWallpapers() { - final PackageManager pm = getPackageManager(); + final PackageManager pm = getContext().getPackageManager(); final ArrayList bundled = new ArrayList(24); Partner partner = Partner.get(pm); @@ -924,7 +927,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { Pair r = getWallpaperArrayResourceId(); if (r != null) { try { - Resources wallpaperRes = getPackageManager().getResourcesForApplication(r.first); + Resources wallpaperRes = getContext().getPackageManager() + .getResourcesForApplication(r.first); addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second); } catch (PackageManager.NameNotFoundException e) { } @@ -947,7 +951,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { try { f.createNewFile(); FileOutputStream thumbFileStream = - openFileOutput(f.getName(), Context.MODE_PRIVATE); + getContext().openFileOutput(f.getName(), Context.MODE_PRIVATE); b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream); thumbFileStream.close(); return true; @@ -959,17 +963,18 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } private File getDefaultThumbFile() { - return new File(getFilesDir(), Build.VERSION.SDK_INT + return new File(getContext().getFilesDir(), Build.VERSION.SDK_INT + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL); } private boolean saveDefaultWallpaperThumb(Bitmap b) { // Delete old thumbnails. - new File(getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete(); - new File(getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete(); + new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete(); + new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete(); for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) { - new File(getFilesDir(), i + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete(); + new File(getContext().getFilesDir(), i + "_" + + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete(); } return writeImageToFileAsJpeg(getDefaultThumbFile(), b); } @@ -989,7 +994,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { Point defaultThumbSize = getDefaultThumbnailSize(res); int rotation = BitmapUtils.getRotationFromExif(res, resId); thumb = createThumbnail( - defaultThumbSize, this, null, null, sysRes, resId, rotation, false); + defaultThumbSize, getContext(), null, null, sysRes, resId, rotation, false); if (thumb != null) { defaultWallpaperExists = saveDefaultWallpaperThumb(thumb); } @@ -1011,7 +1016,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } else { Resources res = getResources(); Point defaultThumbSize = getDefaultThumbnailSize(res); - Drawable wallpaperDrawable = WallpaperManager.getInstance(this).getBuiltInDrawable( + Drawable wallpaperDrawable = WallpaperManager.getInstance(getContext()).getBuiltInDrawable( defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f); if (wallpaperDrawable != null) { thumb = Bitmap.createBitmap( @@ -1038,7 +1043,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { // package name should be. final String packageName = getResources().getResourcePackageName(R.array.wallpapers); try { - ApplicationInfo info = getPackageManager().getApplicationInfo(packageName, 0); + ApplicationInfo info = getContext().getPackageManager().getApplicationInfo(packageName, 0); return new Pair(info, R.array.wallpapers); } catch (PackageManager.NameNotFoundException e) { return null; @@ -1076,9 +1081,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { private static class SimpleWallpapersAdapter extends ArrayAdapter { private final LayoutInflater mLayoutInflater; - SimpleWallpapersAdapter(Activity activity, ArrayList wallpapers) { - super(activity, R.layout.wallpaper_picker_item, wallpapers); - mLayoutInflater = activity.getLayoutInflater(); + SimpleWallpapersAdapter(Context context, ArrayList wallpapers) { + super(context, R.layout.wallpaper_picker_item, wallpapers); + mLayoutInflater = LayoutInflater.from(context); } public View getView(int position, View convertView, ViewGroup parent) { @@ -1111,11 +1116,11 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } public void startActivityForResultSafely(Intent intent, int requestCode) { - Utilities.startActivityForResultSafely(this, intent, requestCode); + Utilities.startActivityForResultSafely(getActivity(), intent, requestCode); } @Override public boolean enableRotation() { - return Utilities.isRotationEnabled(this); + return Utilities.isRotationEnabled(getContext()); } } diff --git a/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java b/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java new file mode 100644 index 000000000..f8541188f --- /dev/null +++ b/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java @@ -0,0 +1,21 @@ +package com.android.launcher3.base; + +import android.app.Activity; +import android.content.Context; + +/** + * A wrapper over {@link Activity} which allows to override some methods. + * The base implementation can change from an Activity to a Fragment (or any other custom + * implementation), Callers should not assume that the base class extends Context, instead use + * either {@link #getContext} or {@link #getActivity} + */ +public class BaseActivity extends Activity { + + public Context getContext() { + return this; + } + + public Activity getActivity() { + return this; + } +} -- cgit v1.2.3 From f819dc2bc782e93ac9ecc163a99af0da62821d31 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 23 Mar 2015 14:45:54 -0700 Subject: Making the scrollbar scrubbable. - This change in behavior will remove the ability to scrub anywhere in the gutter to start fast-scrolling, but requires the user to touch near the scroll bar to start fast-scrolling. - Also fixes issue with wonky scrollbar due to the fake section breaks Change-Id: I34b08ac46ea93f7f4cad7ccde1048a388ee6a55d --- res/drawable-ldrtl/apps_list_fastscroll_bg.xml | 27 +++ res/drawable/apps_list_scrollbar_thumb.xml | 2 +- res/layout/apps_list_view.xml | 5 +- res/values/dimens.xml | 5 +- .../launcher3/AppsContainerRecyclerView.java | 227 +++++++++++++++++---- src/com/android/launcher3/AppsContainerView.java | 10 +- 6 files changed, 221 insertions(+), 55 deletions(-) create mode 100644 res/drawable-ldrtl/apps_list_fastscroll_bg.xml diff --git a/res/drawable-ldrtl/apps_list_fastscroll_bg.xml b/res/drawable-ldrtl/apps_list_fastscroll_bg.xml new file mode 100644 index 000000000..772975a71 --- /dev/null +++ b/res/drawable-ldrtl/apps_list_fastscroll_bg.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable/apps_list_scrollbar_thumb.xml b/res/drawable/apps_list_scrollbar_thumb.xml index 59383a5bb..318d40678 100644 --- a/res/drawable/apps_list_scrollbar_thumb.xml +++ b/res/drawable/apps_list_scrollbar_thumb.xml @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index 59c04103f..3e42f8489 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -36,7 +36,7 @@ android:textSize="16sp" android:textColor="#4c4c4c" android:textColorHint="#9c9c9c" - android:imeOptions="flagNoExtractUi" + android:imeOptions="actionDone|flagNoExtractUi" android:background="@drawable/apps_list_search_bg" android:elevation="4dp" /> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b9b9a2412..c327ec2d4 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -52,8 +52,9 @@ 52dp 64dp 24sp - 6dp - 40dp + 6dp + 64dp + -16dp 64dp 40dp diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index c5a508c9c..4d6b9d412 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; import java.util.List; @@ -42,7 +43,9 @@ public class AppsContainerRecyclerView extends RecyclerView private AlphabeticalAppsList mApps; private int mNumAppsPerRow; + private Drawable mScrollbar; private Drawable mFastScrollerBg; + private Rect mVerticalScrollbarBounds = new Rect(); private boolean mDraggingFastScroller; private String mFastScrollSectionName; private Paint mFastScrollTextPaint; @@ -52,7 +55,9 @@ public class AppsContainerRecyclerView extends RecyclerView private int mDownY; private int mLastX; private int mLastY; - private int mGutterSize; + private int mScrollbarWidth; + private int mScrollbarMinHeight; + private int mScrollbarInset; public AppsContainerRecyclerView(Context context) { this(context, null); @@ -72,14 +77,19 @@ public class AppsContainerRecyclerView extends RecyclerView Resources res = context.getResources(); int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size); - mFastScrollerBg = res.getDrawable(R.drawable.apps_list_fastscroll_bg); + mScrollbar = context.getDrawable(R.drawable.apps_list_scrollbar_thumb); + mFastScrollerBg = context.getDrawable(R.drawable.apps_list_fastscroll_bg); mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); mFastScrollTextPaint = new Paint(); mFastScrollTextPaint.setColor(Color.WHITE); mFastScrollTextPaint.setAntiAlias(true); mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.apps_view_fast_scroll_text_size)); - mGutterSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_gutter_size); + mScrollbarWidth = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_bar_width); + mScrollbarMinHeight = + res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_bar_min_height); + mScrollbarInset = + res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(getFastScrollerAlpha()); } @@ -112,6 +122,13 @@ public class AppsContainerRecyclerView extends RecyclerView return mFastScrollAlpha; } + /** + * Returns the scroll bar width. + */ + public int getScrollbarWidth() { + return mScrollbarWidth; + } + @Override protected void onFinishInflate() { addOnItemTouchListener(this); @@ -120,38 +137,13 @@ public class AppsContainerRecyclerView extends RecyclerView @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - - if (mFastScrollAlpha > 0f) { - boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == - LAYOUT_DIRECTION_RTL); - Rect bgBounds = mFastScrollerBg.getBounds(); - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - int x; - if (isRtl) { - x = getPaddingLeft() + getScrollBarSize(); - } else { - x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); - } - int y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); - y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - - bgBounds.height())); - canvas.translate(x, y); - mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollerBg.draw(canvas); - mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, - mFastScrollSectionName.length(), mFastScrollTextBounds); - canvas.drawText(mFastScrollSectionName, - (bgBounds.width() - mFastScrollTextBounds.width()) / 2, - bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2, - mFastScrollTextPaint); - canvas.restoreToCount(restoreCount); - } + drawVerticalScrubber(canvas); + drawFastScrollerPopup(canvas); } /** * We intercept the touch handling only to support fast scrolling when initiated from the - * gutter. Otherwise, we fall back to the default RecyclerView touch handling. + * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. */ @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { @@ -182,15 +174,7 @@ public class AppsContainerRecyclerView extends RecyclerView break; case MotionEvent.ACTION_MOVE: // Check if we are scrolling - boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == - LAYOUT_DIRECTION_RTL); - boolean isInGutter; - if (isRtl) { - isInGutter = mDownX < mGutterSize; - } else { - isInGutter = mDownX >= (getWidth() - mGutterSize); - } - if (!mDraggingFastScroller && isInGutter && + if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && Math.abs(y - mDownY) > config.getScaledTouchSlop()) { getParent().requestDisallowInterceptTouchEvent(true); mDraggingFastScroller = true; @@ -229,6 +213,67 @@ public class AppsContainerRecyclerView extends RecyclerView anim.start(); } + /** + * Returns whether a given point is near the scrollbar. + */ + private boolean isPointNearScrollbar(int x, int y) { + // Check if we are scrolling + updateVerticalScrollbarBounds(); + mVerticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); + return mVerticalScrollbarBounds.contains(x, y); + } + + /** + * Draws the fast scroller popup. + */ + private void drawFastScrollerPopup(Canvas canvas) { + int x; + int y; + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + + if (mFastScrollAlpha > 0f) { + // Calculate the position for the fast scroller popup + Rect bgBounds = mFastScrollerBg.getBounds(); + if (isRtl) { + x = getPaddingLeft() + getScrollBarSize(); + } else { + x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); + } + y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); + y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - + bgBounds.height())); + + // Draw the fast scroller popup + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(x, y); + mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollerBg.draw(canvas); + mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, + mFastScrollSectionName.length(), mFastScrollTextBounds); + canvas.drawText(mFastScrollSectionName, + (bgBounds.width() - mFastScrollTextBounds.width()) / 2, + bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2, + mFastScrollTextPaint); + canvas.restoreToCount(restoreCount); + } + } + + /** + * Draws the vertical scrollbar. + */ + private void drawVerticalScrubber(Canvas canvas) { + updateVerticalScrollbarBounds(); + + // Draw the scroll bar + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(mVerticalScrollbarBounds.left, mVerticalScrollbarBounds.top); + mScrollbar.setBounds(0, 0, mScrollbarWidth, mVerticalScrollbarBounds.height()); + mScrollbar.draw(canvas); + canvas.restoreToCount(restoreCount); + } + /** * Invalidates the fast scroller popup. */ @@ -243,11 +288,7 @@ public class AppsContainerRecyclerView extends RecyclerView private String scrollToPositionAtProgress(float progress) { List sections = mApps.getSections(); // Get the total number of rows - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); - rowCount += numRowsInSection; - } + int rowCount = getNumRows(); // Find the index of the first app in that row and scroll to that position int rowAtProgress = (int) (progress * rowCount); @@ -270,4 +311,100 @@ public class AppsContainerRecyclerView extends RecyclerView // Returns the section name of the row return mApps.getSectionNameForApp(appInfo); } + + /** + * Returns the bounds for the scrollbar. + */ + private void updateVerticalScrollbarBounds() { + int x; + int y; + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + + // Skip early if there are no items + if (mApps.getApps().isEmpty()) { + mVerticalScrollbarBounds.setEmpty(); + return; + } + + // Find the index and height of the first visible row (all rows have the same height) + int rowIndex = -1; + int rowTopOffset = -1; + int rowHeight = -1; + int rowCount = getNumRows(); + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + int position = getChildPosition(child); + if (position != NO_POSITION) { + AppInfo info = mApps.getApps().get(position); + if (info != AlphabeticalAppsList.SECTION_BREAK_INFO) { + int appIndex = mApps.getAppsWithoutSectionBreaks().indexOf(info); + rowIndex = findRowForAppIndex(appIndex); + rowTopOffset = getLayoutManager().getDecoratedTop(child); + rowHeight = child.getHeight(); + break; + } + } + } + + if (rowIndex != -1) { + int height = getHeight() - getPaddingTop() - getPaddingBottom(); + int totalScrollHeight = rowCount * rowHeight; + if (totalScrollHeight > height) { + int scrollbarHeight = Math.max(mScrollbarMinHeight, + (int) (height / ((float) totalScrollHeight / height))); + + // Calculate the position and size of the scroll bar + if (isRtl) { + x = getPaddingLeft(); + } else { + x = getWidth() - getPaddingRight() - mScrollbarWidth; + } + + // To calculate the offset, we compute the percentage of the total scrollable height + // that the user has already scrolled and then map that to the scroll bar bounds + int availableY = totalScrollHeight - height; + int availableScrollY = height - scrollbarHeight; + y = (rowIndex * rowHeight) - rowTopOffset; + y = getPaddingTop() + + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); + + mVerticalScrollbarBounds.set(x, y, x + mScrollbarWidth, y + scrollbarHeight); + return; + } + } + mVerticalScrollbarBounds.setEmpty(); + } + + /** + * Returns the row index for a given position in the list. + */ + private int findRowForAppIndex(int position) { + List sections = mApps.getSections(); + int appIndex = 0; + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); + if (appIndex + info.numAppsInSection > position) { + return rowCount + ((position - appIndex) / mNumAppsPerRow); + } + appIndex += info.numAppsInSection; + rowCount += numRowsInSection; + } + return appIndex; + } + + /** + * Returns the total number of rows in the list. + */ + private int getNumRows() { + List sections = mApps.getSections(); + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); + rowCount += numRowsInSection; + } + return rowCount; + } } diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index ce092bfe4..a4688f0c9 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -166,12 +166,16 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mAppsListView.setAdapter(mAdapter); mAppsListView.setHasFixedSize(true); if (isRtl) { - mAppsListView.setPadding(mAppsListView.getPaddingLeft(), mAppsListView.getPaddingTop(), + mAppsListView.setPadding( + mAppsListView.getPaddingLeft(), + mAppsListView.getPaddingTop(), mAppsListView.getPaddingRight() + mContentMarginStart, mAppsListView.getPaddingBottom()); } else { - mAppsListView.setPadding(mAppsListView.getPaddingLeft() + mContentMarginStart, - mAppsListView.getPaddingTop(), mAppsListView.getPaddingRight(), + mAppsListView.setPadding( + mAppsListView.getPaddingLeft() + mContentMarginStart, + mAppsListView.getPaddingTop(), + mAppsListView.getPaddingRight(), mAppsListView.getPaddingBottom()); } if (mItemDecoration != null) { -- cgit v1.2.3 From 68052094f502ac551abba37b43e700c339ec8dd0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 24 Mar 2015 14:38:33 -0700 Subject: Removing unused strings with no default translations > Removes build system gives warning when Launcher3 is build Change-Id: I40e5df7538d244b3ec3f2b722bf2606e622011f2 --- res/values-be/strings.xml | 4 ---- res/values-et/strings.xml | 20 -------------------- res/values-ms/strings.xml | 20 -------------------- 3 files changed, 44 deletions(-) diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 2015158d9..2d3c633c3 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -199,10 +199,6 @@ - - - - diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 3b0deb85b..a4955ceda 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -23,13 +23,11 @@ "Kodu" "Androidi tuumrakendused" - "Taustapildi valimiskoht:" "Määra taustapilt" "Taustapildid" "Rakendus pole installitud." "Vidinad" "Vidina valimiseks puudutage seda pikalt." - "Pood" "%1$d × %2$d" "Üksust ei saa sellele avaekraanile tuua." "Valige loomiseks vidin" @@ -52,23 +50,13 @@ "Rakenduse valimine" "Rakendused" "Kodu" - "Eemalda" "Desinstalli" "Eemalda" "Desinstalli" "Rakenduse teave" - "Otsing" - "Häälotsing" "Rakendused" "Eemalda" "Desinstalli värskendus" - "Lisa" - "Rakenduste haldamine" - "Taustapilt" - "Otsing" - "Teadistused" - "Süsteemiseaded" - "Abi" "Rakenduse desinstallimine" "Rakenduse üksikasjad" "Valitud on 1 rakendus" @@ -94,11 +82,7 @@ "Vidinate leht %1$d/%2$d" "Tunne end nagu kodus" "Võite panna oma lemmikrakendused siia." - "Kõikide oma rakenduste nägemiseks puudutage ringi." - "Valige mõned rakendused" - "Rakenduse lisamiseks avakuvale puudutage seda pikalt." "Korraldage oma rakendused kaustadesse" - "Rakenduse liigutamiseks pange sõrm rakendusele ja hoidke seda." "Avakuval uue kausta tegemiseks virnastage üks rakendus teisele." "OK" "Kaust on avatud, %1$d x %2$d" @@ -107,8 +91,4 @@ "Kaust suletud" "Kausta uus nimi: %1$s" "%1$s" - - - - diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index f50a5db5b..248ca1517 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -23,13 +23,11 @@ "Laman Utama" "Apl Teras Android" - "Pilih kertas dinding dari" "Tetapkan kertas dinding" "Kertas dinding" "Aplikasi tidak dipasang." "Widget" "Sentuh & tahan untuk mengambil widget." - "Kedai" "%1$d × %2$d" "Tidak dapat melepaskan item pada skrin Utama ini." "Pilih widget untuk dibuat" @@ -52,23 +50,13 @@ "Pilih aplikasi" "Apl" "Laman Utama" - "Alih keluar" "Nyahpasang" "Alih keluar" "Nyahpasang" "Maklumat apl" - "Carian" - "Carian Suara" "Aplikasi" "Alih keluar" "Nyahpasang kemas kini" - "Tambah" - "Mengurus apl" - "Kertas dinding" - "Cari" - "Pemberitahuan" - "Tetapan sistem" - "Bantuan" "Nyahpasang aplikasi" "Butiran aplikasi" "1 aplikasi dipilih" @@ -94,11 +82,7 @@ "Halaman widget %1$d dari %2$d" "Buat diri anda seperti di rumah" "Anda boleh meletakkan aplikasi kegemaran anda di sini." - "Untuk melihat semua aplikasi anda, sentuh bulatan." - "Pilih beberapa aplikasi" - "Untuk menambahkan aplikasi pada skrin Utama anda, sentuh & tahankannya." "Susun aplikasi anda dengan folder" - "Untuk memindahkan aplikasi, sentuh & tahankannya." "Untuk membuat folder baharu pada skrin Utama anda, tindihkan satu aplikasi di atas yang lain." "OK" "Folder dibuka, %1$d kali %2$d" @@ -107,8 +91,4 @@ "Folder ditutup" "Folder dinamakan semula kepada %1$s" "Folder: %1$s" - - - - -- cgit v1.2.3 From 3879194799d20ce1bd4ca98432dff20f72f634b0 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 24 Mar 2015 15:23:51 -0700 Subject: Check the code point instead of the character when determining digit sections. Change-Id: Ie21fda92ca5097cc9deb7c59001b61dc44603d4b --- src/com/android/launcher3/compat/AlphabeticIndexCompat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java index 47e1b7a98..f890706ff 100644 --- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -105,7 +105,7 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { public String computeSectionName(String s) { String sectionName = getBucketLabel(getBucketIndex(s)); if (sectionName.trim().isEmpty() && s.length() > 0) { - boolean startsWithDigit = Character.isDigit(s.charAt(0)); + boolean startsWithDigit = Character.isDigit(Character.codePointAt(s.trim(), 0)); if (startsWithDigit) { // Digit section return "#"; -- cgit v1.2.3 From d0930655e5670bb40ec70523c1c76ff86fab2538 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 24 Mar 2015 16:28:44 -0700 Subject: Ensure that we are not using the new View constructor. Change-Id: Iac2dba6d42cd60d6260aee9afc590564d25919a5 --- src/com/android/launcher3/AppsContainerView.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index e7b6628cc..06fe93c1f 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -72,11 +72,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett } public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); + super(context, attrs, defStyleAttr); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); Resources res = context.getResources(); -- cgit v1.2.3 From 3d9490ab9520ffe3798539ed1626463a7eb6c9e1 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 24 Mar 2015 17:38:42 -0700 Subject: Removing more code with API 21 dependencies. Change-Id: I16c914334ce0694b84626269ae4bb5e83082c739 --- .../launcher3/AppsContainerRecyclerView.java | 4 +-- src/com/android/launcher3/CellLayout.java | 35 +++++++++++++++++----- src/com/android/launcher3/Launcher.java | 4 ++- .../launcher3/LauncherAppWidgetProviderInfo.java | 2 -- .../LauncherStateTransitionAnimation.java | 6 ++-- .../android/launcher3/WidgetsContainerView.java | 6 +--- src/com/android/launcher3/Workspace.java | 4 ++- 7 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 4d6b9d412..0cc651417 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -77,8 +77,8 @@ public class AppsContainerRecyclerView extends RecyclerView Resources res = context.getResources(); int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size); - mScrollbar = context.getDrawable(R.drawable.apps_list_scrollbar_thumb); - mFastScrollerBg = context.getDrawable(R.drawable.apps_list_fastscroll_bg); + mScrollbar = res.getDrawable(R.drawable.apps_list_scrollbar_thumb); + mFastScrollerBg = res.getDrawable(R.drawable.apps_list_fastscroll_bg); mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); mFastScrollTextPaint = new Paint(); mFastScrollTextPaint.setColor(Color.WHITE); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index eb2aa547d..63afa3091 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -417,10 +417,13 @@ public class CellLayout extends ViewGroup { protected int intersectsValidDropTarget(int id) { LauncherAccessibilityDelegate delegate = LauncherAppState.getInstance().getAccessibilityDelegate(); - LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); + if (delegate == null) { + return -1; + } int y = id % mCountY; int x = id / mCountY; + LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); if (dragInfo.dragType == DragType.WIDGET) { // For a widget, every cell must be vacant. In addition, we will return any valid @@ -489,10 +492,15 @@ public class CellLayout extends ViewGroup { @Override protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + if (delegate == null) { + return false; + } + if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { String confirmation = getConfirmationForIconDrop(viewId); - LauncherAppState.getInstance().getAccessibilityDelegate() - .handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); + delegate.handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); return true; } return false; @@ -500,11 +508,16 @@ public class CellLayout extends ViewGroup { @Override public void onClick(View arg0) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + if (delegate == null) { + return; + } + int viewId = getViewIdAt(mDownX, mDownY); String confirmation = getConfirmationForIconDrop(viewId); - LauncherAppState.getInstance().getAccessibilityDelegate() - .handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); + delegate.handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); } @Override @@ -533,10 +546,13 @@ public class CellLayout extends ViewGroup { private String getLocationDescriptionForIconDrop(int id) { LauncherAccessibilityDelegate delegate = LauncherAppState.getInstance().getAccessibilityDelegate(); - LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); + if (delegate == null) { + return ""; + } int y = id % mCountY; int x = id / mCountY; + LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); Resources res = getContext().getResources(); View child = getChildAt(x, y); @@ -555,11 +571,14 @@ public class CellLayout extends ViewGroup { private String getConfirmationForIconDrop(int id) { LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); + LauncherAppState.getInstance().getAccessibilityDelegate(); + if (delegate == null) { + return ""; + } int y = id % mCountY; int x = id / mCountY; + LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); Resources res = getContext().getResources(); View child = getChildAt(x, y); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 58c4cf7c2..4c5bba9e3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2448,7 +2448,9 @@ public class Launcher extends Activity return; } - if (LauncherAppState.getInstance().getAccessibilityDelegate().onBackPressed()) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + if (delegate != null && delegate.onBackPressed()) { return; } diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index e7f49b2ce..aeef0daeb 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -7,8 +7,6 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Parcel; -import java.lang.reflect.Field; - /** * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords * a common object for describing both framework provided AppWidgets as well as custom widgets diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 4a0aaf3f4..eacf3415e 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -186,7 +186,8 @@ public class LauncherStateTransitionAnimation { View allAppsButtonView) { // Hide the real page background, and swap in the fake one ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false); - revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); + revealView.setBackground( + mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark)); } @Override public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { @@ -508,7 +509,8 @@ public class LauncherStateTransitionAnimation { // Hide the real page background, and swap in the fake one pagedView.stopScrolling(); pagedView.setPageBackgroundsVisible(false); - revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); + revealView.setBackground( + mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark)); // Hide the side pages of the Widget tray to avoid some ugly edge cases final View currentPage = pagedView.getPageAt(pagedView.getNextPage()); diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java index d0dd733a6..7004d8b29 100644 --- a/src/com/android/launcher3/WidgetsContainerView.java +++ b/src/com/android/launcher3/WidgetsContainerView.java @@ -75,11 +75,7 @@ public class WidgetsContainerView extends FrameLayout { } public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); + super(context, attrs, defStyleAttr); } @Override diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 7d6f59b45..92e01324f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -576,7 +576,9 @@ public class Workspace extends SmoothPagedView mScreenOrder.add(insertIndex, screenId); addView(newScreen, insertIndex); - if (LauncherAppState.getInstance().getAccessibilityDelegate().isInAccessibleDrag()) { + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + if (delegate != null && delegate.isInAccessibleDrag()) { newScreen.enableAccessibleDrag(true); } return screenId; -- cgit v1.2.3 From 2f0ec85acf035c28b799e31d860b7785266ab1f3 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Mar 2015 13:38:27 -0700 Subject: Disabling overscroll when folder has only one page Change-Id: I3cc3964dba1af8d96822e46baf2dc9cfaee824ae --- src/com/android/launcher3/FolderPagedView.java | 2 ++ src/com/android/launcher3/PagedView.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 6a13a1385..216e68dcc 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -485,10 +485,12 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { mPageIndicator.setVisibility(View.VISIBLE); mSortButton.setVisibility(View.VISIBLE); mFolder.mFolderName.setGravity(Gravity.START); + setEnableOverscroll(true); } else { mPageIndicator.setVisibility(View.GONE); mSortButton.setVisibility(View.GONE); mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL); + setEnableOverscroll(false); } } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index e7049e2ac..158a30c40 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1702,7 +1702,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setEnableOverscroll(!freeScroll); } - private void setEnableOverscroll(boolean enable) { + protected void setEnableOverscroll(boolean enable) { mAllowOverScroll = enable; } -- cgit v1.2.3 From b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Mar 2015 11:32:11 -0700 Subject: Configuration of workspace through app restrictions > Checking for a grid specific layout before loading the default layout Change-Id: I1e8d4176341e61d0876b0a9bea9ad8010e3a0f6a --- src/com/android/launcher3/AutoInstallsLayout.java | 38 ++++++++++++++-- src/com/android/launcher3/LauncherProvider.java | 53 ++++++++++++++++++++--- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index cbab08bec..9180dcf42 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -45,6 +45,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.Locale; /** * Layout parsing code for auto installs layout @@ -57,6 +58,11 @@ public class AutoInstallsLayout { static final String ACTION_LAUNCHER_CUSTOMIZATION = "android.autoinstalls.config.action.PLAY_AUTO_INSTALL"; + /** + * Layout resource which also includes grid size and hotseat count, e.g., default_layout_6x6_h5 + */ + private static final String FORMATTED_LAYOUT_RES_WITH_HOSTEAT = "default_layout_%dx%d_h%s"; + private static final String FORMATTED_LAYOUT_RES = "default_layout_%dx%d"; private static final String LAYOUT_RES = "default_layout"; static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost, @@ -66,15 +72,39 @@ public class AutoInstallsLayout { if (customizationApkInfo == null) { return null; } + return get(context, customizationApkInfo.first, customizationApkInfo.second, + appWidgetHost, callback); + } + + static AutoInstallsLayout get(Context context, String pkg, Resources targetRes, + AppWidgetHost appWidgetHost, LayoutParserCallback callback) { + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + + // Try with grid size and hotseat count + String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT, + (int) grid.numColumns, (int) grid.numRows, (int) grid.numHotseatIcons); + int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg); + + // Try with only grid size + if (layoutId == 0) { + Log.d(TAG, "Formatted layout: " + layoutName + + " not found. Trying layout without hosteat"); + layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES, + (int) grid.numColumns, (int) grid.numRows); + layoutId = targetRes.getIdentifier(layoutName, "xml", pkg); + } + + // Try the default layout + if (layoutId == 0) { + Log.d(TAG, "Formatted layout: " + layoutName + " not found. Trying the default layout"); + layoutId = targetRes.getIdentifier(LAYOUT_RES, "xml", pkg); + } - String pkg = customizationApkInfo.first; - Resources res = customizationApkInfo.second; - int layoutId = res.getIdentifier(LAYOUT_RES, "xml", pkg); if (layoutId == 0) { Log.e(TAG, "Layout definition not found in package: " + pkg); return null; } - return new AutoInstallsLayout(context, appWidgetHost, callback, res, layoutId, + return new AutoInstallsLayout(context, appWidgetHost, callback, targetRes, layoutId, TAG_WORKSPACE); } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 6dd130505..dfacfa3e4 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.annotation.TargetApi; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.content.ComponentName; @@ -29,6 +30,7 @@ import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.Cursor; import android.database.SQLException; @@ -36,7 +38,10 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; +import android.os.Build; +import android.os.Bundle; import android.os.StrictMode; +import android.os.UserManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -70,6 +75,8 @@ public class LauncherProvider extends ContentProvider { private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; + private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name"; + private LauncherProviderChangeListener mListener; /** @@ -267,9 +274,10 @@ public class LauncherProvider extends ContentProvider { /** * Loads the default workspace based on the following priority scheme: - * 1) From a package provided by play store - * 2) From a partner configuration APK, already in the system image - * 3) The default configuration for the particular device + * 1) From the app restrictions + * 2) From a package provided by play store + * 3) From a partner configuration APK, already in the system image + * 4) The default configuration for the particular device */ synchronized public void loadDefaultFavoritesIfNecessary() { String spKey = LauncherAppState.getSharedPreferencesKey(); @@ -278,9 +286,11 @@ public class LauncherProvider extends ContentProvider { if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) { Log.d(TAG, "loading default workspace"); - AutoInstallsLayout loader = AutoInstallsLayout.get(getContext(), - mOpenHelper.mAppWidgetHost, mOpenHelper); - + AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(); + if (loader == null) { + loader = AutoInstallsLayout.get(getContext(), + mOpenHelper.mAppWidgetHost, mOpenHelper); + } if (loader == null) { final Partner partner = Partner.get(getContext().getPackageManager()); if (partner != null && partner.hasDefaultLayout()) { @@ -314,6 +324,37 @@ public class LauncherProvider extends ContentProvider { } } + /** + * Creates workspace loader from an XML resource listed in the app restrictions. + * + * @return the loader if the restrictions are set and the resource exists; null otherwise. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction() { + // UserManager.getApplicationRestrictions() requires minSdkVersion >= 18 + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + return null; + } + + Context ctx = getContext(); + UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE); + Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName()); + String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME); + + if (packageName != null) { + try { + Resources targetResources = ctx.getPackageManager() + .getResourcesForApplication(packageName); + return AutoInstallsLayout.get(ctx, packageName, targetResources, + mOpenHelper.mAppWidgetHost, mOpenHelper); + } catch (NameNotFoundException e) { + Log.e(TAG, "Target package for restricted profile not found", e); + return null; + } + } + return null; + } + private DefaultLayoutParser getDefaultLayoutParser() { int defaultLayout = LauncherAppState.getInstance() .getDynamicGrid().getDeviceProfile().defaultLayoutId; -- cgit v1.2.3 From 173f711e03dfce70d76c3b45f764a4f6b682a3b3 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 27 Mar 2015 15:14:00 -0700 Subject: Ensure that we capture all paths for starting activity for result -> AppWidgetManagerCompatVL uses an IntentSender to launch the widget config activity (to handle the case of cross profile config activities) -> Launcher was not overriding this method, and so not capturing the fact that we are waiting for a result. issue 19723510 Change-Id: If5751e6d23d31c9f7b3b85f9464a93f935487cec --- src/com/android/launcher3/Launcher.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a8fd79e47..56a74e563 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -42,6 +42,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -2079,10 +2080,26 @@ public class Launcher extends Activity @Override public void startActivityForResult(Intent intent, int requestCode) { + onStartForResult(requestCode); + super.startActivityForResult(intent, requestCode); + } + + @Override + public void startIntentSenderForResult (IntentSender intent, int requestCode, + Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { + onStartForResult(requestCode); + try { + super.startIntentSenderForResult(intent, requestCode, + fillInIntent, flagsMask, flagsValues, extraFlags, options); + } catch (IntentSender.SendIntentException e) { + throw new ActivityNotFoundException(); + } + } + + private void onStartForResult(int requestCode) { if (requestCode >= 0) { setWaitingForResult(true); } - super.startActivityForResult(intent, requestCode); } /** -- cgit v1.2.3 From b823ae4fca345e051831732ff2760023ef6ec2c4 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 27 Mar 2015 18:07:52 -0700 Subject: Avoid casting AppWidgetProviderInfo to LauncherAppWidgetProviderInfo upon unparcel issue 19904413 Change-Id: Ie1b4a2b1331f6a707f8590cd0396138a20946658 --- src/com/android/launcher3/Launcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 56a74e563..cd2bc596a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1317,7 +1317,9 @@ public class Launcher extends Activity mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); - mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO); + AppWidgetProviderInfo info = savedState.getParcelable( + RUNTIME_STATE_PENDING_ADD_WIDGET_INFO); + mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this, info); mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID); setWaitingForResult(true); mRestoring = true; -- cgit v1.2.3 From d3d37090c485359e2cf62eb6aa8e70350d161689 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 30 Mar 2015 10:15:50 -0700 Subject: Updating pageindicator assets Change-Id: I8c045ac8193aed2299e72353fd75341f8e32e250 --- res/drawable-hdpi/ic_pageindicator_add.png | Bin 945 -> 416 bytes res/drawable-hdpi/ic_pageindicator_current.png | Bin 1038 -> 684 bytes res/drawable-hdpi/ic_pageindicator_current_folder.png | Bin 0 -> 678 bytes res/drawable-hdpi/ic_pageindicator_default.png | Bin 895 -> 509 bytes res/drawable-hdpi/ic_pageindicator_default_folder.png | Bin 0 -> 501 bytes res/drawable-mdpi/ic_pageindicator_add.png | Bin 669 -> 243 bytes res/drawable-mdpi/ic_pageindicator_current.png | Bin 704 -> 421 bytes res/drawable-mdpi/ic_pageindicator_current_folder.png | Bin 0 -> 415 bytes res/drawable-mdpi/ic_pageindicator_default.png | Bin 694 -> 322 bytes res/drawable-mdpi/ic_pageindicator_default_folder.png | Bin 0 -> 315 bytes res/drawable-xhdpi/ic_pageindicator_add.png | Bin 1192 -> 588 bytes res/drawable-xhdpi/ic_pageindicator_current.png | Bin 1414 -> 922 bytes .../ic_pageindicator_current_folder.png | Bin 0 -> 896 bytes res/drawable-xhdpi/ic_pageindicator_default.png | Bin 1168 -> 682 bytes .../ic_pageindicator_default_folder.png | Bin 0 -> 654 bytes res/drawable-xxhdpi/ic_pageindicator_add.png | Bin 1607 -> 858 bytes res/drawable-xxhdpi/ic_pageindicator_current.png | Bin 2105 -> 1458 bytes res/drawable-xxhdpi/ic_pageindicator_current_dark.png | Bin 1168 -> 0 bytes .../ic_pageindicator_current_folder.png | Bin 0 -> 1416 bytes res/drawable-xxhdpi/ic_pageindicator_default.png | Bin 1634 -> 1069 bytes res/drawable-xxhdpi/ic_pageindicator_default_dark.png | Bin 927 -> 0 bytes .../ic_pageindicator_default_folder.png | Bin 0 -> 1051 bytes src/com/android/launcher3/FolderPagedView.java | 3 ++- 23 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 res/drawable-hdpi/ic_pageindicator_current_folder.png create mode 100644 res/drawable-hdpi/ic_pageindicator_default_folder.png create mode 100644 res/drawable-mdpi/ic_pageindicator_current_folder.png create mode 100644 res/drawable-mdpi/ic_pageindicator_default_folder.png create mode 100644 res/drawable-xhdpi/ic_pageindicator_current_folder.png create mode 100644 res/drawable-xhdpi/ic_pageindicator_default_folder.png delete mode 100644 res/drawable-xxhdpi/ic_pageindicator_current_dark.png create mode 100644 res/drawable-xxhdpi/ic_pageindicator_current_folder.png delete mode 100644 res/drawable-xxhdpi/ic_pageindicator_default_dark.png create mode 100644 res/drawable-xxhdpi/ic_pageindicator_default_folder.png diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png index ab0e5dbd5..971361d56 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_add.png and b/res/drawable-hdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png index 283f44d37..09405d83a 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_current.png and b/res/drawable-hdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_current_folder.png b/res/drawable-hdpi/ic_pageindicator_current_folder.png new file mode 100644 index 000000000..c3a6f699d Binary files /dev/null and b/res/drawable-hdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png index 47b998967..251de44c9 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_default.png and b/res/drawable-hdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_default_folder.png b/res/drawable-hdpi/ic_pageindicator_default_folder.png new file mode 100644 index 000000000..48a321898 Binary files /dev/null and b/res/drawable-hdpi/ic_pageindicator_default_folder.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png index 11659a3b2..50e1ba524 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_add.png and b/res/drawable-mdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png index b41e1bb67..19a89727c 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_current.png and b/res/drawable-mdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_current_folder.png b/res/drawable-mdpi/ic_pageindicator_current_folder.png new file mode 100644 index 000000000..8d6c0b1ff Binary files /dev/null and b/res/drawable-mdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png index e36c25cf1..6b0a9c9e3 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_default.png and b/res/drawable-mdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_default_folder.png b/res/drawable-mdpi/ic_pageindicator_default_folder.png new file mode 100644 index 000000000..c9edf8d88 Binary files /dev/null and b/res/drawable-mdpi/ic_pageindicator_default_folder.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png index af1da2d42..9fb5359fe 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_add.png and b/res/drawable-xhdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png index 8fa774dee..c44b88fe9 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_current.png and b/res/drawable-xhdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_current_folder.png b/res/drawable-xhdpi/ic_pageindicator_current_folder.png new file mode 100644 index 000000000..fa2168f0c Binary files /dev/null and b/res/drawable-xhdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png index 8eb5eb08d..6d1b5be6b 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_default.png and b/res/drawable-xhdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_default_folder.png b/res/drawable-xhdpi/ic_pageindicator_default_folder.png new file mode 100644 index 000000000..ff0ed3991 Binary files /dev/null and b/res/drawable-xhdpi/ic_pageindicator_default_folder.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png index c28895229..f3f6fbede 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_add.png and b/res/drawable-xxhdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png index 22b290e69..3a3a3ae29 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_current.png and b/res/drawable-xxhdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_dark.png b/res/drawable-xxhdpi/ic_pageindicator_current_dark.png deleted file mode 100644 index d5c4c8d4e..000000000 Binary files a/res/drawable-xxhdpi/ic_pageindicator_current_dark.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png new file mode 100644 index 000000000..4ff8ec6bc Binary files /dev/null and b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png index e608cae30..b057c6313 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_default.png and b/res/drawable-xxhdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_dark.png b/res/drawable-xxhdpi/ic_pageindicator_default_dark.png deleted file mode 100644 index 79d307b07..000000000 Binary files a/res/drawable-xxhdpi/ic_pageindicator_default_dark.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png new file mode 100644 index 000000000..756ad090d Binary files /dev/null and b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png differ diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 216e68dcc..61ad10db4 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -536,7 +536,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { @Override protected PageMarkerResources getPageIndicatorMarker(int pageIndex) { - return new PageMarkerResources(R.drawable.ic_pageindicator_current_dark, R.drawable.ic_pageindicator_default_dark); + return new PageMarkerResources(R.drawable.ic_pageindicator_current_folder, + R.drawable.ic_pageindicator_default_folder); } @Override -- cgit v1.2.3 From 24cf70092b8b0281df071891573642f56e34f9e5 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 30 Mar 2015 14:25:04 -0700 Subject: Ensuring that we fast-scroll to the first section app. Bug: 19992026 Change-Id: Ia015b870f80fa598fa68087f90236c59b0ad7e6d --- src/com/android/launcher3/AppsContainerRecyclerView.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 0cc651417..b942ea451 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -23,6 +23,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; @@ -290,14 +291,14 @@ public class AppsContainerRecyclerView extends RecyclerView // Get the total number of rows int rowCount = getNumRows(); - // Find the index of the first app in that row and scroll to that position + // Find the position of the first application in the section that contains the row at the + // current progress int rowAtProgress = (int) (progress * rowCount); int appIndex = 0; rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); if (rowCount + numRowsInSection > rowAtProgress) { - appIndex += (rowAtProgress - rowCount) * mNumAppsPerRow; break; } rowCount += numRowsInSection; @@ -306,9 +307,14 @@ public class AppsContainerRecyclerView extends RecyclerView appIndex = Math.max(0, Math.min(mApps.getAppsWithoutSectionBreaks().size() - 1, appIndex)); AppInfo appInfo = mApps.getAppsWithoutSectionBreaks().get(appIndex); int sectionedAppIndex = mApps.getApps().indexOf(appInfo); - scrollToPosition(sectionedAppIndex); - // Returns the section name of the row + // Scroll the position into view, anchored at the top of the screen if possible. We call the + // scroll method on the LayoutManager directly since it is not exposed by RecyclerView. + LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); + stopScroll(); + layoutManager.scrollToPositionWithOffset(sectionedAppIndex, 0); + + // Return the section name of the row return mApps.getSectionNameForApp(appInfo); } -- cgit v1.2.3 From 67c3086163593db47e57b8cf4fcb034334374000 Mon Sep 17 00:00:00 2001 From: Robert Kozikowski Date: Mon, 30 Mar 2015 23:46:46 +0100 Subject: Adding Launcher3 callbacks for trimming memory. It will help reduce memory footprint of Launcher, when in background. It is required to help deallocate images, when Google Now Launcher goes into the background. See cl/83222937/ . Plan after this CL: - Generate drop CL with launcher3_drop.sh. Add empty metods to make code compile. - Sync the cl/83222937 and hide the code behind the feature flag. It was previously submitted in ag/628070 and reverted in ag/630423 due to GSA release schedule. Change-Id: I56eee618b86d518d0afc362b6cf44c3bbec1f19c --- src/com/android/launcher3/Launcher.java | 3 +++ src/com/android/launcher3/LauncherCallbacks.java | 1 + src/com/android/launcher3/LauncherExtension.java | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index cd2bc596a..0414a5b1e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3823,6 +3823,9 @@ public class Launcher extends Activity mAppsCustomizeTabHost.trimMemory(); } } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onTrimMemory(level); + } } protected void showWorkspace(boolean animated) { diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index a1f4e0b90..d8128d6e5 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -42,6 +42,7 @@ public interface LauncherCallbacks { public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args); public void onHomeIntent(); public boolean handleBackPressed(); + public void onTrimMemory(int level); /* * Extension points for providing custom behavior on certain user interactions. diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index b264042cf..fe9bd6c23 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -107,6 +107,10 @@ public class LauncherExtension extends Launcher { return false; } + @Override + public void onTrimMemory(int level) { + } + @Override public void onLauncherProviderChange() { } -- cgit v1.2.3 From e9909f58c2b235f8066295c4faa6a00415968ae7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Mar 2015 15:22:29 -0700 Subject: Removing UninstallShortcutReceiver > Removing support due to its flacky design. Removing a shortcut causes a full reload. Also we do not have any concept of owner, so any app can remove any shortcut. Bug: 11372484 Change-Id: I781c922fac7dc77ea82cd0a2af74a5fca22500de --- AndroidManifest.xml | 15 --- res/values/strings.xml | 2 - .../launcher3/UninstallShortcutReceiver.java | 112 +-------------------- src/com/android/launcher3/Workspace.java | 2 - 4 files changed, 3 insertions(+), 128 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8c837cc1d..f43106f18 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -28,12 +28,6 @@ android:protectionLevel="dangerous" android:label="@string/permlab_install_shortcut" android:description="@string/permdesc_install_shortcut" /> - - - - - - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index 0b34d00a8..7f79b984c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -113,8 +113,6 @@ s --> This widget is too large for the Favorites tray Shortcut \"%s\" created. - - Shortcut \"%s\" was removed. Shortcut \"%s\" already exists. diff --git a/src/com/android/launcher3/UninstallShortcutReceiver.java b/src/com/android/launcher3/UninstallShortcutReceiver.java index c9d0bb5f5..59e4cb591 100644 --- a/src/com/android/launcher3/UninstallShortcutReceiver.java +++ b/src/com/android/launcher3/UninstallShortcutReceiver.java @@ -17,117 +17,11 @@ package com.android.launcher3; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.widget.Toast; - -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Iterator; +//TODO: Remove this public class UninstallShortcutReceiver extends BroadcastReceiver { - private static final String ACTION_UNINSTALL_SHORTCUT = - "com.android.launcher.action.UNINSTALL_SHORTCUT"; - - // The set of shortcuts that are pending uninstall - private static ArrayList mUninstallQueue = - new ArrayList(); - - // Determines whether to defer uninstalling shortcuts immediately until - // disableAndFlushUninstallQueue() is called. - private static boolean mUseUninstallQueue = false; - - private static class PendingUninstallShortcutInfo { - Intent data; - - public PendingUninstallShortcutInfo(Intent rawData) { - data = rawData; - } - } - - public void onReceive(Context context, Intent data) { - if (!ACTION_UNINSTALL_SHORTCUT.equals(data.getAction())) { - return; - } - - PendingUninstallShortcutInfo info = new PendingUninstallShortcutInfo(data); - if (mUseUninstallQueue) { - mUninstallQueue.add(info); - } else { - processUninstallShortcut(context, info); - } - } - - static void enableUninstallQueue() { - mUseUninstallQueue = true; - } - - static void disableAndFlushUninstallQueue(Context context) { - mUseUninstallQueue = false; - Iterator iter = mUninstallQueue.iterator(); - while (iter.hasNext()) { - processUninstallShortcut(context, iter.next()); - iter.remove(); - } - } - - private static void processUninstallShortcut(Context context, - PendingUninstallShortcutInfo pendingInfo) { - final Intent data = pendingInfo.data; - - LauncherAppState.setApplicationContext(context.getApplicationContext()); - LauncherAppState app = LauncherAppState.getInstance(); - synchronized (app) { // TODO: make removeShortcut internally threadsafe - removeShortcut(context, data); - } - } - - private static void removeShortcut(Context context, Intent data) { - Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); - String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); - boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); - - if (intent != null && name != null) { - final ContentResolver cr = context.getContentResolver(); - Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.INTENT }, - LauncherSettings.Favorites.TITLE + "=?", new String[] { name }, null); - - final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); - final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); - - boolean changed = false; - - try { - while (c.moveToNext()) { - try { - String intentStr = c.getString(intentIndex); - if (intentStr != null - && intent.filterEquals(Intent.parseUri(intentStr, 0))) { - final long id = c.getLong(idIndex); - final Uri uri = LauncherSettings.Favorites.getContentUri(id, false); - cr.delete(uri, null, null); - changed = true; - if (!duplicate) { - break; - } - } - } catch (URISyntaxException e) { - // Ignore - } - } - } finally { - c.close(); - } - - if (changed) { - cr.notifyChange(LauncherSettings.Favorites.CONTENT_URI, null); - Toast.makeText(context, context.getString(R.string.shortcut_uninstalled, name), - Toast.LENGTH_SHORT).show(); - } - } - } + @Override + public void onReceive(Context context, Intent data) { } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 92e01324f..ad15a6c33 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -402,7 +402,6 @@ public class Workspace extends SmoothPagedView setChildrenBackgroundAlphaMultipliers(1f); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging InstallShortcutReceiver.enableInstallQueue(); - UninstallShortcutReceiver.enableUninstallQueue(); post(new Runnable() { @Override public void run() { @@ -430,7 +429,6 @@ public class Workspace extends SmoothPagedView // Re-enable any Un/InstallShortcutReceiver and now process any queued items InstallShortcutReceiver.disableAndFlushInstallQueue(getContext()); - UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext()); mDragSourceInternal = null; mLauncher.onInteractionEnd(); -- cgit v1.2.3 From 1d4a2df091e2b5928406ea503321563a0ec8a01e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 30 Mar 2015 11:11:46 -0700 Subject: Removing ContentObserver from LauncherModel, and calling reload whereever necessary Change-Id: Ia4a8abdfe2be9703f3217a60527d3a1220b33bdc --- .../launcher3/AppWidgetsRestoredReceiver.java | 5 ++++ src/com/android/launcher3/AutoInstallsLayout.java | 2 +- src/com/android/launcher3/Folder.java | 2 +- src/com/android/launcher3/Launcher.java | 8 +++---- src/com/android/launcher3/LauncherAppState.java | 27 +++++---------------- .../launcher3/LauncherBackupAgentHelper.java | 2 +- .../android/launcher3/LauncherBackupHelper.java | 2 +- src/com/android/launcher3/LauncherModel.java | 28 ++++++++++------------ src/com/android/launcher3/LauncherProvider.java | 26 ++++++++++---------- src/com/android/launcher3/LauncherSettings.java | 27 ++++----------------- src/com/android/launcher3/Workspace.java | 3 +-- 11 files changed, 51 insertions(+), 81 deletions(-) diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 880aaf1ec..5e7a012d2 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -90,5 +90,10 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } + + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null) { + app.reloadWorkspace(); + } } } diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 9180dcf42..dac79a8ef 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -606,7 +606,7 @@ public class AutoInstallsLayout { // failed to add, and less than 2 were actually added if (folderItems.size() < 2) { // Delete the folder - Uri uri = Favorites.getContentUri(folderId, false); + Uri uri = Favorites.getContentUri(folderId); SqlArguments args = new SqlArguments(uri, null, null); mDb.delete(args.table, args.where, args.args); addedId = -1; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 23582cec8..4529a9459 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -845,7 +845,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View v = list.get(i); ItemInfo info = (ItemInfo) v.getTag(); LauncherModel.addItemToDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY, false); + info.cellX, info.cellY); } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8a9db71ac..8a21d624c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1554,7 +1554,7 @@ public class Launcher extends Activity return; } - LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1], false); + LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]); if (!mRestoring) { mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, @@ -1616,7 +1616,7 @@ public class Launcher extends Activity launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo); LauncherModel.addItemToDatabase(this, launcherInfo, - container, screenId, info.cellX, info.cellY, false); + container, screenId, info.cellX, info.cellY); if (!mRestoring) { if (hostView == null) { @@ -2409,8 +2409,8 @@ public class Launcher extends Activity folderInfo.title = getText(R.string.folder_name); // Update the model - LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, cellX, cellY, - false); + LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, + cellX, cellY); sFolders.put(folderInfo.id, folderInfo); // Create the view diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 555b1ccad..2a08b8176 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -19,16 +19,13 @@ package com.android.launcher3; import android.annotation.TargetApi; import android.app.SearchManager; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.Resources; -import android.database.ContentObserver; import android.graphics.Point; import android.os.Build; -import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; @@ -116,11 +113,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); sContext.registerReceiver(mModel, filter); - - // Register for changes to the favorites - ContentResolver resolver = sContext.getContentResolver(); - resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, - mFavoritesObserver); } /** @@ -131,23 +123,16 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext); launcherApps.removeOnAppsChangedCallback(mModel); PackageInstallerCompat.getInstance(sContext).onStop(); - - ContentResolver resolver = sContext.getContentResolver(); - resolver.unregisterContentObserver(mFavoritesObserver); } /** - * Receives notifications whenever the user favorites have changed. + * Reloads the workspace items from the DB and re-binds the workspace. This should generally + * not be called as DB updates are automatically followed by UI update */ - private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - // If the database has ever changed, then we really need to force a reload of the - // workspace on the next load - mModel.resetLoadedState(false, true); - mModel.startLoaderFromBackground(); - } - }; + public void reloadWorkspace() { + mModel.resetLoadedState(false, true); + mModel.startLoaderFromBackground(); + } LauncherModel setLauncher(Launcher launcher) { mModel.initialize(launcher); diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index ddfd70d6b..5f7173fac 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -78,7 +78,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { super.onRestore(data, appVersionCode, newState); // If no favorite was migrated, clear the data and start fresh. final Cursor c = getContentResolver().query( - LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, null, null, null, null); + LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); hasData = c.moveToNext(); c.close(); } catch (Exception e) { diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index c03480065..064f4363a 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -462,7 +462,7 @@ public class LauncherBackupHelper implements BackupHelper { ContentResolver cr = mContext.getContentResolver(); ContentValues values = unpackFavorite(buffer, dataSize); - cr.insert(Favorites.CONTENT_URI_NO_NOTIFICATION, values); + cr.insert(Favorites.CONTENT_URI, values); } /** diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index dcb375928..c6ed0da82 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -566,7 +566,7 @@ public class LauncherModel extends BroadcastReceiver // Add the shortcut to the db addItemToDatabase(context, shortcutInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, - screenId, cordinates[0], cordinates[1], false); + screenId, cordinates[0], cordinates[1]); // Save the ShortcutInfo for binding in the workspace addedShortcutsFinal.add(shortcutInfo); } @@ -652,7 +652,7 @@ public class LauncherModel extends BroadcastReceiver long screenId, int cellX, int cellY) { if (item.container == ItemInfo.NO_ID) { // From all apps - addItemToDatabase(context, item, container, screenId, cellX, cellY, false); + addItemToDatabase(context, item, container, screenId, cellX, cellY); } else { // From somewhere else moveItemInDatabase(context, item, container, screenId, cellX, cellY); @@ -718,7 +718,7 @@ public class LauncherModel extends BroadcastReceiver static void updateItemInDatabaseHelper(Context context, final ContentValues values, final ItemInfo item, final String callingFunction) { final long itemId = item.id; - final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false); + final Uri uri = LauncherSettings.Favorites.getContentUri(itemId); final ContentResolver cr = context.getContentResolver(); final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); @@ -744,7 +744,7 @@ public class LauncherModel extends BroadcastReceiver for (int i = 0; i < count; i++) { ItemInfo item = items.get(i); final long itemId = item.id; - final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false); + final Uri uri = LauncherSettings.Favorites.getContentUri(itemId); ContentValues values = valuesList.get(i); ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); @@ -994,7 +994,7 @@ public class LauncherModel extends BroadcastReceiver * cellY fields of the item. Also assigns an ID to the item. */ static void addItemToDatabase(Context context, final ItemInfo item, final long container, - final long screenId, final int cellX, final int cellY, final boolean notify) { + final long screenId, final int cellX, final int cellY) { item.container = container; item.cellX = cellX; item.cellY = cellY; @@ -1017,8 +1017,7 @@ public class LauncherModel extends BroadcastReceiver final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); Runnable r = new Runnable() { public void run() { - cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI : - LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values); + cr.insert(LauncherSettings.Favorites.CONTENT_URI, values); // Lock on mBgLock *after* the db operation synchronized (sBgLock) { @@ -1102,7 +1101,7 @@ public class LauncherModel extends BroadcastReceiver Runnable r = new Runnable() { public void run() { for (ItemInfo item : items) { - final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false); + final Uri uri = LauncherSettings.Favorites.getContentUri(item.id); cr.delete(uri, null, null); // Lock on mBgLock *after* the db operation @@ -1197,7 +1196,7 @@ public class LauncherModel extends BroadcastReceiver Runnable r = new Runnable() { public void run() { - cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null); + cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null); // Lock on mBgLock *after* the db operation synchronized (sBgLock) { sBgItemsIdMap.remove(info.id); @@ -1205,7 +1204,7 @@ public class LauncherModel extends BroadcastReceiver sBgWorkspaceItems.remove(info); } - cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, + cr.delete(LauncherSettings.Favorites.CONTENT_URI, LauncherSettings.Favorites.CONTAINER + "=" + info.id, null); // Lock on mBgLock *after* the db operation synchronized (sBgLock) { @@ -1823,7 +1822,7 @@ public class LauncherModel extends BroadcastReceiver final ArrayList itemsToRemove = new ArrayList(); final ArrayList restoredRows = new ArrayList(); - final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION; + final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI; if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri); final Cursor c = contentResolver.query(contentUri, null, null, null, null); @@ -2304,8 +2303,7 @@ public class LauncherModel extends BroadcastReceiver } // Don't notify content observers try { - client.delete(LauncherSettings.Favorites.getContentUri(id, false), - null, null); + client.delete(LauncherSettings.Favorites.getContentUri(id), null, null); } catch (RemoteException e) { Log.w(TAG, "Could not remove id = " + id); } @@ -2324,7 +2322,7 @@ public class LauncherModel extends BroadcastReceiver selectionBuilder.append(")"); ContentValues values = new ContentValues(); values.put(LauncherSettings.Favorites.RESTORED, 0); - updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, + updater.update(LauncherSettings.Favorites.CONTENT_URI, values, selectionBuilder.toString(), null); } catch (RemoteException e) { Log.w(TAG, "Could not update restored rows"); @@ -2395,7 +2393,7 @@ public class LauncherModel extends BroadcastReceiver */ private void updateItem(long itemId, ContentValues update) { mContext.getContentResolver().update( - LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, + LauncherSettings.Favorites.CONTENT_URI, update, BaseColumns._ID + "= ?", new String[]{Long.toString(itemId)}); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index dfacfa3e4..a23553a04 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -70,7 +70,6 @@ public class LauncherProvider extends ContentProvider { static final String TABLE_FAVORITES = "favorites"; static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens"; - static final String PARAMETER_NOTIFY = "notify"; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; @@ -150,7 +149,8 @@ public class LauncherProvider extends ContentProvider { // In very limited cases, we support system|signature permission apps to add to the db String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD); - if (externalAdd != null && "true".equals(externalAdd)) { + final boolean isExternalAll = externalAdd != null && "true".equals(externalAdd); + if (isExternalAll) { if (!mOpenHelper.initializeExternalAdd(initialValues)) { return null; } @@ -162,7 +162,14 @@ public class LauncherProvider extends ContentProvider { if (rowId < 0) return null; uri = ContentUris.withAppendedId(uri, rowId); - sendNotify(uri); + notifyListeners(); + + if (isExternalAll) { + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null) { + app.reloadWorkspace(); + } + } return uri; } @@ -187,7 +194,7 @@ public class LauncherProvider extends ContentProvider { db.endTransaction(); } - sendNotify(uri); + notifyListeners(); return values.length; } @@ -211,7 +218,7 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.delete(args.table, args.where, args.args); - if (count > 0) sendNotify(uri); + if (count > 0) notifyListeners(); return count; } @@ -223,17 +230,12 @@ public class LauncherProvider extends ContentProvider { addModifiedTime(values); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.update(args.table, values, args.where, args.args); - if (count > 0) sendNotify(uri); + if (count > 0) notifyListeners(); return count; } - private void sendNotify(Uri uri) { - String notify = uri.getQueryParameter(PARAMETER_NOTIFY); - if (notify == null || "true".equals(notify)) { - getContext().getContentResolver().notifyChange(uri, null); - } - + private void notifyListeners() { // always notify the backup agent LauncherBackupAgentHelper.dataChanged(getContext()); if (mListener != null) { diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index d161fbb3d..d657cb50f 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -109,8 +109,7 @@ class LauncherSettings { * The content:// style URL for this table */ static final Uri CONTENT_URI = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_WORKSPACE_SCREENS + - "?" + LauncherProvider.PARAMETER_NOTIFY + "=true"); + LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_WORKSPACE_SCREENS); /** * The rank of this screen -- ie. how it is ordered relative to the other screens. @@ -127,36 +126,18 @@ class LauncherSettings { * The content:// style URL for this table */ static final Uri CONTENT_URI = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_FAVORITES + - "?" + LauncherProvider.PARAMETER_NOTIFY + "=true"); - - /** - * The content:// style URL for this table - */ - static final Uri OLD_CONTENT_URI = Uri.parse("content://" + - LauncherProvider.OLD_AUTHORITY + "/" + LauncherProvider.TABLE_FAVORITES + - "?" + LauncherProvider.PARAMETER_NOTIFY + "=true"); - - /** - * The content:// style URL for this table. When this Uri is used, no notification is - * sent if the content changes. - */ - static final Uri CONTENT_URI_NO_NOTIFICATION = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_FAVORITES + - "?" + LauncherProvider.PARAMETER_NOTIFY + "=false"); + LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_FAVORITES); /** * The content:// style URL for a given row, identified by its id. * * @param id The row id. - * @param notify True to send a notification is the content changes. * * @return The unique content URL for the specified row. */ - static Uri getContentUri(long id, boolean notify) { + static Uri getContentUri(long id) { return Uri.parse("content://" + LauncherProvider.AUTHORITY + - "/" + LauncherProvider.TABLE_FAVORITES + "/" + id + "?" + - LauncherProvider.PARAMETER_NOTIFY + "=" + notify); + "/" + LauncherProvider.TABLE_FAVORITES + "/" + id); } /** diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index ad15a6c33..a79add05f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4323,8 +4323,7 @@ public class Workspace extends SmoothPagedView cellX = hotseat.getCellXFromOrder((int) info.screenId); cellY = hotseat.getCellYFromOrder((int) info.screenId); } - LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX, - cellY, false); + LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX, cellY); } if (v instanceof FolderIcon) { FolderIcon fi = (FolderIcon) v; -- cgit v1.2.3 From 0c9a354c9732585620569bafaec0973977baf614 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 1 Apr 2015 16:04:21 -0700 Subject: Adding a system status column in icon cache & invalidating items based on it Bug: 20034430 Change-Id: Icd6c6426fc7ed26cd104bf22a9d2b0263cb0fa67 --- src/com/android/launcher3/IconCache.java | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 9b2119e0b..21ac5c030 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -49,6 +49,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map.Entry; /** @@ -225,6 +226,7 @@ public class IconCache { * @return The set of packages for which icons have updated. */ public HashSet updateDBIcons(UserHandleCompat user, List apps) { + mIconDb.updateSystemStateString(mContext); long userSerial = mUserManager.getSerialNumberForUser(user); PackageManager pm = mContext.getPackageManager(); HashMap pkgInfoMap = new HashMap(); @@ -239,7 +241,8 @@ public class IconCache { Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, new String[] {IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, - IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION}, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, + IconDB.COLUMN_SYSTEM_STATE}, IconDB.COLUMN_USER + " = ? ", new String[] {Long.toString(userSerial)}, null, null, null); @@ -248,6 +251,7 @@ public class IconCache { final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); HashSet itemsToRemove = new HashSet(); HashSet updatedPackages = new HashSet(); @@ -268,7 +272,8 @@ public class IconCache { long updateTime = c.getLong(indexLastUpdate); int version = c.getInt(indexVersion); LauncherActivityInfoCompat app = componentMap.remove(component); - if (version == info.versionCode && updateTime == info.lastUpdateTime) { + if (version == info.versionCode && updateTime == info.lastUpdateTime && + TextUtils.equals(mIconDb.mSystemState, c.getString(systemStateIndex))) { continue; } if (app == null) { @@ -605,7 +610,7 @@ public class IconCache { } private static final class IconDB extends SQLiteOpenHelper { - private final static int DB_VERSION = 2; + private final static int DB_VERSION = 3; private final static String TABLE_NAME = "icons"; private final static String COLUMN_ROWID = "rowid"; @@ -616,11 +621,21 @@ public class IconCache { private final static String COLUMN_ICON = "icon"; private final static String COLUMN_ICON_LOW_RES = "icon_low_res"; private final static String COLUMN_LABEL = "label"; + private final static String COLUMN_SYSTEM_STATE = "system_state"; + + public String mSystemState; public IconDB(Context context) { super(context, LauncherFiles.APP_ICONS_DB, null, DB_VERSION); + updateSystemStateString(context); + } + + public void updateSystemStateString(Context c) { + mSystemState = Locale.getDefault().toString() + "," + + c.getResources().getConfiguration().mcc; } + @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + @@ -631,6 +646,7 @@ public class IconCache { COLUMN_ICON + " BLOB, " + COLUMN_ICON_LOW_RES + " BLOB, " + COLUMN_LABEL + " TEXT, " + + COLUMN_SYSTEM_STATE + " TEXT, " + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " + ");"); } @@ -656,12 +672,13 @@ public class IconCache { public ContentValues newContentValues(Bitmap icon, String label) { ContentValues values = new ContentValues(); - values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon)); - values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap( + values.put(COLUMN_ICON, Utilities.flattenBitmap(icon)); + values.put(COLUMN_ICON_LOW_RES, Utilities.flattenBitmap( Bitmap.createScaledBitmap(icon, icon.getWidth() / LOW_RES_SCALE_FACTOR, icon.getHeight() / LOW_RES_SCALE_FACTOR, true))); - values.put(IconDB.COLUMN_LABEL, label); + values.put(COLUMN_LABEL, label); + values.put(COLUMN_SYSTEM_STATE, mSystemState); return values; } } -- cgit v1.2.3 From 68fdeaadc621b7a9200f5c75790eb756a1983d02 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Thu, 2 Apr 2015 11:53:23 -0700 Subject: Stop using custom activity transition, rely on system default in these cases -> workaround for platform issue related to this resource, but also a decent code cleanup issue 20006693 Change-Id: Iea3a2fb14be5ed78d882ddf9d970b12d177f4249 --- res/anim/fade_in_fast.xml | 23 ----------------------- res/anim/fade_out_fast.xml | 23 ----------------------- res/anim/no_anim.xml | 18 ------------------ src/com/android/launcher3/Launcher.java | 7 ++++--- 4 files changed, 4 insertions(+), 67 deletions(-) delete mode 100644 res/anim/fade_in_fast.xml delete mode 100644 res/anim/fade_out_fast.xml delete mode 100644 res/anim/no_anim.xml diff --git a/res/anim/fade_in_fast.xml b/res/anim/fade_in_fast.xml deleted file mode 100644 index 4fa9847aa..000000000 --- a/res/anim/fade_in_fast.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/res/anim/fade_out_fast.xml b/res/anim/fade_out_fast.xml deleted file mode 100644 index a061a6ca9..000000000 --- a/res/anim/fade_out_fast.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/res/anim/no_anim.xml b/res/anim/no_anim.xml deleted file mode 100644 index 02b162519..000000000 --- a/res/anim/no_anim.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0414a5b1e..5b8eaef50 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2939,9 +2939,10 @@ public class Launcher extends Activity } Bundle optsBundle = null; - if (useLaunchAnimation) { - ActivityOptions opts = Utilities.isLmpOrAbove() ? - ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) : + if (useLaunchAnimation && !Utilities.isLmpOrAbove()) { + // On pre-L devices, we use the scale up transition. + // Otherwise we use system default. + ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); optsBundle = opts.toBundle(); } -- cgit v1.2.3 From 22235bcb40071af464fc9accf0fbf082056182f7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 Apr 2015 09:23:43 -0700 Subject: Reinflating QBS on resume, if the widget orientation is not same as launcher orientation Bug: 20044969 Change-Id: I5c285ddb09fc8b5d8444795eda64cd28486ab81d --- src/com/android/launcher3/Launcher.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8a21d624c..c040d93f2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1047,6 +1047,7 @@ public class Launcher extends Activity // (framework issue). On resuming, we ensure that any widgets are inflated for the current // orientation. getWorkspace().reinflateWidgetsIfNecessary(); + reinflateQSBIfNecessary(); // Process any items that were added while Launcher was away. InstallShortcutReceiver.disableAndFlushInstallQueue(this); @@ -3505,6 +3506,15 @@ public class Launcher extends Activity return mQsb; } + private void reinflateQSBIfNecessary() { + if (mQsb instanceof LauncherAppWidgetHostView && + ((LauncherAppWidgetHostView) mQsb).isReinflateRequired()) { + mSearchDropTargetBar.removeView(mQsb); + mQsb = null; + mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); + } + } + @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); -- cgit v1.2.3 From 35ca873bd9e1331765343a6cae8387431794b0af Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 6 Apr 2015 10:45:31 -0700 Subject: Adding nullcheck when getting app restrictions Bug: 20085185 Change-Id: I782a79a331a2d58287e34e24ed7730207bf260cb --- src/com/android/launcher3/LauncherProvider.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index a23553a04..1f59533cc 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -341,8 +341,11 @@ public class LauncherProvider extends ContentProvider { Context ctx = getContext(); UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE); Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName()); - String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME); + if (bundle == null) { + return null; + } + String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME); if (packageName != null) { try { Resources targetResources = ctx.getPackageManager() -- cgit v1.2.3 From e0bca386247024728b1e71e9ae530fac7e5b3171 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 6 Apr 2015 18:39:22 +0000 Subject: Revert "Setting hotseat padding to the container for symmetry" This reverts commit d81992b6e70e05a3c208e02d019e606b7cb8a0b7. Bug:20087585 Change-Id: I4949ec9e0e27afa218597b943001e7240cfdc548 --- src/com/android/launcher3/DeviceProfile.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 7d02e10b5..b97f0f2f7 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -766,7 +766,8 @@ public class DeviceProfile { lp.gravity = Gravity.BOTTOM; lp.width = LayoutParams.MATCH_PARENT; lp.height = hotseatBarHeightPx; - hotseat.setPadding(2 * edgeMarginPx, 0, 2 * edgeMarginPx, 0); + hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0, + 2 * edgeMarginPx, 0); } hotseat.setLayoutParams(lp); -- cgit v1.2.3 From 2e52c90b3ff0a04bd734aa19c54c39cef186b653 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Mon, 6 Apr 2015 13:11:28 -0700 Subject: resolved conflicts for merge of 68fdeaad to ub-launcher3-burnaby Change-Id: I29aa5b37968b1ced81cdeb68a021db9310047252 --- res/anim/fade_in_fast.xml | 23 ----------------------- res/anim/fade_out_fast.xml | 23 ----------------------- res/anim/no_anim.xml | 18 ------------------ src/com/android/launcher3/Launcher.java | 9 +++------ 4 files changed, 3 insertions(+), 70 deletions(-) delete mode 100644 res/anim/fade_in_fast.xml delete mode 100644 res/anim/fade_out_fast.xml delete mode 100644 res/anim/no_anim.xml diff --git a/res/anim/fade_in_fast.xml b/res/anim/fade_in_fast.xml deleted file mode 100644 index 4fa9847aa..000000000 --- a/res/anim/fade_in_fast.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/res/anim/fade_out_fast.xml b/res/anim/fade_out_fast.xml deleted file mode 100644 index a061a6ca9..000000000 --- a/res/anim/fade_out_fast.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/res/anim/no_anim.xml b/res/anim/no_anim.xml deleted file mode 100644 index 02b162519..000000000 --- a/res/anim/no_anim.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index c040d93f2..2fa2f4ad7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2958,14 +2958,11 @@ public class Launcher extends Activity sClipRevealMethod = null; } } - if (opts == null) { - opts = Utilities.isLmpOrAbove() ? - ActivityOptions.makeCustomAnimation(this, - R.anim.task_open_enter, R.anim.no_anim) : - ActivityOptions.makeScaleUpAnimation(v, 0, 0, + if (opts == null && !Utilities.isLmpOrAbove()) { + opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); } - optsBundle = opts.toBundle(); + optsBundle = opts != null ? opts.toBundle() : null; } if (user == null || user.equals(UserHandleCompat.myUserHandle())) { -- cgit v1.2.3 From d180cf7e16d8dbd7d33980078baac7d09709b6cd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 6 Apr 2015 12:45:40 -0700 Subject: Adding support for loading package icon in iconCache > package icons need to be retrieved from IconCache for the new widget tray Bug: 19897708 Change-Id: Iaafc5e16477aaa4e9a7c46b5abf8146cd1101ffd --- src/com/android/launcher3/IconCache.java | 77 ++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 9b2119e0b..aaec2b40a 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -200,7 +200,7 @@ public class IconCache { PackageManager.GET_UNINSTALLED_PACKAGES); long userSerial = mUserManager.getSerialNumberForUser(user); for (LauncherActivityInfoCompat app : mLauncherApps.getActivityList(packageName, user)) { - addIconToDB(app, info, userSerial); + addIconToDBAndMemCache(app, info, userSerial); } } catch (NameNotFoundException e) { Log.d(TAG, "Package not found", e); @@ -295,14 +295,25 @@ public class IconCache { if (info == null) { continue; } - addIconToDB(app, info, userSerial); + addIconToDBAndMemCache(app, info, userSerial); } return updatedPackages; } - private void addIconToDB(LauncherActivityInfoCompat app, PackageInfo info, long userSerial) { + private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, + long userSerial) { ContentValues values = updateCacheAndGetContentValues(app); + addIconToDB(values, app.getComponentName(), info, userSerial); values.put(IconDB.COLUMN_COMPONENT, app.getComponentName().flattenToString()); + } + + /** + * Updates {@param values} to contain versoning information and adds it to the DB. + * @param values {@link ContentValues} containing icon & title + */ + private void addIconToDB(ContentValues values, ComponentName key, + PackageInfo info, long userSerial) { + values.put(IconDB.COLUMN_COMPONENT, key.flattenToString()); values.put(IconDB.COLUMN_USER, userSerial); values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime); values.put(IconDB.COLUMN_VERSION, info.versionCode); @@ -320,7 +331,6 @@ public class IconCache { return mIconDb.newContentValues(entry.icon, entry.title.toString()); } - /** * Empty out the cache. */ @@ -435,6 +445,18 @@ public class IconCache { shortcutInfo.usingLowResIcon = entry.isLowResIcon; } + /** + * Fill in {@param appInfo} with the icon and label for {@param packageName} + */ + public synchronized void getTitleAndIconForApp( + String packageName, UserHandleCompat user, boolean useLowResIcon, AppInfo appInfoOut) { + CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon); + appInfoOut.iconBitmap = entry.icon; + appInfoOut.title = entry.title; + appInfoOut.usingLowResIcon = entry.isLowResIcon; + appInfoOut.contentDescription = entry.contentDescription; + } + public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { if (!mDefaultIcons.containsKey(user)) { mDefaultIcons.put(user, makeDefaultIcon(user)); @@ -464,8 +486,8 @@ public class IconCache { entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext); } else { if (usePackageIcon) { - CacheEntry packageEntry = getEntryForPackage( - componentName.getPackageName(), user); + CacheEntry packageEntry = getEntryForPackageLocked( + componentName.getPackageName(), user, false); if (packageEntry != null) { if (DEBUG) Log.d(TAG, "using package default icon for " + componentName.toShortString()); @@ -498,7 +520,7 @@ public class IconCache { Bitmap icon, CharSequence title) { removeFromMemCacheLocked(packageName, user); - CacheEntry entry = getEntryForPackage(packageName, user); + CacheEntry entry = getEntryForPackageLocked(packageName, user, false); if (!TextUtils.isEmpty(title)) { entry.title = title; } @@ -510,24 +532,41 @@ public class IconCache { /** * Gets an entry for the package, which can be used as a fallback entry for various components. * This method is not thread safe, it must be called from a synchronized method. + * */ - private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) { - ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME);; + private CacheEntry getEntryForPackageLocked(String packageName, UserHandleCompat user, + boolean useLowResIcon) { + ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME); ComponentKey cacheKey = new ComponentKey(cn, user); CacheEntry entry = mCache.get(cacheKey); - if (entry == null) { + if (entry == null || (entry.isLowResIcon && !useLowResIcon)) { entry = new CacheEntry(); - entry.title = ""; - entry.contentDescription = ""; mCache.put(cacheKey, entry); - try { - ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); - entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext); - entry.title = info.loadLabel(mPackageManager); - entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); - } catch (NameNotFoundException e) { - if (DEBUG) Log.d(TAG, "Application not installed " + packageName); + // Check the DB first. + if (!getEntryFromDB(cn, user, entry, useLowResIcon)) { + try { + PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); + ApplicationInfo appInfo = info.applicationInfo; + if (appInfo == null) { + throw new NameNotFoundException("ApplicationInfo is null"); + } + Drawable drawable = mUserManager.getBadgedDrawableForUser( + appInfo.loadIcon(mPackageManager), user); + entry.icon = Utilities.createIconBitmap(drawable, mContext); + entry.title = appInfo.loadLabel(mPackageManager); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); + entry.isLowResIcon = false; + + // Add the icon in the DB here, since these do not get written during + // package updates. + ContentValues values = + mIconDb.newContentValues(entry.icon, entry.title.toString()); + addIconToDB(values, cn, info, mUserManager.getSerialNumberForUser(user)); + + } catch (NameNotFoundException e) { + if (DEBUG) Log.d(TAG, "Application not installed " + packageName); + } } } return entry; -- cgit v1.2.3 From c517f4ce65cfbb3c52231262cf24847820213d29 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 6 Apr 2015 12:42:02 -0700 Subject: Ensuring that work-profile applications are mixed in with other apps. Bug: 20046158 Change-Id: I833c1a1467889db07f9ec33ca77641322901958e --- .../android/launcher3/AlphabeticalAppsList.java | 76 +++++++++++++++++++--- src/com/android/launcher3/LauncherModel.java | 35 ---------- 2 files changed, 66 insertions(+), 45 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index c1d2738da..9ab9d1295 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -4,13 +4,75 @@ import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; +import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +/** + * A private class to manage access to an app name comparator. + */ +class AppNameComparator { + private UserManagerCompat mUserManager; + private Comparator mAppNameComparator; + private HashMap mUserSerialCache = new HashMap<>(); + + public AppNameComparator(Context context) { + final Collator collator = Collator.getInstance(); + mUserManager = UserManagerCompat.getInstance(context); + mAppNameComparator = new Comparator() { + public final int compare(AppInfo a, AppInfo b) { + // Order by the title + int result = collator.compare(a.title.toString().trim(), + b.title.toString().trim()); + if (result == 0) { + // If two apps have the same title, then order by the component name + result = a.componentName.compareTo(b.componentName); + if (result == 0) { + // If the two apps are the same component, then prioritize by the order that + // the app user was created (prioritizing the main user's apps) + if (UserHandleCompat.myUserHandle().equals(a.user)) { + return -1; + } else { + Long aUserSerial = getAndCacheUserSerial(a.user); + Long bUserSerial = getAndCacheUserSerial(b.user); + return aUserSerial.compareTo(bUserSerial); + } + } + } + return result; + } + }; + } + + /** + * Returns a locale-aware comparator that will alphabetically order a list of applications. + */ + public Comparator getComparator() { + // Clear the user serial cache so that we get serials as needed in the comparator + mUserSerialCache.clear(); + return mAppNameComparator; + } + + /** + * Returns the user serial for this user, using a cached serial if possible. + */ + private Long getAndCacheUserSerial(UserHandleCompat user) { + Long userSerial = mUserSerialCache.get(user); + if (userSerial == null) { + userSerial = mUserManager.getSerialNumberForUser(user); + mUserSerialCache.put(user, userSerial); + } + return userSerial; + } +} + /** * The alphabetically sorted list of applications. */ @@ -41,9 +103,11 @@ public class AlphabeticalAppsList { private RecyclerView.Adapter mAdapter; private Filter mFilter; private AlphabeticIndexCompat mIndexer; + private AppNameComparator mAppNameComparator; public AlphabeticalAppsList(Context context) { mIndexer = new AlphabeticIndexCompat(context); + mAppNameComparator = new AppNameComparator(context); } /** @@ -81,13 +145,6 @@ public class AlphabeticalAppsList { return mIndexer.computeSectionName(info.title.toString().trim()); } - /** - * Returns the indexer for this locale. - */ - public AlphabeticIndexCompat getIndexer() { - return mIndexer; - } - /** * Returns whether there are no filtered results. */ @@ -108,7 +165,7 @@ public class AlphabeticalAppsList { * Sets the current set of apps. */ public void setApps(List apps) { - Collections.sort(apps, LauncherModel.getAppNameComparator()); + Collections.sort(apps, mAppNameComparator.getComparator()); mApps.clear(); mApps.addAll(apps); onAppsUpdated(); @@ -183,8 +240,7 @@ public class AlphabeticalAppsList { * Implementation to actually add an app to the alphabetic list */ private void addApp(AppInfo info) { - Comparator appNameComparator = LauncherModel.getAppNameComparator(); - int index = Collections.binarySearch(mApps, info, appNameComparator); + int index = Collections.binarySearch(mApps, info, mAppNameComparator.getComparator()); if (index < 0) { mApps.add(-(index + 1), info); onAppsUpdated(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index c6ed0da82..1f36331ec 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3622,41 +3622,6 @@ public class LauncherModel extends BroadcastReceiver return folderInfo; } - public static final Comparator getAppNameComparator() { - final Collator collator = Collator.getInstance(); - return new Comparator() { - public final int compare(AppInfo a, AppInfo b) { - if (a.user.equals(b.user)) { - int result = collator.compare(a.title.toString().trim(), - b.title.toString().trim()); - if (result == 0) { - result = a.componentName.compareTo(b.componentName); - } - return result; - } else { - // TODO Need to figure out rules for sorting - // profiles, this puts work second. - return a.user.toString().compareTo(b.user.toString()); - } - } - }; - } - public static final Comparator APP_INSTALL_TIME_COMPARATOR - = new Comparator() { - public final int compare(AppInfo a, AppInfo b) { - if (a.firstInstallTime < b.firstInstallTime) return 1; - if (a.firstInstallTime > b.firstInstallTime) return -1; - return 0; - } - }; - static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) { - if (info.activityInfo != null) { - return new ComponentName(info.activityInfo.packageName, info.activityInfo.name); - } else { - return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); - } - } - public static class WidgetAndShortcutNameComparator implements Comparator { private final AppWidgetManagerCompat mManager; private final PackageManager mPackageManager; -- cgit v1.2.3 From 9121fbffafe0b90a23768e5ebc5fffab2a2175c2 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 6 Apr 2015 15:12:49 -0700 Subject: Small refactoring to apps list. - Fixes issue with fading in app icons when items are added/removed - Reduces number of expensive calls when drawing sections and the scrollbar - Removes fake section AppInfos in the adapters --- .../android/launcher3/AlphabeticalAppsList.java | 107 +++++++++++++++------ .../launcher3/AppsContainerRecyclerView.java | 57 ++++++----- src/com/android/launcher3/AppsContainerView.java | 27 +++--- src/com/android/launcher3/AppsGridAdapter.java | 21 ++-- src/com/android/launcher3/AppsListAdapter.java | 16 +-- src/com/android/launcher3/BubbleTextView.java | 5 - 6 files changed, 138 insertions(+), 95 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 9ab9d1295..8d1db632e 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -81,24 +81,65 @@ public class AlphabeticalAppsList { /** * Info about a section in the alphabetic list */ - public class SectionInfo { + public static class SectionInfo { + // The name of this section public String sectionName; + // The number of applications in this section public int numAppsInSection; + // The first app AdapterItem for this section + public AdapterItem firstAppItem; + + public SectionInfo(String name) { + sectionName = name; + } + } + + /** + * Info about a particular adapter item (can be either section or app) + */ + public static class AdapterItem { + // The index of this adapter item in the list + public int position; + // Whether or not the item at this adapter position is a section or not + public boolean isSectionHeader; + // The name of this section, or the section that this app is contained in + public String sectionName; + // The associated AppInfo, or null if this adapter item is a section + public AppInfo appInfo; + // The index of this app (not including sections), or -1 if this adapter item is a section + public int appIndex; + + public static AdapterItem asSection(int pos, String name) { + AdapterItem item = new AdapterItem(); + item.position = pos; + item.isSectionHeader = true; + item.sectionName = name; + item.appInfo = null; + item.appIndex = -1; + return item; + } + + public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, int appIndex) { + AdapterItem item = new AdapterItem(); + item.position = pos; + item.isSectionHeader = false; + item.sectionName = sectionName; + item.appInfo = appInfo; + item.appIndex = appIndex; + return item; + } } /** * A filter interface to limit the set of applications in the apps list. */ public interface Filter { - public boolean retainApp(AppInfo info); + public boolean retainApp(AppInfo info, String sectionName); } - // Hack to force RecyclerView to break sections - public static final AppInfo SECTION_BREAK_INFO = null; - private List mApps = new ArrayList<>(); private List mFilteredApps = new ArrayList<>(); - private List mSectionedFilteredApps = new ArrayList<>(); + private List mSectionedFilteredApps = new ArrayList<>(); private List mSections = new ArrayList<>(); private RecyclerView.Adapter mAdapter; private Filter mFilter; @@ -127,22 +168,15 @@ public class AlphabeticalAppsList { /** * Returns the current filtered list of applications broken down into their sections. */ - public List getApps() { + public List getAdapterItems() { return mSectionedFilteredApps; } /** - * Returns the current filtered list of applications. + * Returns the number of applications in this list. */ - public List getAppsWithoutSectionBreaks() { - return mFilteredApps; - } - - /** - * Returns the section name for the application. - */ - public String getSectionNameForApp(AppInfo info) { - return mIndexer.computeSectionName(info.title.toString().trim()); + public int getSize() { + return mFilteredApps.size(); } /** @@ -276,28 +310,39 @@ public class AlphabeticalAppsList { * Updates internals when the set of apps are updated. */ private void onAppsUpdated() { - // Recreate the filtered apps + // Recreate the filtered and sectioned apps (for convenience for the grid layout) mFilteredApps.clear(); - for (AppInfo info : mApps) { - if (mFilter == null || mFilter.retainApp(info)) { - mFilteredApps.add(info); - } - } - - // Section the apps (for convenience for the grid layout) mSections.clear(); mSectionedFilteredApps.clear(); SectionInfo lastSectionInfo = null; - for (AppInfo info : mFilteredApps) { - String sectionName = getSectionNameForApp(info); + int position = 0; + int appIndex = 0; + for (AppInfo info : mApps) { + String sectionName = mIndexer.computeSectionName(info.title.toString().trim()); + + // Check if we want to retain this app + if (mFilter != null && !mFilter.retainApp(info, sectionName)) { + continue; + } + + // Create a new section if necessary if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) { - lastSectionInfo = new SectionInfo(); - lastSectionInfo.sectionName = sectionName; - mSectionedFilteredApps.add(SECTION_BREAK_INFO); + lastSectionInfo = new SectionInfo(sectionName); mSections.add(lastSectionInfo); + + // Create a new section item + AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName); + mSectionedFilteredApps.add(sectionItem); } + + // Create an app item + AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++); lastSectionInfo.numAppsInSection++; - mSectionedFilteredApps.add(info); + if (lastSectionInfo.firstAppItem == null) { + lastSectionInfo.firstAppItem = appItem; + } + mSectionedFilteredApps.add(appItem); + mFilteredApps.add(info); } } } diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index b942ea451..63e7fe4ac 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -228,12 +228,12 @@ public class AppsContainerRecyclerView extends RecyclerView * Draws the fast scroller popup. */ private void drawFastScrollerPopup(Canvas canvas) { - int x; - int y; - boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == - LAYOUT_DIRECTION_RTL); - if (mFastScrollAlpha > 0f) { + int x; + int y; + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + // Calculate the position for the fast scroller popup Rect bgBounds = mFastScrollerBg.getBounds(); if (isRtl) { @@ -288,52 +288,50 @@ public class AppsContainerRecyclerView extends RecyclerView */ private String scrollToPositionAtProgress(float progress) { List sections = mApps.getSections(); - // Get the total number of rows - int rowCount = getNumRows(); + if (sections.isEmpty()) { + return ""; + } // Find the position of the first application in the section that contains the row at the // current progress - int rowAtProgress = (int) (progress * rowCount); - int appIndex = 0; - rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); - if (rowCount + numRowsInSection > rowAtProgress) { + int rowAtProgress = (int) (progress * getNumRows()); + int rowCount = 0; + AlphabeticalAppsList.SectionInfo lastSectionInfo = null; + for (AlphabeticalAppsList.SectionInfo section : sections) { + int numRowsInSection = (int) Math.ceil((float) section.numAppsInSection / mNumAppsPerRow); + if (rowCount + numRowsInSection >= rowAtProgress) { + lastSectionInfo = section; break; } rowCount += numRowsInSection; - appIndex += info.numAppsInSection; } - appIndex = Math.max(0, Math.min(mApps.getAppsWithoutSectionBreaks().size() - 1, appIndex)); - AppInfo appInfo = mApps.getAppsWithoutSectionBreaks().get(appIndex); - int sectionedAppIndex = mApps.getApps().indexOf(appInfo); + int position = mApps.getAdapterItems().indexOf(lastSectionInfo.firstAppItem); // Scroll the position into view, anchored at the top of the screen if possible. We call the // scroll method on the LayoutManager directly since it is not exposed by RecyclerView. LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); stopScroll(); - layoutManager.scrollToPositionWithOffset(sectionedAppIndex, 0); + layoutManager.scrollToPositionWithOffset(position, 0); // Return the section name of the row - return mApps.getSectionNameForApp(appInfo); + return mApps.getAdapterItems().get(position).sectionName; } /** * Returns the bounds for the scrollbar. */ private void updateVerticalScrollbarBounds() { - int x; - int y; - boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == - LAYOUT_DIRECTION_RTL); - // Skip early if there are no items - if (mApps.getApps().isEmpty()) { + if (mApps.getAdapterItems().isEmpty()) { mVerticalScrollbarBounds.setEmpty(); return; } // Find the index and height of the first visible row (all rows have the same height) + int x; + int y; + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); int rowIndex = -1; int rowTopOffset = -1; int rowHeight = -1; @@ -341,12 +339,11 @@ public class AppsContainerRecyclerView extends RecyclerView int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - int position = getChildPosition(child); + int position = getChildAdapterPosition(child); if (position != NO_POSITION) { - AppInfo info = mApps.getApps().get(position); - if (info != AlphabeticalAppsList.SECTION_BREAK_INFO) { - int appIndex = mApps.getAppsWithoutSectionBreaks().indexOf(info); - rowIndex = findRowForAppIndex(appIndex); + AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); + if (!item.isSectionHeader) { + rowIndex = findRowForAppIndex(item.appIndex); rowTopOffset = getLayoutManager().getDecoratedTop(child); rowHeight = child.getHeight(); break; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 06fe93c1f..2de45cbcc 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -21,7 +21,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.text.Editable; -import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.KeyEvent; @@ -319,9 +318,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett final String filterText = s.toString().toLowerCase().replaceAll("\\s+", ""); mApps.setFilter(new AlphabeticalAppsList.Filter() { @Override - public boolean retainApp(AppInfo info) { + public boolean retainApp(AppInfo info, String sectionName) { String title = info.title.toString(); - String sectionName = mApps.getSectionNameForApp(info); return sectionName.toLowerCase().contains(filterText) || title.toLowerCase().replaceAll("\\s+", "").contains(filterText); } @@ -332,15 +330,22 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { - List appsWithoutSections = mApps.getAppsWithoutSectionBreaks(); - List apps = mApps.getApps(); - if (appsWithoutSections.size() == 1) { - mAppsListView.getChildAt(apps.indexOf(appsWithoutSections.get(0))).performClick(); - InputMethodManager imm = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(getWindowToken(), 0); + // Skip the quick-launch if there isn't exactly one item + if (mApps.getSize() != 1) { + return false; + } + + List items = mApps.getAdapterItems(); + for (int i = 0; i < items.size(); i++) { + AlphabeticalAppsList.AdapterItem item = items.get(i); + if (!item.isSectionHeader) { + mAppsListView.getChildAt(i).performClick(); + InputMethodManager imm = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getWindowToken(), 0); + return true; + } } - return true; } return false; } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 5895cbf08..96d971669 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -12,9 +12,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.util.Thunk; +import java.util.List; + /** * The grid view adapter of all the apps. @@ -54,8 +55,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { return mAppsPerRow; } - AppInfo info = mApps.getApps().get(position); - if (info == AlphabeticalAppsList.SECTION_BREAK_INFO) { + if (mApps.getAdapterItems().get(position).isSectionHeader) { // Section break spans full width return mAppsPerRow; } else { @@ -71,6 +71,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + List items = mApps.getAdapterItems(); for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); @@ -78,11 +79,11 @@ class AppsGridAdapter extends RecyclerView.Adapter { GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) child.getLayoutParams(); if (!holder.mIsSectionRow && !holder.mIsEmptyRow && !lp.isItemRemoved()) { - if (mApps.getApps().get(holder.getPosition() - 1) == - AlphabeticalAppsList.SECTION_BREAK_INFO) { + if (items.get(holder.getAdapterPosition() - 1).isSectionHeader) { // Draw at the parent - AppInfo info = mApps.getApps().get(holder.getPosition()); - String section = mApps.getSectionNameForApp(info); + AlphabeticalAppsList.AdapterItem item = + items.get(holder.getAdapterPosition()); + String section = item.sectionName; mSectionTextPaint.getTextBounds(section, 0, section.length(), mTmpBounds); if (mIsRtl) { @@ -212,7 +213,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { public void onBindViewHolder(ViewHolder holder, int position) { switch (holder.getItemViewType()) { case ICON_VIEW_TYPE: - AppInfo info = mApps.getApps().get(position); + AppInfo info = mApps.getAdapterItems().get(position).appInfo; BubbleTextView icon = (BubbleTextView) holder.mContent; icon.applyFromApplicationInfo(info); break; @@ -229,14 +230,14 @@ class AppsGridAdapter extends RecyclerView.Adapter { // For the empty view return 1; } - return mApps.getApps().size(); + return mApps.getAdapterItems().size(); } @Override public int getItemViewType(int position) { if (mApps.hasNoFilteredResults()) { return EMPTY_VIEW_TYPE; - } else if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { + } else if (mApps.getAdapterItems().get(position).isSectionHeader) { return SECTION_BREAK_VIEW_TYPE; } return ICON_VIEW_TYPE; diff --git a/src/com/android/launcher3/AppsListAdapter.java b/src/com/android/launcher3/AppsListAdapter.java index e1f4d3578..ffd309261 100644 --- a/src/com/android/launcher3/AppsListAdapter.java +++ b/src/com/android/launcher3/AppsListAdapter.java @@ -9,7 +9,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; -import com.android.launcher3.compat.AlphabeticIndexCompat; /** * The linear list view adapter for all the apps. @@ -93,15 +92,16 @@ class AppsListAdapter extends RecyclerView.Adapter { public void onBindViewHolder(ViewHolder holder, int position) { switch (holder.getItemViewType()) { case ICON_VIEW_TYPE: - AppInfo info = mApps.getApps().get(position); + AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); ViewGroup content = (ViewGroup) holder.mContent; - String sectionDescription = mApps.getSectionNameForApp(info); + String sectionDescription = item.sectionName; // Bind the section header boolean showSectionHeader = true; if (position > 0) { - AppInfo prevInfo = mApps.getApps().get(position - 1); - showSectionHeader = (prevInfo == AlphabeticalAppsList.SECTION_BREAK_INFO); + AlphabeticalAppsList.AdapterItem prevItem = + mApps.getAdapterItems().get(position - 1); + showSectionHeader = prevItem.isSectionHeader; } TextView tv = (TextView) content.findViewById(R.id.section); if (showSectionHeader) { @@ -113,7 +113,7 @@ class AppsListAdapter extends RecyclerView.Adapter { // Bind the icon BubbleTextView icon = (BubbleTextView) content.getChildAt(1); - icon.applyFromApplicationInfo(info); + icon.applyFromApplicationInfo(item.appInfo); break; case EMPTY_VIEW_TYPE: TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text); @@ -128,14 +128,14 @@ class AppsListAdapter extends RecyclerView.Adapter { // For the empty view return 1; } - return mApps.getApps().size(); + return mApps.getAdapterItems().size(); } @Override public int getItemViewType(int position) { if (mApps.hasNoFilteredResults()) { return EMPTY_VIEW_TYPE; - } else if (mApps.getApps().get(position) == AlphabeticalAppsList.SECTION_BREAK_INFO) { + } else if (mApps.getAdapterItems().get(position).isSectionHeader) { return SECTION_BREAK_VIEW_TYPE; } return ICON_VIEW_TYPE; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 50549cad0..ae6ebba34 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -388,11 +388,6 @@ public class BubbleTextView extends TextView { } } - @Override - protected boolean onSetAlpha(int alpha) { - return true; - } - @Override public void cancelLongPress() { super.cancelLongPress(); -- cgit v1.2.3 From 3b0883fcda88dd192549055a387fb41e1c2c17ad Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 7 Apr 2015 09:27:07 -0700 Subject: Fixing some RTL issues with scrollable folder > folder name alingment > scroll hint direction > Fake animation direction for icons changing pages Change-Id: Ia17ab2861a6d72c876806427e2de1682976b7671 --- src/com/android/launcher3/Folder.java | 67 +++++++++++--------------- src/com/android/launcher3/FolderPagedView.java | 18 +++++-- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 4529a9459..1e1d1eeb4 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -76,11 +76,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList static final int STATE_ANIMATING = 1; static final int STATE_OPEN = 2; - /** - * Fraction of the width to scroll when showing the next page hint. - */ - private static final float SCROLL_HINT_FRACTION = 0.07f; - /** * Time for which the scroll hint is shown before automatically changing page. */ @@ -656,42 +651,17 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList float x = r[0]; int currentPage = mPagedView.getNextPage(); - int cellWidth = mPagedView.getCurrentCellLayout().getCellWidth(); - if (currentPage > 0 && x < cellWidth * ICON_OVERSCROLL_WIDTH_FACTOR) { - // Show scroll hint on the left - if (mScrollHintDir != DragController.SCROLL_LEFT) { - mPagedView.showScrollHint(-SCROLL_HINT_FRACTION); - mScrollHintDir = DragController.SCROLL_LEFT; - } - - // Set alarm for when the hint is complete - if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != DragController.SCROLL_LEFT) { - mCurrentScrollDir = DragController.SCROLL_LEFT; - mOnScrollHintAlarm.cancelAlarm(); - mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d)); - mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION); - mReorderAlarm.cancelAlarm(); - mTargetRank = mEmptyCellRank; - } - } else if (currentPage < (mPagedView.getPageCount() - 1) && - (x > (getWidth() - cellWidth * ICON_OVERSCROLL_WIDTH_FACTOR))) { - // Show scroll hint on the right - if (mScrollHintDir != DragController.SCROLL_RIGHT) { - mPagedView.showScrollHint(SCROLL_HINT_FRACTION); - mScrollHintDir = DragController.SCROLL_RIGHT; - } - - // Set alarm for when the hint is complete - if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != DragController.SCROLL_RIGHT) { - mCurrentScrollDir = DragController.SCROLL_RIGHT; - mOnScrollHintAlarm.cancelAlarm(); - mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d)); - mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION); + float cellOverlap = mPagedView.getCurrentCellLayout().getCellWidth() + * ICON_OVERSCROLL_WIDTH_FACTOR; + boolean isOutsideLeftEdge = x < cellOverlap; + boolean isOutsideRightEdge = x > (getWidth() - cellOverlap); - mReorderAlarm.cancelAlarm(); - mTargetRank = mEmptyCellRank; - } + if (currentPage > 0 && (mPagedView.rtlLayout ? isOutsideRightEdge : isOutsideLeftEdge)) { + showScrollHint(DragController.SCROLL_LEFT, d); + } else if (currentPage < (mPagedView.getPageCount() - 1) + && (mPagedView.rtlLayout ? isOutsideLeftEdge : isOutsideRightEdge)) { + showScrollHint(DragController.SCROLL_RIGHT, d); } else { mOnScrollHintAlarm.cancelAlarm(); if (mScrollHintDir != DragController.SCROLL_NONE) { @@ -701,6 +671,25 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } + private void showScrollHint(int direction, DragObject d) { + // Show scroll hint on the right + if (mScrollHintDir != direction) { + mPagedView.showScrollHint(direction); + mScrollHintDir = direction; + } + + // Set alarm for when the hint is complete + if (!mOnScrollHintAlarm.alarmPending() || mCurrentScrollDir != direction) { + mCurrentScrollDir = direction; + mOnScrollHintAlarm.cancelAlarm(); + mOnScrollHintAlarm.setOnAlarmListener(new OnScrollHintListener(d)); + mOnScrollHintAlarm.setAlarm(SCROLL_HINT_DURATION); + + mReorderAlarm.cancelAlarm(); + mTargetRank = mEmptyCellRank; + } + } + OnAlarmListener mOnExitAlarmListener = new OnAlarmListener() { public void onAlarm(Alarm alarm) { completeDragExit(); diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 61ad10db4..1c42d2592 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; +import android.util.LayoutDirection; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -53,11 +54,18 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { private static final int SORT_ANIM_HIDE_DURATION = 130; private static final int SORT_ANIM_SHOW_DURATION = 160; + /** + * Fraction of the width to scroll when showing the next page hint. + */ + private static final float SCROLL_HINT_FRACTION = 0.07f; + private static final int[] sTempPosArray = new int[2]; // TODO: Remove this restriction private static final int MAX_ITEMS_PER_PAGE = 4; + public final boolean rtlLayout; + private final LayoutInflater mInflater; private final IconCache mIconCache; @@ -94,6 +102,8 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { mInflater = LayoutInflater.from(context); mIconCache = app.getIconCache(); + + rtlLayout = getResources().getConfiguration().getLayoutDirection() == LayoutDirection.RTL; } @Override @@ -484,7 +494,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { if (getPageCount() > 1) { mPageIndicator.setVisibility(View.VISIBLE); mSortButton.setVisibility(View.VISIBLE); - mFolder.mFolderName.setGravity(Gravity.START); + mFolder.mFolderName.setGravity(rtlLayout ? Gravity.RIGHT : Gravity.LEFT); setEnableOverscroll(true); } else { mPageIndicator.setVisibility(View.GONE); @@ -611,7 +621,9 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { /** * Scrolls the current view by a fraction */ - public void showScrollHint(float fraction) { + public void showScrollHint(int direction) { + float fraction = (direction == DragController.SCROLL_LEFT) ^ rtlLayout + ? -SCROLL_HINT_FRACTION : SCROLL_HINT_FRACTION; int hint = (int) (fraction * getWidth()); int scroll = getScrollForPage(getNextPage()) + hint; int delta = scroll - mUnboundedScrollX; @@ -761,7 +773,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } }; v.animate() - .translationXBy(direction > 0 ? -v.getWidth() : v.getWidth()) + .translationXBy((direction > 0 ^ rtlLayout) ? -v.getWidth() : v.getWidth()) .setDuration(REORDER_ANIMATION_DURATION) .setStartDelay(0) .withEndAction(endAction); -- cgit v1.2.3 From 1c7c49331e22ba0bc1b54e72735364a946bf24e7 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 7 Apr 2015 13:45:29 -0700 Subject: Fixing issue with building velvet against lower support lib. --- src/com/android/launcher3/AppsContainerRecyclerView.java | 2 +- src/com/android/launcher3/AppsGridAdapter.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 63e7fe4ac..c93bacf93 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -339,7 +339,7 @@ public class AppsContainerRecyclerView extends RecyclerView int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - int position = getChildAdapterPosition(child); + int position = getChildPosition(child); if (position != NO_POSITION) { AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); if (!item.isSectionHeader) { diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 96d971669..5b6967c26 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -79,10 +79,10 @@ class AppsGridAdapter extends RecyclerView.Adapter { GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) child.getLayoutParams(); if (!holder.mIsSectionRow && !holder.mIsEmptyRow && !lp.isItemRemoved()) { - if (items.get(holder.getAdapterPosition() - 1).isSectionHeader) { + if (items.get(holder.getPosition() - 1).isSectionHeader) { // Draw at the parent AlphabeticalAppsList.AdapterItem item = - items.get(holder.getAdapterPosition()); + items.get(holder.getPosition()); String section = item.sectionName; mSectionTextPaint.getTextBounds(section, 0, section.length(), mTmpBounds); -- cgit v1.2.3 From c624a26aa37bfd89ccc103b949a235fe765c5089 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 8 Apr 2015 09:22:45 -0700 Subject: Removing unused receivers Bug: 20068934 Change-Id: Ie08055ba964ebe4fe2c2a9ec39500d894d82f591 --- res/values/strings.xml | 5 ---- .../android/launcher3/PackageChangedReceiver.java | 13 --------- .../launcher3/UninstallShortcutReceiver.java | 27 ------------------ .../android/launcher3/UserInitializeReceiver.java | 32 ---------------------- 4 files changed, 77 deletions(-) delete mode 100644 src/com/android/launcher3/PackageChangedReceiver.java delete mode 100644 src/com/android/launcher3/UninstallShortcutReceiver.java delete mode 100644 src/com/android/launcher3/UserInitializeReceiver.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 7f79b984c..1b58d75b9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -174,11 +174,6 @@ s --> Allows an app to add shortcuts without user intervention. - uninstall shortcuts - - Allows the app to remove - shortcuts without user intervention. - read Home settings and shortcuts Allows the app to read the settings and diff --git a/src/com/android/launcher3/PackageChangedReceiver.java b/src/com/android/launcher3/PackageChangedReceiver.java deleted file mode 100644 index b98f47272..000000000 --- a/src/com/android/launcher3/PackageChangedReceiver.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.android.launcher3; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -// TODO: Remove this -public class PackageChangedReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, Intent intent) { - - } -} diff --git a/src/com/android/launcher3/UninstallShortcutReceiver.java b/src/com/android/launcher3/UninstallShortcutReceiver.java deleted file mode 100644 index 59e4cb591..000000000 --- a/src/com/android/launcher3/UninstallShortcutReceiver.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2008 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.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -//TODO: Remove this -public class UninstallShortcutReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent data) { } -} diff --git a/src/com/android/launcher3/UserInitializeReceiver.java b/src/com/android/launcher3/UserInitializeReceiver.java deleted file mode 100644 index d8e17b12f..000000000 --- a/src/com/android/launcher3/UserInitializeReceiver.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2012 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.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -/** - * Takes care of setting initial wallpaper for a user, by selecting the - * first wallpaper that is not in use by another user. - */ -public class UserInitializeReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // TODO: initial wallpaper now that wallpapers are owned by another app - } -} -- cgit v1.2.3 From 059228ad8e99658d6007c0f7f8bc205245d2dc9e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 7 Apr 2015 15:30:26 -0700 Subject: Fixing focus indicator position when the overlay page has different size than the current page. Bug: 20104483 Change-Id: Ib79d1bc4eeffc03b706ad6d77d8285b8a7f1188a --- src/com/android/launcher3/FocusIndicatorView.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java index ab21c90e6..ecf93e4b3 100644 --- a/src/com/android/launcher3/FocusIndicatorView.java +++ b/src/com/android/launcher3/FocusIndicatorView.java @@ -149,7 +149,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen } /** - * Computes the location of a view relative to {@link #mCommonParent}, off-setting + * Computes the location of a view relative to {@param parent}, off-setting * any shift due to page view scroll. * @param pos an array of two integers in which to hold the coordinates */ @@ -166,12 +166,12 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen private static void computeLocationRelativeToParentHelper(View child, View commonParent, int[] shift) { View parent = (View) child.getParent(); - if (parent instanceof PagedView) { - child = ((PagedView) parent).getPageAt(0); - } - shift[0] += child.getLeft(); shift[1] += child.getTop(); + if (parent instanceof PagedView) { + PagedView page = (PagedView) parent; + shift[0] -= page.getScrollForPage(page.indexOfChild(child)); + } if (parent != commonParent) { computeLocationRelativeToParentHelper(parent, commonParent, shift); -- cgit v1.2.3 From 7f06ae5c8b5c4d617797813b1bd04ee479e9bcff Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 8 Apr 2015 11:08:27 -0700 Subject: Removing Pools.java as it is present in support lib Change-Id: I0f1346223aa289889dbefedd47bff093e9c2c05d --- .../src/com/android/photos/views/Pools.java | 165 --------------------- .../android/photos/views/TiledImageRenderer.java | 4 +- 2 files changed, 2 insertions(+), 167 deletions(-) delete mode 100644 WallpaperPicker/src/com/android/photos/views/Pools.java diff --git a/WallpaperPicker/src/com/android/photos/views/Pools.java b/WallpaperPicker/src/com/android/photos/views/Pools.java deleted file mode 100644 index c60f2f013..000000000 --- a/WallpaperPicker/src/com/android/photos/views/Pools.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2009 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.photos.views; - -/** - * Helper class for crating pools of objects. An example use looks like this: - *
- * public class MyPooledClass {
- *
- *     private static final SynchronizedPool sPool =
- *             new SynchronizedPool(10);
- *
- *     public static MyPooledClass obtain() {
- *         MyPooledClass instance = sPool.acquire();
- *         return (instance != null) ? instance : new MyPooledClass();
- *     }
- *
- *     public void recycle() {
- *          // Clear state if needed.
- *          sPool.release(this);
- *     }
- *
- *     . . .
- * }
- * 
- * - * @hide - */ -public final class Pools { - - /** - * Interface for managing a pool of objects. - * - * @param The pooled type. - */ - public static interface Pool { - - /** - * @return An instance from the pool if such, null otherwise. - */ - public T acquire(); - - /** - * Release an instance to the pool. - * - * @param instance The instance to release. - * @return Whether the instance was put in the pool. - * - * @throws IllegalStateException If the instance is already in the pool. - */ - public boolean release(T instance); - } - - private Pools() { - /* do nothing - hiding constructor */ - } - - /** - * Simple (non-synchronized) pool of objects. - * - * @param The pooled type. - */ - public static class SimplePool implements Pool { - private final Object[] mPool; - - private int mPoolSize; - - /** - * Creates a new instance. - * - * @param maxPoolSize The max pool size. - * - * @throws IllegalArgumentException If the max pool size is less than zero. - */ - public SimplePool(int maxPoolSize) { - if (maxPoolSize <= 0) { - throw new IllegalArgumentException("The max pool size must be > 0"); - } - mPool = new Object[maxPoolSize]; - } - - @Override - @SuppressWarnings("unchecked") - public T acquire() { - if (mPoolSize > 0) { - final int lastPooledIndex = mPoolSize - 1; - T instance = (T) mPool[lastPooledIndex]; - mPool[lastPooledIndex] = null; - mPoolSize--; - return instance; - } - return null; - } - - @Override - public boolean release(T instance) { - if (isInPool(instance)) { - throw new IllegalStateException("Already in the pool!"); - } - if (mPoolSize < mPool.length) { - mPool[mPoolSize] = instance; - mPoolSize++; - return true; - } - return false; - } - - private boolean isInPool(T instance) { - for (int i = 0; i < mPoolSize; i++) { - if (mPool[i] == instance) { - return true; - } - } - return false; - } - } - - /** - * Synchronized) pool of objects. - * - * @param The pooled type. - */ - public static class SynchronizedPool extends SimplePool { - private final Object mLock = new Object(); - - /** - * Creates a new instance. - * - * @param maxPoolSize The max pool size. - * - * @throws IllegalArgumentException If the max pool size is less than zero. - */ - public SynchronizedPool(int maxPoolSize) { - super(maxPoolSize); - } - - @Override - public T acquire() { - synchronized (mLock) { - return super.acquire(); - } - } - - @Override - public boolean release(T element) { - synchronized (mLock) { - return super.release(element); - } - } - } -} \ No newline at end of file diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java index 39a73b9d5..e57ce70b9 100644 --- a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java @@ -20,6 +20,8 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.RectF; +import android.support.v4.util.Pools.Pool; +import android.support.v4.util.Pools.SynchronizedPool; import android.util.DisplayMetrics; import android.util.Log; import android.util.LongSparseArray; @@ -31,8 +33,6 @@ import com.android.gallery3d.glrenderer.BasicTexture; import com.android.gallery3d.glrenderer.GLCanvas; import com.android.gallery3d.glrenderer.UploadedTexture; import com.android.launcher3.util.Thunk; -import com.android.photos.views.Pools.Pool; -import com.android.photos.views.Pools.SynchronizedPool; /** * Handles laying out, decoding, and drawing of tiles in GL -- cgit v1.2.3 From bd808530dbfa765a33fa4fbfce46d1e0fcfcc5ca Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 8 Apr 2015 11:07:53 -0700 Subject: Temporarily tweaking the all apps layout to fit the screen on tablets. Change-Id: I2f8fe8e39a39cd6a055f22696881c51be7624832 --- res/layout-sw600dp/apps_view.xml | 11 ++++++----- res/values-sw600dp/dimens.xml | 4 +++- res/values/dimens.xml | 1 + src/com/android/launcher3/DeviceProfile.java | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml index 6f22460fa..fba170b2e 100644 --- a/res/layout-sw600dp/apps_view.xml +++ b/res/layout-sw600dp/apps_view.xml @@ -18,16 +18,17 @@ android:id="@+id/apps_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#22000000" + android:padding="@dimen/apps_container_inset" + android:background="@drawable/apps_customize_bg" android:descendantFocusability="afterDescendants"> \ No newline at end of file diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index d9075872a..d80f18c9e 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -18,7 +18,9 @@ 64dp - 480dp + 24dp + 64dp + 26sp 76dp diff --git a/res/values/dimens.xml b/res/values/dimens.xml index c327ec2d4..735373d69 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -48,6 +48,7 @@ 0dp + 0dp 8dp 52dp 64dp diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index b4d225e8b..2d07b8436 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -429,7 +429,7 @@ public class DeviceProfile { int availableAppsWidthPx = (appsContainerViewPx > 0) ? appsContainerViewPx : availableWidthPx; appsViewNumCols = (availableAppsWidthPx - appsViewLeftMarginPx) / - (allAppsCellWidthPx + allAppsCellPaddingPx); + (allAppsCellWidthPx + 2 * allAppsCellPaddingPx); } void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx, -- cgit v1.2.3 From 47a4f5882dedd019141fbaf3c718898b75efbc39 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 8 Apr 2015 13:20:51 -0700 Subject: update .gitignore config file so that we do not have to scroll through the unstaged items to pick which one to stage. Change-Id: I317e95fd74455999c957bf5434a8c7cb78afc3b2 --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4d5667e54..aea5d6102 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ db_files *.iml .project .classpath +.project.properties gen/ tests/stress/gen/ WallpaperPicker/gen/ +WallpaperPicker/.project.properties +bin/ -- cgit v1.2.3 From 3f471440a8b6b71d4c15501a96befd3b715c9e8f Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 8 Apr 2015 19:01:34 -0700 Subject: WidgetTray revamp work - RecyclerView is rendering - Animation is connected - Drag and drop is now handled - UI tweaking: background, margins, more to come. - Flicker and preview not loading issue: fixed height for the horizontal scroll view. - Shortcuts are added - Widget Preview loading should support shortPress for drop - UI tweaks left: overlay of arrow when there are more items to scroll - icons are added in the section header - Sorting of widget sections and widget horizontal list - Adding all the padding constants to dimen.xml file - RecyclerView should only support one view type For items to be addressed in follow up patches OR CLs, TODO is added to the comment. b/19897708 Change-Id: Ibfc4da1696a23d20bada93db46e126706eb13cdc --- res/layout-land/launcher.xml | 4 +- res/layout-port/launcher.xml | 4 +- res/layout-sw720dp/launcher.xml | 4 +- res/layout/apps_customize_pane.xml | 62 -- res/layout/apps_customize_widget.xml | 105 -- res/layout/apps_list_row_view.xml | 2 +- res/layout/widget_cell.xml | 85 ++ res/layout/widgets_list_row_view.xml | 89 ++ res/layout/widgets_view.xml | 49 + res/values-land/dimens.xml | 5 - res/values-sw600dp/dimens.xml | 8 +- res/values-v17/styles.xml | 4 +- res/values/colors.xml | 4 +- res/values/dimens.xml | 28 +- res/values/styles.xml | 4 +- src/com/android/launcher3/AppInfo.java | 13 +- .../android/launcher3/AppsCustomizePagedView.java | 1060 -------------------- .../android/launcher3/AppsCustomizeTabHost.java | 228 ----- src/com/android/launcher3/CellLayout.java | 4 +- src/com/android/launcher3/DeleteDropTarget.java | 15 +- src/com/android/launcher3/DeviceProfile.java | 62 +- src/com/android/launcher3/DragSource.java | 2 +- src/com/android/launcher3/FastBitmapDrawable.java | 4 +- src/com/android/launcher3/IconCache.java | 12 +- src/com/android/launcher3/Insettable.java | 4 + src/com/android/launcher3/ItemInfo.java | 14 +- src/com/android/launcher3/Launcher.java | 118 +-- src/com/android/launcher3/LauncherAppState.java | 2 +- .../launcher3/LauncherAppWidgetProviderInfo.java | 15 +- src/com/android/launcher3/LauncherModel.java | 2 +- src/com/android/launcher3/LauncherSettings.java | 8 +- .../LauncherStateTransitionAnimation.java | 56 +- src/com/android/launcher3/PagedViewGridLayout.java | 121 --- src/com/android/launcher3/PagedViewWidget.java | 295 ------ .../launcher3/PagedViewWidgetImageView.java | 49 - .../launcher3/PagedViewWithDraggableItems.java | 174 ---- src/com/android/launcher3/PendingAddItemInfo.java | 88 +- src/com/android/launcher3/WidgetPreviewLoader.java | 16 +- .../android/launcher3/WidgetsContainerView.java | 84 -- src/com/android/launcher3/Workspace.java | 2 + .../android/launcher3/widget/PackageItemInfo.java | 57 ++ .../launcher3/widget/PendingAddShortcutInfo.java | 44 + .../launcher3/widget/PendingAddWidgetInfo.java | 91 ++ src/com/android/launcher3/widget/WidgetCell.java | 338 +++++++ .../android/launcher3/widget/WidgetImageView.java | 48 + .../launcher3/widget/WidgetsContainerView.java | 376 +++++++ .../launcher3/widget/WidgetsListAdapter.java | 188 ++++ src/com/android/launcher3/widget/WidgetsModel.java | 136 +++ .../android/launcher3/widget/WidgetsRowView.java | 90 ++ .../launcher3/widget/WidgetsRowViewHolder.java | 36 + 50 files changed, 1786 insertions(+), 2523 deletions(-) delete mode 100644 res/layout/apps_customize_pane.xml delete mode 100644 res/layout/apps_customize_widget.xml create mode 100644 res/layout/widget_cell.xml create mode 100644 res/layout/widgets_list_row_view.xml create mode 100644 res/layout/widgets_view.xml delete mode 100644 src/com/android/launcher3/AppsCustomizePagedView.java delete mode 100644 src/com/android/launcher3/AppsCustomizeTabHost.java delete mode 100644 src/com/android/launcher3/PagedViewGridLayout.java delete mode 100644 src/com/android/launcher3/PagedViewWidget.java delete mode 100644 src/com/android/launcher3/PagedViewWidgetImageView.java delete mode 100644 src/com/android/launcher3/PagedViewWithDraggableItems.java delete mode 100644 src/com/android/launcher3/WidgetsContainerView.java create mode 100644 src/com/android/launcher3/widget/PackageItemInfo.java create mode 100644 src/com/android/launcher3/widget/PendingAddShortcutInfo.java create mode 100644 src/com/android/launcher3/widget/PendingAddWidgetInfo.java create mode 100644 src/com/android/launcher3/widget/WidgetCell.java create mode 100644 src/com/android/launcher3/widget/WidgetImageView.java create mode 100644 src/com/android/launcher3/widget/WidgetsContainerView.java create mode 100644 src/com/android/launcher3/widget/WidgetsListAdapter.java create mode 100644 src/com/android/launcher3/widget/WidgetsModel.java create mode 100644 src/com/android/launcher3/widget/WidgetsRowView.java create mode 100644 src/com/android/launcher3/widget/WidgetsRowViewHolder.java diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index b13984a26..d5dd91ab4 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -57,8 +57,8 @@ android:id="@+id/overview_panel" android:visibility="gone" /> - diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml index 3cb338efe..5a018c516 100644 --- a/res/layout-port/launcher.xml +++ b/res/layout-port/launcher.xml @@ -66,8 +66,8 @@ android:id="@+id/search_drop_target_bar" layout="@layout/search_drop_target_bar" /> - diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml index a3d502cf4..8bd827a25 100644 --- a/res/layout-sw720dp/launcher.xml +++ b/res/layout-sw720dp/launcher.xml @@ -66,8 +66,8 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> - diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml deleted file mode 100644 index e42576ffe..000000000 --- a/res/layout/apps_customize_pane.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/res/layout/apps_customize_widget.xml b/res/layout/apps_customize_widget.xml deleted file mode 100644 index a8344e3ff..000000000 --- a/res/layout/apps_customize_widget.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/layout/apps_list_row_view.xml b/res/layout/apps_list_row_view.xml index 83c175bb8..e80285b95 100644 --- a/res/layout/apps_list_row_view.xml +++ b/res/layout/apps_list_row_view.xml @@ -30,4 +30,4 @@ android:textColor="@color/apps_view_section_text_color" android:textSize="@dimen/apps_view_section_text_size" android:focusable="false" /> - \ No newline at end of file + diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml new file mode 100644 index 000000000..1286a622b --- /dev/null +++ b/res/layout/widget_cell.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml new file mode 100644 index 000000000..c7863c7c3 --- /dev/null +++ b/res/layout/widgets_list_row_view.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml new file mode 100644 index 000000000..8e7ed161a --- /dev/null +++ b/res/layout/widgets_view.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml index 1b3418154..06a99842e 100644 --- a/res/values-land/dimens.xml +++ b/res/values-land/dimens.xml @@ -18,9 +18,4 @@ 8dip 0dip - - - 42dp - 3 - 2 diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index d80f18c9e..13a1f4098 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -24,11 +24,9 @@ 76dp - 60dp - 8dp - 8dp - @dimen/app_widget_preview_padding_left - @dimen/app_widget_preview_padding_right + 8dp + @dimen/widget_preview_padding_left + @dimen/widget_preview_padding_right 400dp diff --git a/res/values-v17/styles.xml b/res/values-v17/styles.xml index 11d2a1f82..229375f85 100644 --- a/res/values-v17/styles.xml +++ b/res/values-v17/styles.xml @@ -1,6 +1,6 @@ - diff --git a/res/values/colors.xml b/res/values/colors.xml index 590a8872b..3a06bd95a 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -36,8 +36,10 @@ #FFFFFFFF #FF374248 - + #009688 #009688 + + #009688 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 735373d69..7c99278a8 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -60,18 +60,9 @@ 40dp - - 52dp - 0dp 48dp 0dp - - 12dp - 4dp - 16dp - 14dp 14sp @@ -85,12 +76,19 @@ should be. If 0, it will not be scaled at all. --> 12dp - - 16dp - 16dp - 32dp - 8dp - 8dp + + 8dp + 140dp + 16dp + 16dp + 8dp + 8dp + 8dp + + 52dp + 8dp + + 160dp 0dp diff --git a/res/values/styles.xml b/res/values/styles.xml index 77798f174..94efebc06 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -91,8 +91,8 @@ - diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index a1391b232..7c6b0664c 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -43,7 +43,7 @@ public class AppInfo extends ItemInfo { /** * A bitmap version of the application icon. */ - Bitmap iconBitmap; + public Bitmap iconBitmap; /** * Indicates whether we're using a low res icon @@ -55,7 +55,7 @@ public class AppInfo extends ItemInfo { */ long firstInstallTime; - ComponentName componentName; + public ComponentName componentName; static final int DOWNLOADED_FLAG = 1; static final int UPDATED_SYSTEM_APP_FLAG = 2; @@ -121,12 +121,15 @@ public class AppInfo extends ItemInfo { + " user=" + user + ")"; } + /** + * Helper method used for debugging. + */ public static void dumpApplicationInfoList(String tag, String label, ArrayList list) { Log.d(tag, label + " size=" + list.size()); for (AppInfo info: list) { - Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" - + info.iconBitmap + " firstInstallTime=" - + info.firstInstallTime); + Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap + + " firstInstallTime=" + info.firstInstallTime + + " componentName=" + info.componentName.getPackageName()); } } diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java deleted file mode 100644 index 58bcf1dbe..000000000 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ /dev/null @@ -1,1060 +0,0 @@ -/* - * Copyright (C) 2011 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.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.GridLayout; -import android.widget.ImageView; -import android.widget.Toast; - -import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.FocusHelper.PagedViewKeyListener; -import com.android.launcher3.compat.AppWidgetManagerCompat; -import com.android.launcher3.util.Thunk; - -import java.util.ArrayList; - -/** - * The Apps/Customize page that displays all the applications, widgets, and shortcuts. - */ -public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements - View.OnClickListener, DragSource, - PagedViewWidget.ShortPressListener, LauncherTransitionable { - static final String TAG = "AppsCustomizePagedView"; - - private static Rect sTmpRect = new Rect(); - private static final int[] sTempPosArray = new int[2]; - - /** - * The different content types that this paged view can show. - */ - public enum ContentType { - Widgets - } - private ContentType mContentType = ContentType.Widgets; - - // Refs - @Thunk Launcher mLauncher; - private DragController mDragController; - private final LayoutInflater mLayoutInflater; - private final PackageManager mPackageManager; - - // Save and Restore - private int mSaveInstanceStateItemIndex = -1; - - // Content - private ArrayList mWidgets; - - // Caching - private IconCache mIconCache; - - // Dimens - private int mContentWidth, mContentHeight; - @Thunk int mWidgetCountX, mWidgetCountY; - private int mNumWidgetPages; - - private final PagedViewKeyListener mKeyListener = new PagedViewKeyListener(); - - private Runnable mInflateWidgetRunnable = null; - private Runnable mBindWidgetRunnable = null; - static final int WIDGET_NO_CLEANUP_REQUIRED = -1; - static final int WIDGET_PRELOAD_PENDING = 0; - static final int WIDGET_BOUND = 1; - static final int WIDGET_INFLATED = 2; - int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; - int mWidgetLoadingId = -1; - PendingAddWidgetInfo mCreateWidgetInfo = null; - private boolean mDraggingWidget = false; - boolean mPageBackgroundsVisible = true; - - private Toast mWidgetInstructionToast; - - // Deferral of loading widget previews during launcher transitions - private boolean mInTransition; - - WidgetPreviewLoader mWidgetPreviewLoader; - - private boolean mInBulkBind; - private boolean mNeedToUpdatePageCountsAndInvalidateData; - - public AppsCustomizePagedView(Context context, AttributeSet attrs) { - super(context, attrs); - mLayoutInflater = LayoutInflater.from(context); - mPackageManager = context.getPackageManager(); - mWidgets = new ArrayList<>(); - mIconCache = (LauncherAppState.getInstance()).getIconCache(); - - // Save the default widget preview background - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); - mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); - mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); - a.recycle(); - - // The padding on the non-matched dimension for the default widget preview icons - // (top + bottom) - mFadeInAdjacentScreens = false; - - // Unless otherwise specified this view is important for accessibility. - if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - setSinglePageInViewport(); - } - - @Override - protected void init() { - super.init(); - mCenterPagesVertically = false; - - Context context = getContext(); - Resources r = context.getResources(); - setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f); - } - - public void onFinishInflate() { - super.onFinishInflate(); - - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setPadding(grid.edgeMarginPx, 2 * grid.edgeMarginPx, - grid.edgeMarginPx, 2 * grid.edgeMarginPx); - } - - void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) { - setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight); - } - - WidgetPreviewLoader getWidgetPreviewLoader() { - if (mWidgetPreviewLoader == null) { - mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache(); - } - return mWidgetPreviewLoader; - } - - /** Returns the item index of the center item on this page so that we can restore to this - * item index when we rotate. */ - private int getMiddleComponentIndexOnCurrentPage() { - int i = -1; - if (getPageCount() > 0) { - int currentPage = getCurrentPage(); - if (mContentType == ContentType.Widgets) { - PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage); - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - int childCount = layout.getChildCount(); - if (childCount > 0) { - i = (currentPage * numItemsPerPage) + (childCount / 2); - } - } else { - throw new RuntimeException("Invalid ContentType"); - } - } - return i; - } - - /** Get the index of the item to restore to if we need to restore the current page. */ - int getSaveInstanceStateIndex() { - if (mSaveInstanceStateItemIndex == -1) { - mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage(); - } - return mSaveInstanceStateItemIndex; - } - - /** Returns the page in the current orientation which is expected to contain the specified - * item index. */ - int getPageForComponent(int index) { - if (index < 0) return 0; - - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - return index / numItemsPerPage; - } - - /** Restores the page for an item at the specified index */ - void restorePageForIndex(int index) { - if (index < 0) return; - mSaveInstanceStateItemIndex = index; - } - - private void updatePageCounts() { - mNumWidgetPages = (int) Math.ceil(mWidgets.size() / - (float) (mWidgetCountX * mWidgetCountY)); - } - - protected void onDataReady(int width, int height) { - updatePageCounts(); - - // Force a measure to update recalculate the gaps - mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); - mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); - - final boolean hostIsTransitioning = getTabHost().isInTransition(); - int page = getPageForComponent(mSaveInstanceStateItemIndex); - invalidatePageData(Math.max(0, page), hostIsTransitioning); - } - - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - - if (!isDataReady()) { - if (!mWidgets.isEmpty()) { - post(new Runnable() { - // This code triggers requestLayout so must be posted outside of the - // layout pass. - public void run() { - if (Utilities.isViewAttachedToWindow(AppsCustomizePagedView.this)) { - setDataIsReady(); - onDataReady(getMeasuredWidth(), getMeasuredHeight()); - } - } - }); - } - } - } - - public void onPackagesUpdated(ArrayList widgetsAndShortcuts) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - // Get the list of widgets and shortcuts - mWidgets.clear(); - for (Object o : widgetsAndShortcuts) { - if (o instanceof LauncherAppWidgetProviderInfo) { - LauncherAppWidgetProviderInfo widget = (LauncherAppWidgetProviderInfo) o; - if (!app.shouldShowAppOrWidgetProvider(widget.provider) && !widget.isCustomWidget) { - continue; - } - - if (widget.minSpanX > 0 && widget.minSpanY > 0) { - // Ensure that all widgets we show can be added on a workspace of this size - int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget); - int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget); - int minSpanX = Math.min(spanXY[0], minSpanXY[0]); - int minSpanY = Math.min(spanXY[1], minSpanXY[1]); - if (minSpanX <= (int) grid.numColumns && - minSpanY <= (int) grid.numRows) { - mWidgets.add(widget); - } else { - Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" + - widget.minWidth + ", " + widget.minHeight + ")"); - } - } else { - Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" + - widget.minWidth + ", " + widget.minHeight + ")"); - } - } else { - // just add shortcuts - mWidgets.add(o); - } - } - - updatePageCountsAndInvalidateData(); - } - - public void setBulkBind(boolean bulkBind) { - if (bulkBind) { - mInBulkBind = true; - } else { - mInBulkBind = false; - if (mNeedToUpdatePageCountsAndInvalidateData) { - updatePageCountsAndInvalidateData(); - } - } - } - - private void updatePageCountsAndInvalidateData() { - if (mInBulkBind) { - mNeedToUpdatePageCountsAndInvalidateData = true; - } else { - updatePageCounts(); - invalidateOnDataChange(); - mNeedToUpdatePageCountsAndInvalidateData = false; - } - } - - @Override - public void onClick(View v) { - // When we have exited all apps or are in transition, disregard clicks - if (!mLauncher.isWidgetsViewVisible() - || mLauncher.getWorkspace().isSwitchingState() - || !(v instanceof PagedViewWidget)) return; - - // Let the user know that they have to long press to add a widget - if (mWidgetInstructionToast != null) { - mWidgetInstructionToast.cancel(); - } - mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add, - Toast.LENGTH_SHORT); - mWidgetInstructionToast.show(); - } - - /* - * PagedViewWithDraggableItems implementation - */ - @Override - protected void determineDraggingStart(android.view.MotionEvent ev) { - } - - static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { - Bundle options = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, sTmpRect); - Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher, - info.componentName, null); - - float density = launcher.getResources().getDisplayMetrics().density; - int xPaddingDips = (int) ((padding.left + padding.right) / density); - int yPaddingDips = (int) ((padding.top + padding.bottom) / density); - - options = new Bundle(); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, - sTmpRect.left - xPaddingDips); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, - sTmpRect.top - yPaddingDips); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, - sTmpRect.right - xPaddingDips); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, - sTmpRect.bottom - yPaddingDips); - } - return options; - } - - private void preloadWidget(final PendingAddWidgetInfo info) { - final LauncherAppWidgetProviderInfo pInfo = info.info; - final Bundle options = pInfo.isCustomWidget ? null : - getDefaultOptionsForWidget(mLauncher, info); - - if (pInfo.configure != null) { - info.bindOptions = options; - return; - } - - mWidgetCleanupState = WIDGET_PRELOAD_PENDING; - mBindWidgetRunnable = new Runnable() { - @Override - public void run() { - if (pInfo.isCustomWidget) { - mWidgetCleanupState = WIDGET_BOUND; - return; - } - - mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); - if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( - mWidgetLoadingId, pInfo, options)) { - mWidgetCleanupState = WIDGET_BOUND; - } - - } - }; - post(mBindWidgetRunnable); - - mInflateWidgetRunnable = new Runnable() { - @Override - public void run() { - if (mWidgetCleanupState != WIDGET_BOUND) { - return; - } - AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( - getContext(), mWidgetLoadingId, pInfo); - info.boundWidget = hostView; - mWidgetCleanupState = WIDGET_INFLATED; - hostView.setVisibility(INVISIBLE); - int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false); - - // We want the first widget layout to be the correct size. This will be important - // for width size reporting to the AppWidgetManager. - DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], - unScaledSize[1]); - lp.x = lp.y = 0; - lp.customPosition = true; - hostView.setLayoutParams(lp); - mLauncher.getDragLayer().addView(hostView); - } - }; - post(mInflateWidgetRunnable); - } - - @Override - public void onShortPress(View v) { - // We are anticipating a long press, and we use this time to load bind and instantiate - // the widget. This will need to be cleaned up if it turns out no long press occurs. - if (mCreateWidgetInfo != null) { - // Just in case the cleanup process wasn't properly executed. This shouldn't happen. - cleanupWidgetPreloading(false); - } - mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag()); - preloadWidget(mCreateWidgetInfo); - } - - private void cleanupWidgetPreloading(boolean widgetWasAdded) { - if (!widgetWasAdded) { - // If the widget was not added, we may need to do further cleanup. - PendingAddWidgetInfo info = mCreateWidgetInfo; - mCreateWidgetInfo = null; - - if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) { - // We never did any preloading, so just remove pending callbacks to do so - removeCallbacks(mBindWidgetRunnable); - removeCallbacks(mInflateWidgetRunnable); - } else if (mWidgetCleanupState == WIDGET_BOUND) { - // Delete the widget id which was allocated - if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); - } - - // We never got around to inflating the widget, so remove the callback to do so. - removeCallbacks(mInflateWidgetRunnable); - } else if (mWidgetCleanupState == WIDGET_INFLATED) { - // Delete the widget id which was allocated - if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); - } - - // The widget was inflated and added to the DragLayer -- remove it. - AppWidgetHostView widget = info.boundWidget; - mLauncher.getDragLayer().removeView(widget); - } - } - mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; - mWidgetLoadingId = -1; - mCreateWidgetInfo = null; - PagedViewWidget.resetShortPressTarget(); - } - - @Override - public void cleanUpShortPress(View v) { - if (!mDraggingWidget) { - cleanupWidgetPreloading(false); - } - } - - private boolean beginDraggingWidget(PagedViewWidget v) { - mDraggingWidget = true; - // Get the widget preview as the drag representation - ImageView image = (ImageView) v.findViewById(R.id.widget_preview); - PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); - - // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and - // we abort the drag. - if (image.getDrawable() == null) { - mDraggingWidget = false; - return false; - } - - // Compose the drag image - Bitmap preview; - Bitmap outline; - float scale = 1f; - Point previewPadding = null; - - if (createItemInfo instanceof PendingAddWidgetInfo) { - // This can happen in some weird cases involving multi-touch. We can't start dragging - // the widget if this is null, so we break out. - if (mCreateWidgetInfo == null) { - return false; - } - - PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo; - createItemInfo = createWidgetInfo; - int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true); - - FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); - float minScale = 1.25f; - int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); - - int[] previewSizeBeforeScale = new int[1]; - preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, - maxWidth, null, previewSizeBeforeScale); - // Compare the size of the drag preview to the preview in the AppsCustomize tray - int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], - v.getActualItemWidth()); - scale = previewWidthInAppsCustomize / (float) preview.getWidth(); - - // The bitmap in the AppsCustomize tray is always the the same size, so there - // might be extra pixels around the preview itself - this accounts for that - if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) { - int padding = - (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2; - previewPadding = new Point(padding, 0); - } - } else { - PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag(); - Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo); - preview = Utilities.createIconBitmap(icon, mLauncher); - createItemInfo.spanX = createItemInfo.spanY = 1; - } - - // Don't clip alpha values for the drag outline if we're using the default widget preview - boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo && - (((PendingAddWidgetInfo) createItemInfo).previewImage == 0)); - - // Save the preview for the outline generation, then dim the preview - outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(), - false); - - // Start the drag - mLauncher.lockScreenOrientation(); - mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha); - mDragController.startDrag(image, preview, this, createItemInfo, - DragController.DRAG_ACTION_COPY, previewPadding, scale); - outline.recycle(); - preview.recycle(); - return true; - } - - @Override - protected boolean beginDragging(final View v) { - if (!super.beginDragging(v)) return false; - - if (v instanceof PagedViewWidget) { - if (!beginDraggingWidget((PagedViewWidget) v)) { - return false; - } - } else { - Log.e(TAG, "Unexpected dragging view: " + v); - } - - // We delay entering spring-loaded mode slightly to make sure the UI - // thready is free of any work. - postDelayed(new Runnable() { - @Override - public void run() { - // We don't enter spring-loaded mode if the drag has been cancelled - if (mLauncher.getDragController().isDragging()) { - // Go into spring loaded mode (must happen before we startDrag()) - mLauncher.enterSpringLoadedDragMode(); - } - } - }, 150); - - return true; - } - - /** - * Clean up after dragging. - * - * @param target where the item was dragged to (can be null if the item was flung) - */ - private void endDragging(View target, boolean isFlingToDelete, boolean success) { - if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && - !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { - // Exit spring loaded mode if we have not successfully dropped or have not handled the - // drop in Workspace - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - mLauncher.unlockScreenOrientation(false); - } else { - mLauncher.unlockScreenOrientation(false); - } - } - - @Override - public View getContent() { - if (getChildCount() > 0) { - return getChildAt(0); - } - return null; - } - - @Override - public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - mInTransition = true; - if (toWorkspace) { - cancelAllTasks(false); - } - } - - @Override - public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { - } - - @Override - public void onLauncherTransitionStep(Launcher l, float t) { - } - - @Override - public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - mInTransition = false; - mForceDrawAllChildrenNextFrame = !toWorkspace; - if (!toWorkspace) { - loadPreviewsForPage(getNextPage()); - } - } - - @Override - public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, - boolean success) { - // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling - if (isFlingToDelete) return; - - endDragging(target, false, success); - - // Display an error message if the drag failed due to there not being enough space on the - // target layout we were dropping on. - if (!success) { - boolean showOutOfSpaceMessage = false; - if (target instanceof Workspace) { - int currentScreen = mLauncher.getCurrentWorkspaceScreen(); - Workspace workspace = (Workspace) target; - CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); - ItemInfo itemInfo = (ItemInfo) d.dragInfo; - if (layout != null) { - layout.calculateSpans(itemInfo); - showOutOfSpaceMessage = - !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); - } - } - if (showOutOfSpaceMessage) { - mLauncher.showOutOfSpaceMessage(false); - } - - d.deferDragViewCleanupPostAnimation = false; - } - cleanupWidgetPreloading(success); - mDraggingWidget = false; - } - - @Override - public void onFlingToDeleteCompleted() { - // We just dismiss the drag when we fling, so cleanup here - endDragging(null, true, true); - cleanupWidgetPreloading(false); - mDraggingWidget = false; - } - - @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override - public boolean supportsAppInfoDropTarget() { - return true; - } - - @Override - public boolean supportsDeleteDropTarget() { - return false; - } - - @Override - public float getIntrinsicIconScaleFactor() { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - return (float) grid.allAppsIconSizePx / grid.iconSizePx; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - cancelAllTasks(true); - } - - @Override - public void trimMemory() { - super.trimMemory(); - cancelAllTasks(true); - } - - private void cancelAllTasks(boolean clearCompletedTasks) { - for (int page = getPageCount() - 1; page >= 0; page--) { - final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - if (layout != null) { - for (int i = 0; i < layout.getChildCount(); i++) { - ((PagedViewWidget) layout.getChildAt(i)).deletePreview(clearCompletedTasks); - } - } - } - } - - - public void setContentType(ContentType type) { - // Widgets appear to be cleared every time you leave, always force invalidate for them - if (mContentType != type || type == ContentType.Widgets) { - int page = (mContentType != type) ? 0 : getCurrentPage(); - mContentType = type; - invalidatePageData(page, true); - } - } - - public ContentType getContentType() { - return mContentType; - } - - public void setPageBackgroundsVisible(boolean visible) { - mPageBackgroundsVisible = visible; - int childCount = getChildCount(); - for (int i = 0; i < childCount; ++i) { - Drawable bg = getChildAt(i).getBackground(); - if (bg != null) { - bg.setAlpha(visible ? 255 : 0); - } - } - } - - /* - * Widgets PagedView implementation - */ - private void setupPage(PagedViewGridLayout layout) { - // Note: We force a measure here to get around the fact that when we do layout calculations - // immediately after syncing, we don't have a proper width. - int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); - int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); - - Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel_dark); - if (bg != null) { - bg.setAlpha(mPageBackgroundsVisible ? 255 : 0); - layout.setBackground(bg); - } - layout.measure(widthSpec, heightSpec); - } - - public void syncWidgetPageItems(final int page, final boolean immediate) { - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - - final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - - // Calculate the dimensions of each cell we are giving to each widget - final ArrayList items = new ArrayList(); - int contentWidth = mContentWidth - layout.getPaddingLeft() - layout.getPaddingRight(); - final int cellWidth = contentWidth / mWidgetCountX; - int contentHeight = mContentHeight - layout.getPaddingTop() - layout.getPaddingBottom(); - - final int cellHeight = contentHeight / mWidgetCountY; - - // Prepare the set of widgets to load previews for in the background - int offset = page * numItemsPerPage; - for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) { - items.add(mWidgets.get(i)); - } - - // Prepopulate the pages with the other widget info, and fill in the previews later - layout.setColumnCount(layout.getCellCountX()); - for (int i = 0; i < items.size(); ++i) { - Object rawInfo = items.get(i); - PendingAddItemInfo createItemInfo = null; - PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( - R.layout.apps_customize_widget, layout, false); - - if (rawInfo instanceof LauncherAppWidgetProviderInfo) { - // Fill in the widget information - LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) rawInfo; - createItemInfo = new PendingAddWidgetInfo(info, null); - - widget.applyFromAppWidgetProviderInfo(info, -1, getWidgetPreviewLoader()); - widget.setTag(createItemInfo); - widget.setShortPressListener(this); - } else if (rawInfo instanceof ResolveInfo) { - // Fill in the shortcuts information - ResolveInfo info = (ResolveInfo) rawInfo; - createItemInfo = new PendingAddShortcutInfo(info.activityInfo); - createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; - createItemInfo.componentName = new ComponentName(info.activityInfo.packageName, - info.activityInfo.name); - widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader()); - widget.setTag(createItemInfo); - } - - widget.setOnClickListener(this); - widget.setOnLongClickListener(this); - widget.setOnTouchListener(this); - widget.setOnKeyListener(mKeyListener); - - // Layout each widget - int ix = i % mWidgetCountX; - int iy = i / mWidgetCountX; - - if (ix > 0) { - View border = widget.findViewById(R.id.left_border); - border.setVisibility(View.VISIBLE); - } - if (ix < mWidgetCountX - 1) { - View border = widget.findViewById(R.id.right_border); - border.setVisibility(View.VISIBLE); - } - - GridLayout.LayoutParams lp = new GridLayout.LayoutParams( - GridLayout.spec(iy, GridLayout.START), - GridLayout.spec(ix, GridLayout.TOP)); - lp.width = cellWidth; - lp.height = cellHeight; - lp.setGravity(Gravity.TOP | Gravity.START); - layout.addView(widget, lp); - } - - if (immediate && !mInTransition) { - loadPreviewsForPage(page); - } - } - - private void loadPreviewsForPage(int page) { - final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - - if (layout != null) { - for (int i = 0; i < layout.getChildCount(); i++) { - ((PagedViewWidget) layout.getChildAt(i)).ensurePreview(); - } - } - } - - @Override - public void syncPages() { - disablePagedViewAnimations(); - - removeAllViews(); - cancelAllTasks(true); - - Context context = getContext(); - if (mContentType == ContentType.Widgets) { - for (int j = 0; j < mNumWidgetPages; ++j) { - PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, - mWidgetCountY); - setupPage(layout); - addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT)); - } - } else { - throw new RuntimeException("Invalid ContentType"); - } - - enablePagedViewAnimations(); - } - - @Override - public void syncPageItems(int page, boolean immediate) { - if (mContentType == ContentType.Widgets) { - syncWidgetPageItems(page, immediate); - } else { - Log.e(TAG, "Unexpected ContentType"); - } - } - - // We want our pages to be z-ordered such that the further a page is to the left, the higher - // it is in the z-order. This is important to insure touch events are handled correctly. - View getPageAt(int index) { - return getChildAt(indexToPage(index)); - } - - @Override - protected int indexToPage(int index) { - return getChildCount() - index - 1; - } - - // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. - @Override - protected void screenScrolled(int screenCenter) { - super.screenScrolled(screenCenter); - enableHwLayersOnVisiblePages(); - } - - private void enableHwLayersOnVisiblePages() { - final int screenCount = getChildCount(); - - getVisiblePages(mTempVisiblePagesRange); - int leftScreen = mTempVisiblePagesRange[0]; - int rightScreen = mTempVisiblePagesRange[1]; - int forceDrawScreen = -1; - if (leftScreen == rightScreen) { - // make sure we're caching at least two pages always - if (rightScreen < screenCount - 1) { - rightScreen++; - forceDrawScreen = rightScreen; - } else if (leftScreen > 0) { - leftScreen--; - forceDrawScreen = leftScreen; - } - } else { - forceDrawScreen = leftScreen + 1; - } - - for (int i = 0; i < screenCount; i++) { - final View layout = (View) getPageAt(i); - if (!(leftScreen <= i && i <= rightScreen && - (i == forceDrawScreen || shouldDrawChild(layout)))) { - layout.setLayerType(LAYER_TYPE_NONE, null); - } - } - - for (int i = 0; i < screenCount; i++) { - final View layout = (View) getPageAt(i); - - if (leftScreen <= i && i <= rightScreen && - (i == forceDrawScreen || shouldDrawChild(layout))) { - if (layout.getLayerType() != LAYER_TYPE_HARDWARE) { - layout.setLayerType(LAYER_TYPE_HARDWARE, null); - } - } - } - } - - protected void overScroll(float amount) { - dampedOverScroll(amount); - } - - /** - * Used by the parent to get the content width to set the tab bar to - * @return - */ - public int getPageContentWidth() { - return mContentWidth; - } - - @Override - protected void onPageEndMoving() { - super.onPageEndMoving(); - mForceDrawAllChildrenNextFrame = true; - // We reset the save index when we change pages so that it will be recalculated on next - // rotation - mSaveInstanceStateItemIndex = -1; - } - - @Override - protected void onPageBeginMoving() { - super.onPageBeginMoving(); - if (!mInTransition) { - getVisiblePages(sTempPosArray); - for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) { - loadPreviewsForPage(i); - } - } - } - - /* - * AllAppsView implementation - */ - public void setup(Launcher launcher, DragController dragController) { - mLauncher = launcher; - mDragController = dragController; - } - - /** - * We should call thise method whenever the core data changes (mWidgets) so that we can - * appropriately determine when to invalidate the PagedView page data. In cases where the data - * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the - * next onMeasure() pass, which will trigger an invalidatePageData() itself. - */ - private void invalidateOnDataChange() { - if (!isDataReady()) { - // The next layout pass will trigger data-ready if both widgets and apps are set, so - // request a layout to trigger the page data when ready. - requestLayout(); - } else { - cancelAllTasks(false); - invalidatePageData(); - } - } - - public void reset() { - // If we have reset, then we should not continue to restore the previous state - mSaveInstanceStateItemIndex = -1; - - if (mContentType != ContentType.Widgets) { - setContentType(ContentType.Widgets); - } - - if (mCurrentPage != 0) { - invalidatePageData(0); - } - } - - private AppsCustomizeTabHost getTabHost() { - return (AppsCustomizeTabHost) mLauncher.findViewById(R.id.apps_customize_pane); - } - - public void dumpState() { - // TODO: Dump information related to current list of Applications, Widgets, etc. - dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets); - } - - private void dumpAppWidgetProviderInfoList(String tag, String label, - ArrayList list) { - Log.d(tag, label + " size=" + list.size()); - for (Object i: list) { - if (i instanceof AppWidgetProviderInfo) { - AppWidgetProviderInfo info = (AppWidgetProviderInfo) i; - Log.d(tag, " label=\"" + info.label + "\" previewImage=" + info.previewImage - + " resizeMode=" + info.resizeMode + " configure=" + info.configure - + " initialLayout=" + info.initialLayout - + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight); - } else if (i instanceof ResolveInfo) { - ResolveInfo info = (ResolveInfo) i; - Log.d(tag, " label=\"" + info.loadLabel(mPackageManager) + "\" icon=" - + info.icon); - } - } - } - - public void surrender() { - // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we - // should stop this now. - - // Stop all background tasks - cancelAllTasks(true); - } - - /* - * We load an extra page on each side to prevent flashes from scrolling and loading of the - * widget previews in the background with the AsyncTasks. - */ - final static int sLookBehindPageCount = 2; - final static int sLookAheadPageCount = 2; - protected int getAssociatedLowerPageBound(int page) { - final int count = getChildCount(); - int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1); - int windowMinIndex = Math.max(Math.min(page - sLookBehindPageCount, count - windowSize), 0); - return windowMinIndex; - } - protected int getAssociatedUpperPageBound(int page) { - final int count = getChildCount(); - int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1); - int windowMaxIndex = Math.min(Math.max(page + sLookAheadPageCount, windowSize - 1), - count - 1); - return windowMaxIndex; - } - - protected String getCurrentPageDescription() { - int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; - int stringId = R.string.default_scroll_format; - int count = 0; - - if (mContentType == ContentType.Widgets) { - stringId = R.string.apps_customize_widgets_scroll_format; - count = mNumWidgetPages; - } else { - throw new RuntimeException("Invalid ContentType"); - } - - return String.format(getContext().getString(stringId), page + 1, count); - } -} diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java deleted file mode 100644 index 5e2f05c61..000000000 --- a/src/com/android/launcher3/AppsCustomizeTabHost.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2011 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.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; -import android.widget.FrameLayout; - -public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable { - static final String LOG_TAG = "AppsCustomizeTabHost"; - - private static final String WIDGETS_TAB_TAG = "WIDGETS"; - - private AppsCustomizePagedView mPagedView; - private View mContent; - private boolean mInTransition = false; - - private final Rect mInsets = new Rect(); - - public AppsCustomizeTabHost(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Convenience methods to select specific tabs. We want to set the content type immediately - * in these cases, but we note that we still call setCurrentTabByTag() so that the tab view - * reflects the new content (but doesn't do the animation and logic associated with changing - * tabs manually). - */ - void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) { - mPagedView.setContentType(type); - } - - @Override - public void setInsets(Rect insets) { - mInsets.set(insets); - LayoutParams flp = (LayoutParams) mContent.getLayoutParams(); - flp.topMargin = insets.top; - flp.bottomMargin = insets.bottom; - flp.leftMargin = insets.left; - flp.rightMargin = insets.right; - mContent.setLayoutParams(flp); - } - - /** - * Setup the tab host and create all necessary tabs. - */ - @Override - protected void onFinishInflate() { - mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content); - mContent = findViewById(R.id.content); - } - - public String getContentTag() { - return getTabTagForContentType(mPagedView.getContentType()); - } - - /** - * Returns the content view used for the launcher transitions. - */ - public View getContentView() { - return findViewById(R.id.apps_customize_pane_content); - } - - /** - * Returns the reveal view used for the launcher transitions. - */ - public View getRevealView() { - return findViewById(R.id.fake_page); - } - - /** - * Returns the page indicators view. - */ - public View getPageIndicators() { - return findViewById(R.id.apps_customize_page_indicator); - } - - /** - * Returns the content type for the specified tab tag. - */ - public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) { - return AppsCustomizePagedView.ContentType.Widgets; - } - - /** - * Returns the tab tag for a given content type. - */ - public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) { - return WIDGETS_TAB_TAG; - } - - /** - * Disable focus on anything under this view in the hierarchy if we are not visible. - */ - @Override - public int getDescendantFocusability() { - if (getVisibility() != View.VISIBLE) { - return ViewGroup.FOCUS_BLOCK_DESCENDANTS; - } - return super.getDescendantFocusability(); - } - - void reset() { - // Reset immediately - mPagedView.reset(); - } - - void trimMemory() { - mPagedView.trimMemory(); - } - - public void onWindowVisible() { - if (getVisibility() == VISIBLE) { - mContent.setVisibility(VISIBLE); - // We unload the widget previews when the UI is hidden, so need to reload pages - // Load the current page synchronously, and the neighboring pages asynchronously - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true); - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); - } - } - @Override - public ViewGroup getContent() { - return mPagedView; - } - - public boolean isInTransition() { - return mInTransition; - } - - /* LauncherTransitionable overrides */ - @Override - public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace); - mInTransition = true; - - if (toWorkspace) { - // Going from All Apps -> Workspace - setVisibilityOfSiblingsWithLowerZOrder(VISIBLE); - } else { - // Going from Workspace -> All Apps - mContent.setVisibility(VISIBLE); - - // Make sure the current page is loaded (we start loading the side pages after the - // transition to prevent slowing down the animation) - // TODO: revisit this - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); - } - } - - @Override - public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { - mPagedView.onLauncherTransitionStart(l, animated, toWorkspace); - } - - @Override - public void onLauncherTransitionStep(Launcher l, float t) { - mPagedView.onLauncherTransitionStep(l, t); - } - - @Override - public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace); - mInTransition = false; - - if (!toWorkspace) { - // Make sure adjacent pages are loaded (we wait until after the transition to - // prevent slowing down the animation) - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); - - // Opening apps, need to announce what page we are on. - AccessibilityManager am = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - if (am.isEnabled()) { - // Notify the user when the page changes - announceForAccessibility(mPagedView.getCurrentPageDescription()); - } - - // Going from Workspace -> All Apps - // NOTE: We should do this at the end since we check visibility state in some of the - // cling initialization/dismiss code above. - setVisibilityOfSiblingsWithLowerZOrder(INVISIBLE); - } - } - - private void setVisibilityOfSiblingsWithLowerZOrder(int visibility) { - ViewGroup parent = (ViewGroup) getParent(); - if (parent == null) return; - - View appsView = ((Launcher) getContext()).getAppsView(); - View overviewPanel = ((Launcher) getContext()).getOverviewPanel(); - final int count = parent.getChildCount(); - if (!isChildrenDrawingOrderEnabled()) { - for (int i = 0; i < count; i++) { - final View child = parent.getChildAt(i); - if (child == this) { - break; - } else { - if (child.getVisibility() == GONE || child == overviewPanel || - child == appsView) { - continue; - } - child.setVisibility(visibility); - } - } - } else { - throw new RuntimeException("Failed; can't get z-order of views"); - } - } -} diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 63afa3091..f4afb954d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -45,7 +45,6 @@ import android.util.Log; import android.util.SparseArray; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; @@ -56,6 +55,7 @@ import android.view.animation.LayoutAnimationController; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.LauncherAccessibilityDelegate.DragType; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.ArrayList; import java.util.Arrays; @@ -3025,7 +3025,7 @@ public class CellLayout extends ViewGroup { * * @return True if a vacant cell of the specified dimension was found, false otherwise. */ - boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { + public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied); } diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 1f0dad221..62aa285ab 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -42,6 +42,7 @@ import android.view.animation.LinearInterpolator; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.WidgetsContainerView; public class DeleteDropTarget extends ButtonDropTarget { private static int DELETE_ANIMATION_DURATION = 285; @@ -100,8 +101,9 @@ public class DeleteDropTarget extends ButtonDropTarget { private boolean isAllAppsApplication(DragSource source, Object info) { return source.supportsAppInfoDropTarget() && (info instanceof AppInfo); } - private boolean isAllAppsWidget(DragSource source, Object info) { - if (source instanceof AppsCustomizePagedView) { + + private boolean isWidget(DragSource source, Object info) { + if (source instanceof WidgetsContainerView) { if (info instanceof PendingAddItemInfo) { PendingAddItemInfo addInfo = (PendingAddItemInfo) info; switch (addInfo.itemType) { @@ -173,7 +175,7 @@ public class DeleteDropTarget extends ButtonDropTarget { // If we are dragging an application from AppsCustomize, only show the control if we can // delete the app (it was downloaded), and rename the string to "uninstall" in such a case. // Hide the delete target if it is a widget from AppsCustomize. - if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) { + if (!willAcceptDrop(info) || isWidget(source, info)) { isVisible = false; } if (useUninstallLabel) { @@ -489,13 +491,14 @@ public class DeleteDropTarget extends ButtonDropTarget { } public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) { - final boolean isAllApps = d.dragSource instanceof AppsCustomizePagedView; + final boolean isWidgets = d.dragSource instanceof WidgetsContainerView; + final boolean isAllapps = d.dragSource instanceof AppsContainerView; // Don't highlight the icon as it's animating d.dragView.setColor(0); d.dragView.updateInitialScaleToCurrentScale(); // Don't highlight the target if we are flinging from AllApps - if (isAllApps) { + if (isWidgets || isAllapps) { resetHoverColor(); } @@ -545,7 +548,7 @@ public class DeleteDropTarget extends ButtonDropTarget { public void run() { // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up // itself, otherwise, complete the drop to initiate the deletion process - if (!isAllApps) { + if (!isWidgets || !isAllapps) { mLauncher.exitSpringLoadedDragMode(); completeDrop(d); } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 331695acc..ea2852080 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -106,8 +106,8 @@ public class DeviceProfile { public int cellWidthPx; public int cellHeightPx; - int iconSizePx; - int iconTextSizePx; + public int iconSizePx; + public int iconTextSizePx; int iconDrawablePaddingPx; int allAppsIconSizePx; int allAppsIconTextSizePx; @@ -803,64 +803,6 @@ public class DeviceProfile { } } - // Layout AllApps - AppsCustomizeTabHost host = (AppsCustomizeTabHost) - launcher.findViewById(R.id.apps_customize_pane); - if (host != null) { - // Center the all apps page indicator - int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f, - (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX))); - pageIndicator = host.findViewById(R.id.apps_customize_page_indicator); - if (pageIndicator != null) { - LinearLayout.LayoutParams lllp = (LinearLayout.LayoutParams) pageIndicator.getLayoutParams(); - lllp.width = LayoutParams.WRAP_CONTENT; - lllp.height = pageIndicatorHeight; - pageIndicator.setLayoutParams(lllp); - } - - AppsCustomizePagedView pagedView = (AppsCustomizePagedView) - host.findViewById(R.id.apps_customize_pane_content); - - FrameLayout fakePageContainer = (FrameLayout) - host.findViewById(R.id.fake_page_container); - FrameLayout fakePage = (FrameLayout) host.findViewById(R.id.fake_page); - - padding = new Rect(); - if (pagedView != null) { - // Constrain the dimensions of all apps so that it does not span the full width - int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) / - (2 * (allAppsNumCols + 1)); - int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) / - (2 * (allAppsNumRows + 1)); - paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f)); - paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f)); - int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR)); - int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2; - // Only adjust the side paddings on landscape phones, or tablets - if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) { - padding.left = padding.right = gridPaddingLR; - } - - // The icons are centered, so we can't just offset by the page indicator height - // because the empty space will actually be pageIndicatorHeight + paddingTB - padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB); - - pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight); - fakePage.setBackground(res.getDrawable(R.drawable.quantum_panel)); - - // Horizontal padding for the whole paged view - int pagedFixedViewPadding = - res.getDimensionPixelSize(R.dimen.apps_customize_horizontal_padding); - - padding.left += pagedFixedViewPadding; - padding.right += pagedFixedViewPadding; - - pagedView.setPadding(padding.left, padding.top, padding.right, padding.bottom); - fakePageContainer.setPadding(padding.left, padding.top, padding.right, padding.bottom); - - } - } - // Layout the Overview Mode ViewGroup overviewMode = launcher.getOverviewPanel(); if (overviewMode != null) { diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java index 7369eeac2..2a1346ef5 100644 --- a/src/com/android/launcher3/DragSource.java +++ b/src/com/android/launcher3/DragSource.java @@ -22,9 +22,9 @@ import com.android.launcher3.DropTarget.DragObject; /** * Interface defining an object that can originate a drag. - * */ public interface DragSource { + /** * @return whether items dragged from this source supports */ diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index ff02bbbc3..28e923e67 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -32,7 +32,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.SparseArray; -class FastBitmapDrawable extends Drawable { +public class FastBitmapDrawable extends Drawable { static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() { @@ -72,7 +72,7 @@ class FastBitmapDrawable extends Drawable { private boolean mPressed = false; private ObjectAnimator mPressedAnimator; - FastBitmapDrawable(Bitmap b) { + public FastBitmapDrawable(Bitmap b) { mAlpha = 255; mBitmap = b; setBounds(0, 0, b.getWidth(), b.getHeight()); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index f4af7f542..f6238dab2 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -44,6 +44,7 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.PackageItemInfo; import java.util.HashMap; import java.util.HashSet; @@ -454,12 +455,13 @@ public class IconCache { * Fill in {@param appInfo} with the icon and label for {@param packageName} */ public synchronized void getTitleAndIconForApp( - String packageName, UserHandleCompat user, boolean useLowResIcon, AppInfo appInfoOut) { + String packageName, UserHandleCompat user, boolean useLowResIcon, + PackageItemInfo infoOut) { CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon); - appInfoOut.iconBitmap = entry.icon; - appInfoOut.title = entry.title; - appInfoOut.usingLowResIcon = entry.isLowResIcon; - appInfoOut.contentDescription = entry.contentDescription; + infoOut.iconBitmap = entry.icon; + infoOut.title = entry.title; + infoOut.usingLowResIcon = entry.isLowResIcon; + infoOut.contentDescription = entry.contentDescription; } public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { diff --git a/src/com/android/launcher3/Insettable.java b/src/com/android/launcher3/Insettable.java index 1d2356c65..3b8ef2f93 100644 --- a/src/com/android/launcher3/Insettable.java +++ b/src/com/android/launcher3/Insettable.java @@ -18,6 +18,10 @@ package com.android.launcher3; import android.graphics.Rect; +/** + * Allows the implementing {@link View} to not draw underneath system bars. + * e.g., notification bar on top and home key area on the bottom. + */ public interface Insettable { void setInsets(Rect insets); diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index f114de221..f7e0ea488 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -36,7 +36,7 @@ public class ItemInfo { */ static final String EXTRA_PROFILE = "profile"; - static final int NO_ID = -1; + public static final int NO_ID = -1; /** * The id in the settings database for this item @@ -82,7 +82,7 @@ public class ItemInfo { /** * Indicates the Y cell span. */ - int spanY = 1; + public int spanY = 1; /** * Indicates the minimum X cell span. @@ -107,21 +107,21 @@ public class ItemInfo { /** * Title of the item */ - CharSequence title; + public CharSequence title; /** * Content description of the item. */ - CharSequence contentDescription; + public CharSequence contentDescription; /** * The position of the item in a drag-and-drop operation. */ - int[] dropPos = null; + public int[] dropPos = null; - UserHandleCompat user; + public UserHandleCompat user; - ItemInfo() { + public ItemInfo() { user = UserHandleCompat.myUserHandle(); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2fa2f4ad7..068934e1b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -103,6 +103,8 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.launcher3.widget.WidgetsContainerView; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -130,11 +132,11 @@ public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener, LauncherStateTransitionAnimation.Callbacks { - static final String TAG = "Launcher"; - static final boolean LOGD = false; + static final String TAG = "Launcher - MERONG"; + static final boolean LOGD = true; static final boolean PROFILE_STARTUP = false; - static final boolean DEBUG_WIDGETS = false; + static final boolean DEBUG_WIDGETS = true; static final boolean DEBUG_STRICT_MODE = false; static final boolean DEBUG_RESUME_TIME = false; static final boolean DEBUG_DUMP_LOG = false; @@ -264,9 +266,13 @@ public class Launcher extends Activity private View mAllAppsButton; private SearchDropTargetBar mSearchDropTargetBar; + + // Main container view for the all apps screen. @Thunk AppsContainerView mAppsView; - @Thunk AppsCustomizeTabHost mAppsCustomizeTabHost; - private AppsCustomizePagedView mAppsCustomizeContent; + + // Main container view for the widget tray screen. + private WidgetsContainerView mWidgetsView; + private boolean mAutoAdvanceRunning = false; private AppWidgetHostView mQsb; @@ -672,7 +678,7 @@ public class Launcher extends Activity return mInflater; } - boolean isDraggingEnabled() { + public boolean isDraggingEnabled() { // We prevent dragging when we are loading the workspace as it is possible to pick up a view // that is subsequently removed from the workspace in startBinding(). return !mModel.isLoadingWorkspace(); @@ -1013,15 +1019,9 @@ public class Launcher extends Activity startTimeCallbacks = System.currentTimeMillis(); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setBulkBind(true); - } for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) { mBindOnResumeCallbacks.get(i).run(); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setBulkBind(false); - } mBindOnResumeCallbacks.clear(); if (DEBUG_RESUME_TIME) { Log.d(TAG, "Time spent processing callbacks in onResume: " + @@ -1213,9 +1213,8 @@ public class Launcher extends Activity if (mModel.isCurrentCallbacks(this)) { mModel.stopLoader(); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.surrender(); - } + //TODO(hyunyoungs): stop the widgets loader when there is a rotation. + return Boolean.TRUE; } @@ -1336,19 +1335,6 @@ public class Launcher extends Activity mRestoring = true; } - // Restore the AppsCustomize tab - if (mAppsCustomizeTabHost != null) { - String curTab = savedState.getString("apps_customize_currentTab"); - if (curTab != null) { - mAppsCustomizeTabHost.setContentTypeImmediate( - mAppsCustomizeTabHost.getContentTypeForTabTag(curTab)); - mAppsCustomizeContent.loadAssociatedPages( - mAppsCustomizeContent.getCurrentPage()); - } - - int currentIndex = savedState.getInt("apps_customize_currentIndex"); - mAppsCustomizeContent.restorePageForIndex(currentIndex); - } mItemIdToViewId = (HashMap) savedState.getSerializable(RUNTIME_STATE_VIEW_IDS); } @@ -1434,10 +1420,7 @@ public class Launcher extends Activity mAppsView = (AppsContainerView) findViewById(R.id.apps_view); // Setup AppsCustomize - mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane); - mAppsCustomizeContent = (AppsCustomizePagedView) - mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content); - mAppsCustomizeContent.setup(this, dragController); + mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view); // Setup the drag controller (drop targets have to be added in reverse order in priority) dragController.setDragScoller(mWorkspace); @@ -1651,7 +1634,7 @@ public class Launcher extends Activity // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop - if (mAppsView != null && mAppsCustomizeTabHost != null && + if (mAppsView != null && mWidgetsView != null && mPendingAddInfo.container == ItemInfo.NO_ID) { showWorkspace(false); } @@ -1735,7 +1718,6 @@ public class Launcher extends Activity // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged // is a more appropriate event to handle if (mVisible) { - mAppsCustomizeTabHost.onWindowVisible(); if (!mWorkspaceLoading) { final ViewTreeObserver observer = mWorkspace.getViewTreeObserver(); // We want to let Launcher draw itself at least once before we force it to build @@ -1839,7 +1821,7 @@ public class Launcher extends Activity launcherInfo.hostView = null; } - void showOutOfSpaceMessage(boolean isHotseatLayout) { + public void showOutOfSpaceMessage(boolean isHotseatLayout) { int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space); Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); } @@ -1852,8 +1834,8 @@ public class Launcher extends Activity return mAppsView; } - public AppsCustomizeTabHost getWidgetsView() { - return mAppsCustomizeTabHost; + public WidgetsContainerView getWidgetsView() { + return mWidgetsView; } public Workspace getWorkspace() { @@ -1946,9 +1928,9 @@ public class Launcher extends Activity mAppsView.scrollToTop(); } - // Reset the apps customize page - if (!alreadyOnHome && mAppsCustomizeTabHost != null) { - mAppsCustomizeTabHost.reset(); + // Reset the widgets view + if (!alreadyOnHome && mWidgetsView != null) { + mWidgetsView.scrollToTop(); } if (mLauncherCallbacks != null) { @@ -2003,16 +1985,8 @@ public class Launcher extends Activity outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id); } - // Save the current AppsCustomize tab - if (mAppsCustomizeTabHost != null) { - AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType(); - String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type); - if (currentTabTag != null) { - outState.putString("apps_customize_currentTab", currentTabTag); - } - int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex(); - outState.putInt("apps_customize_currentIndex", currentIndex); - } + // Save the current widgets tray? + // TODO(hyunyoungs) outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId); if (mLauncherCallbacks != null) { @@ -3276,9 +3250,7 @@ public class Launcher extends Activity SQLiteDatabase.releaseMemory(); // This clears all widget bitmaps from the widget tray - if (mAppsCustomizeTabHost != null) { - mAppsCustomizeTabHost.trimMemory(); - } + // TODO(hyunyoungs) } if (mLauncherCallbacks != null) { mLauncherCallbacks.onTrimMemory(level); @@ -3355,15 +3327,16 @@ public class Launcher extends Activity * Shows the widgets view. */ void showWidgetsView(boolean animated, boolean resetPageToZero) { + Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero); if (resetPageToZero) { - mAppsCustomizeTabHost.reset(); + mWidgetsView.scrollToTop(); } showAppsOrWidgets(animated, State.WIDGETS); - mAppsCustomizeTabHost.post(new Runnable() { + + mWidgetsView.post(new Runnable() { @Override public void run() { - // We post this in-case the all apps view isn't yet constructed. - mAppsCustomizeTabHost.requestFocus(); + mWidgetsView.requestFocus(); } }); } @@ -3394,7 +3367,9 @@ public class Launcher extends Activity .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } - void enterSpringLoadedDragMode() { + public void enterSpringLoadedDragMode() { + Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", + mState.name())); if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED || mState == State.WIDGETS_SPRING_LOADED) { return; @@ -3405,7 +3380,7 @@ public class Launcher extends Activity mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; } - void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, + public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; @@ -3413,10 +3388,12 @@ public class Launcher extends Activity @Override public void run() { if (successfulDrop) { + // TODO(hyunyoungs): verify if this hack is still needed, if not, delete. + // // Before we show workspace, hide all apps again because // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should // clean up our state transition functions - mAppsCustomizeTabHost.setVisibility(View.GONE); + mWidgetsView.setVisibility(View.GONE); showWorkspace(true, onCompleteRunnable); } else { exitSpringLoadedDragMode(); @@ -3918,8 +3895,8 @@ public class Launcher extends Activity pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = - AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo); + Bundle options = null; + // AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo); int newWidgetId = mAppWidgetHost.allocateAppWidgetId(); boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( @@ -4122,9 +4099,9 @@ public class Launcher extends Activity if (mAppsView != null) { mAppsView.setApps(apps); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */)); + if (mWidgetsView != null) { + mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false), + getPackageManager()); } if (mLauncherCallbacks != null) { mLauncherCallbacks.bindAllApplications(apps); @@ -4276,15 +4253,16 @@ public class Launcher extends Activity mWidgetsAndShortcuts = null; } }; + public void bindPackagesUpdated(final ArrayList widgetsAndShortcuts) { if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) { mWidgetsAndShortcuts = widgetsAndShortcuts; return; } - // Update the widgets pane - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts); + if (mWidgetsView != null) { + mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false), + getPackageManager()); } } @@ -4577,10 +4555,8 @@ public class Launcher extends Activity Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState); Log.d(TAG, "sFolders.size=" + sFolders.size()); mModel.dumpState(); + // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState(); - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.dumpState(); - } Log.d(TAG, "END launcher3 dump state"); } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 2a08b8176..3bd385028 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -141,7 +141,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mModel; } - LauncherAccessibilityDelegate getAccessibilityDelegate() { + public LauncherAccessibilityDelegate getAccessibilityDelegate() { return mAccessibilityDelegate; } diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index aeef0daeb..bb4580ce7 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -16,10 +16,10 @@ import android.os.Parcel; public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { public boolean isCustomWidget = false; - int spanX = -1; - int spanY = -1; - int minSpanX = -1; - int minSpanY = -1; + public int spanX = -1; + public int spanY = -1; + public int minSpanX = -1; + public int minSpanY = -1; public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context, AppWidgetProviderInfo info) { @@ -78,10 +78,11 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { return super.loadIcon(context, cache.getFullResIconDpi()); } - public String toString() { + public String toString(PackageManager pm) { if (isCustomWidget) { - return "LauncherAppWidgetProviderInfo(" + provider + ")"; + return "WidgetProviderInfo(" + provider + ")"; } - return super.toString(); + return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)", + provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 1f36331ec..98ba09bc6 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3628,7 +3628,7 @@ public class LauncherModel extends BroadcastReceiver private final HashMap mLabelCache; private final Collator mCollator; - WidgetAndShortcutNameComparator(Context context) { + public WidgetAndShortcutNameComparator(Context context) { mManager = AppWidgetManagerCompat.getInstance(context); mPackageManager = context.getPackageManager(); mLabelCache = new HashMap(); diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index d657cb50f..111de409e 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -22,7 +22,7 @@ import android.provider.BaseColumns; /** * Settings related utilities. */ -class LauncherSettings { +public class LauncherSettings { /** Columns required on table staht will be subject to backup and restore. */ static interface ChangeLogColumns extends BaseColumns { /** @@ -121,7 +121,7 @@ class LauncherSettings { /** * Favorites. */ - static final class Favorites implements BaseLauncherColumns { + public static final class Favorites implements BaseLauncherColumns { /** * The content:// style URL for this table */ @@ -217,12 +217,12 @@ class LauncherSettings { /** * The favorite is a widget */ - static final int ITEM_TYPE_APPWIDGET = 4; + public static final int ITEM_TYPE_APPWIDGET = 4; /** * The favorite is a custom widget provided by the launcher */ - static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5; + public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5; /** * The favorite is a clock diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index eacf3415e..e92bfb053 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -23,6 +23,7 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.content.res.Resources; +import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewAnimationUtils; @@ -30,6 +31,7 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.WidgetsContainerView; import java.util.HashMap; @@ -179,20 +181,12 @@ public class LauncherStateTransitionAnimation { * Starts an animation to the widgets view. */ public void startAnimationToWidgets(final boolean animated) { - final AppsCustomizeTabHost toView = mLauncher.getWidgetsView(); + final WidgetsContainerView toView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - // Hide the real page background, and swap in the fake one - ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false); - revealView.setBackground( - mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark)); - } - @Override - public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { - // Show the real page background - ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true); + revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); } @Override public float getMaterialRevealViewFinalAlpha(View revealView) { @@ -204,7 +198,7 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), toView.getPageIndicators(), animated, cb); + toView.getRevealView(), null, animated, cb); } /** @@ -500,44 +494,8 @@ public class LauncherStateTransitionAnimation { private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, final Workspace.State toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { - AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView(); + WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { - @Override - public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); - - // Hide the real page background, and swap in the fake one - pagedView.stopScrolling(); - pagedView.setPageBackgroundsVisible(false); - revealView.setBackground( - mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark)); - - // Hide the side pages of the Widget tray to avoid some ugly edge cases - final View currentPage = pagedView.getPageAt(pagedView.getNextPage()); - int count = pagedView.getChildCount(); - for (int i = 0; i < count; i++) { - View child = pagedView.getChildAt(i); - if (child != currentPage) { - child.setVisibility(View.INVISIBLE); - } - } - } - @Override - public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { - AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); - - // Show the real page background and force-update the page - pagedView.setPageBackgroundsVisible(true); - pagedView.setCurrentPage(pagedView.getNextPage()); - pagedView.updateCurrentPageScroll(); - - // Unhide the side pages - int count = pagedView.getChildCount(); - for (int i = 0; i < count; i++) { - View child = pagedView.getChildAt(i); - child.setVisibility(View.VISIBLE); - } - } @Override public float getMaterialRevealViewFinalYDrift(View revealView) { return revealView.getMeasuredHeight() / 2; @@ -559,7 +517,7 @@ public class LauncherStateTransitionAnimation { }; startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, widgetsView.getContentView(), widgetsView.getRevealView(), - widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb); + null, animated, onCompleteRunnable, cb); } /** diff --git a/src/com/android/launcher3/PagedViewGridLayout.java b/src/com/android/launcher3/PagedViewGridLayout.java deleted file mode 100644 index f69fa562d..000000000 --- a/src/com/android/launcher3/PagedViewGridLayout.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2011 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.Context; -import android.view.MotionEvent; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.GridLayout; - -/** - * The grid based layout used strictly for the widget/wallpaper tab of the AppsCustomize pane - */ -public class PagedViewGridLayout extends GridLayout implements Page { - static final String TAG = "PagedViewGridLayout"; - - private int mCellCountX; - private int mCellCountY; - private Runnable mOnLayoutListener; - - public PagedViewGridLayout(Context context, int cellCountX, int cellCountY) { - super(context, null, 0); - mCellCountX = cellCountX; - mCellCountY = cellCountY; - } - - int getCellCountX() { - return mCellCountX; - } - - int getCellCountY() { - return mCellCountY; - } - - /** - * Clears all the key listeners for the individual widgets. - */ - public void resetChildrenOnKeyListeners() { - int childCount = getChildCount(); - for (int j = 0; j < childCount; ++j) { - getChildAt(j).setOnKeyListener(null); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mOnLayoutListener = null; - } - - public void setOnLayoutListener(Runnable r) { - mOnLayoutListener = r; - } - - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (mOnLayoutListener != null) { - mOnLayoutListener.run(); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean result = super.onTouchEvent(event); - int count = getPageChildCount(); - if (count > 0) { - // We only intercept the touch if we are tapping in empty space after the final row - View child = getChildOnPageAt(count - 1); - int bottom = child.getBottom(); - result = result || (event.getY() < bottom); - } - return result; - } - - @Override - public void removeAllViewsOnPage() { - removeAllViews(); - mOnLayoutListener = null; - setLayerType(LAYER_TYPE_NONE, null); - } - - @Override - public void removeViewOnPageAt(int index) { - removeViewAt(index); - } - - @Override - public int getPageChildCount() { - return getChildCount(); - } - - @Override - public View getChildOnPageAt(int i) { - return getChildAt(i); - } - - @Override - public int indexOfChildOnPage(View v) { - return indexOfChild(v); - } - - public static class LayoutParams extends FrameLayout.LayoutParams { - public LayoutParams(int width, int height) { - super(width, height); - } - } -} diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java deleted file mode 100644 index d9ca7be87..000000000 --- a/src/com/android/launcher3/PagedViewWidget.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2010 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.Context; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnLayoutChangeListener; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; -import com.android.launcher3.compat.AppWidgetManagerCompat; - -/** - * The linear layout used strictly for the widget/wallpaper tab of the customization tray - */ -public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListener { - - private static PagedViewWidget sShortpressTarget = null; - - private final Rect mOriginalImagePadding = new Rect(); - - private String mDimensionsFormatString; - private CheckForShortPress mPendingCheckForShortPress = null; - private ShortPressListener mShortPressListener = null; - private boolean mShortPressTriggered = false; - private boolean mIsAppWidget; - private Object mInfo; - - private WidgetPreviewLoader mWidgetPreviewLoader; - private PreviewLoadRequest mActiveRequest; - - public PagedViewWidget(Context context) { - this(context, null); - } - - public PagedViewWidget(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - final Resources r = context.getResources(); - mDimensionsFormatString = r.getString(R.string.widget_dims_format); - - setWillNotDraw(false); - setClipToPadding(false); - setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - final ImageView image = (ImageView) findViewById(R.id.widget_preview); - mOriginalImagePadding.left = image.getPaddingLeft(); - mOriginalImagePadding.top = image.getPaddingTop(); - mOriginalImagePadding.right = image.getPaddingRight(); - mOriginalImagePadding.bottom = image.getPaddingBottom(); - - // Ensure we are using the right text size - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - TextView name = (TextView) findViewById(R.id.widget_name); - if (name != null) { - name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); - } - TextView dims = (TextView) findViewById(R.id.widget_dims); - if (dims != null) { - dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - deletePreview(true); - } - - public void deletePreview(boolean recycleImage) { - if (recycleImage) { - final ImageView image = (ImageView) findViewById(R.id.widget_preview); - if (image != null) { - image.setImageDrawable(null); - } - } - - if (mActiveRequest != null) { - mActiveRequest.cancel(recycleImage); - mActiveRequest = null; - } - } - - public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, - int maxWidth, WidgetPreviewLoader loader) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - mIsAppWidget = true; - mInfo = info; - final ImageView image = (ImageView) findViewById(R.id.widget_preview); - if (maxWidth > -1) { - image.setMaxWidth(maxWidth); - } - final TextView name = (TextView) findViewById(R.id.widget_name); - name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); - final TextView dims = (TextView) findViewById(R.id.widget_dims); - if (dims != null) { - int hSpan = Math.min(info.spanX, (int) grid.numColumns); - int vSpan = Math.min(info.spanY, (int) grid.numRows); - dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); - } - mWidgetPreviewLoader = loader; - } - - public void applyFromResolveInfo( - PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) { - mIsAppWidget = false; - mInfo = info; - CharSequence label = info.loadLabel(pm); - final TextView name = (TextView) findViewById(R.id.widget_name); - name.setText(label); - final TextView dims = (TextView) findViewById(R.id.widget_dims); - if (dims != null) { - dims.setText(String.format(mDimensionsFormatString, 1, 1)); - } - mWidgetPreviewLoader = loader; - } - - public int[] getPreviewSize() { - final ImageView i = (ImageView) findViewById(R.id.widget_preview); - int[] maxSize = new int[2]; - maxSize[0] = i.getWidth() - mOriginalImagePadding.left - mOriginalImagePadding.right; - maxSize[1] = i.getHeight() - mOriginalImagePadding.top; - return maxSize; - } - - public void applyPreview(Bitmap bitmap) { - FastBitmapDrawable preview = new FastBitmapDrawable(bitmap); - final PagedViewWidgetImageView image = - (PagedViewWidgetImageView) findViewById(R.id.widget_preview); - if (preview != null) { - image.mAllowRequestLayout = false; - image.setImageDrawable(preview); - if (mIsAppWidget) { - // center horizontally - int[] imageSize = getPreviewSize(); - int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2; - image.setPadding(mOriginalImagePadding.left + centerAmount, - mOriginalImagePadding.top, - mOriginalImagePadding.right, - mOriginalImagePadding.bottom); - } - image.setAlpha(1f); - image.mAllowRequestLayout = true; - } - } - - void setShortPressListener(ShortPressListener listener) { - mShortPressListener = listener; - } - - interface ShortPressListener { - void onShortPress(View v); - void cleanUpShortPress(View v); - } - - class CheckForShortPress implements Runnable { - public void run() { - if (sShortpressTarget != null) return; - if (mShortPressListener != null) { - mShortPressListener.onShortPress(PagedViewWidget.this); - sShortpressTarget = PagedViewWidget.this; - } - mShortPressTriggered = true; - } - } - - private void checkForShortPress() { - if (sShortpressTarget != null) return; - if (mPendingCheckForShortPress == null) { - mPendingCheckForShortPress = new CheckForShortPress(); - } - postDelayed(mPendingCheckForShortPress, 120); - } - - /** - * Remove the longpress detection timer. - */ - private void removeShortPressCallback() { - if (mPendingCheckForShortPress != null) { - removeCallbacks(mPendingCheckForShortPress); - } - } - - private void cleanUpShortPress() { - removeShortPressCallback(); - if (mShortPressTriggered) { - if (mShortPressListener != null) { - mShortPressListener.cleanUpShortPress(PagedViewWidget.this); - } - mShortPressTriggered = false; - } - } - - static void resetShortPressTarget() { - sShortpressTarget = null; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - cleanUpShortPress(); - break; - case MotionEvent.ACTION_DOWN: - checkForShortPress(); - break; - case MotionEvent.ACTION_CANCEL: - cleanUpShortPress(); - break; - case MotionEvent.ACTION_MOVE: - break; - } - - // We eat up the touch events here, since the PagedView (which uses the same swiping - // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when - // the user is scrolling between pages. This means that if the pages themselves don't - // handle touch events, it gets forwarded up to PagedView itself, and it's own - // onTouchEvent() handling will prevent further intercept touch events from being called - // (it's the same view in that case). This is not ideal, but to prevent more changes, - // we just always mark the touch event as handled. - return true; - } - - public void ensurePreview() { - if (mActiveRequest != null) { - return; - } - int[] size = getPreviewSize(); - - if (size[0] <= 0 || size[1] <= 0) { - addOnLayoutChangeListener(this); - return; - } - Bitmap[] immediateResult = new Bitmap[1]; - mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this, - immediateResult); - if (immediateResult[0] != null) { - applyPreview(immediateResult[0]); - } - } - - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { - removeOnLayoutChangeListener(this); - ensurePreview(); - } - - public int getActualItemWidth() { - ItemInfo info = (ItemInfo) getTag(); - int[] size = getPreviewSize(); - int cellWidth = LauncherAppState.getInstance() - .getDynamicGrid().getDeviceProfile().cellWidthPx; - - return Math.min(size[0], info.spanX * cellWidth); - } -} diff --git a/src/com/android/launcher3/PagedViewWidgetImageView.java b/src/com/android/launcher3/PagedViewWidgetImageView.java deleted file mode 100644 index 7d8279547..000000000 --- a/src/com/android/launcher3/PagedViewWidgetImageView.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2011 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.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.widget.ImageView; - -public class PagedViewWidgetImageView extends ImageView { - public boolean mAllowRequestLayout = true; - - public PagedViewWidgetImageView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public void requestLayout() { - if (mAllowRequestLayout) { - super.requestLayout(); - } - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.save(); - canvas.clipRect(getScrollX() + getPaddingLeft(), - getScrollY() + getPaddingTop(), - getScrollX() + getRight() - getLeft() - getPaddingRight(), - getScrollY() + getBottom() - getTop() - getPaddingBottom()); - - super.onDraw(canvas); - canvas.restore(); - - } -} diff --git a/src/com/android/launcher3/PagedViewWithDraggableItems.java b/src/com/android/launcher3/PagedViewWithDraggableItems.java deleted file mode 100644 index f0743cf1c..000000000 --- a/src/com/android/launcher3/PagedViewWithDraggableItems.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2010 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.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - - -/* Class that does most of the work of enabling dragging items out of a PagedView by performing a - * vertical drag. Used by both CustomizePagedView and AllAppsPagedView. - * Subclasses must do the following: - * * call setDragSlopeThreshold after making an instance of the PagedViewWithDraggableItems - * * call child.setOnLongClickListener(this) and child.setOnTouchListener(this) on all children - * (good place to do it is in syncPageItems) - * * override beginDragging(View) (but be careful to call super.beginDragging(View) - * - */ -public abstract class PagedViewWithDraggableItems extends PagedView - implements View.OnLongClickListener, View.OnTouchListener { - private View mLastTouchedItem; - private boolean mIsDragging; - private boolean mIsDragEnabled; - private float mDragSlopeThreshold; - private Launcher mLauncher; - - public PagedViewWithDraggableItems(Context context) { - this(context, null); - } - - public PagedViewWithDraggableItems(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagedViewWithDraggableItems(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mLauncher = (Launcher) context; - } - - protected boolean beginDragging(View v) { - boolean wasDragging = mIsDragging; - mIsDragging = true; - return !wasDragging; - } - - protected void cancelDragging() { - mIsDragging = false; - mLastTouchedItem = null; - mIsDragEnabled = false; - } - - private void handleTouchEvent(MotionEvent ev) { - final int action = ev.getAction(); - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - cancelDragging(); - mIsDragEnabled = true; - break; - case MotionEvent.ACTION_MOVE: - if (mTouchState != TOUCH_STATE_SCROLLING && !mIsDragging && mIsDragEnabled) { - determineDraggingStart(ev); - } - break; - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - handleTouchEvent(ev); - return super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - handleTouchEvent(ev); - return super.onTouchEvent(ev); - } - - public void trimMemory() { - mLastTouchedItem = null; - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - mLastTouchedItem = v; - mIsDragEnabled = true; - return false; - } - - @Override - public boolean onLongClick(View v) { - // Return early if this is not initiated from a touch - if (!v.isInTouchMode()) return false; - // Return early if we are still animating the pages - if (mNextPage != INVALID_PAGE) return false; - // When we have exited all apps or are in transition, disregard long clicks - if (!mLauncher.isWidgetsViewVisible() || - mLauncher.getWorkspace().isSwitchingState()) return false; - // Return if global dragging is not enabled - if (!mLauncher.isDraggingEnabled()) return false; - - return beginDragging(v); - } - - /* - * Determines if we should change the touch state to start scrolling after the - * user moves their touch point too far. - */ - protected void determineScrollingStart(MotionEvent ev) { - if (!mIsDragging) super.determineScrollingStart(ev); - } - - /* - * Determines if we should change the touch state to start dragging after the - * user moves their touch point far enough. - */ - protected void determineDraggingStart(MotionEvent ev) { - /* - * Locally do absolute value. mLastMotionX is set to the y value - * of the down event. - */ - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - final float x = ev.getX(pointerIndex); - final float y = ev.getY(pointerIndex); - final int xDiff = (int) Math.abs(x - mLastMotionX); - final int yDiff = (int) Math.abs(y - mLastMotionY); - - final int touchSlop = mTouchSlop; - boolean yMoved = yDiff > touchSlop; - boolean isUpwardMotion = (yDiff / (float) xDiff) > mDragSlopeThreshold; - - if (isUpwardMotion && yMoved && mLastTouchedItem != null) { - // Drag if the user moved far enough along the Y axis - beginDragging(mLastTouchedItem); - - // Cancel any pending long press - if (mAllowLongPress) { - mAllowLongPress = false; - // Try canceling the long press. It could also have been scheduled - // by a distant descendant, so use the mAllowLongPress flag to block - // everything - final View currentPage = getPageAt(mCurrentPage); - if (currentPage != null) { - currentPage.cancelLongPress(); - } - } - } - } - - public void setDragSlopeThreshold(float dragSlopeThreshold) { - mDragSlopeThreshold = dragSlopeThreshold; - } - - @Override - protected void onDetachedFromWindow() { - cancelDragging(); - super.onDetachedFromWindow(); - } -} diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java index ac54a262f..1aaf85bbd 100644 --- a/src/com/android/launcher3/PendingAddItemInfo.java +++ b/src/com/android/launcher3/PendingAddItemInfo.java @@ -16,93 +16,17 @@ package com.android.launcher3; -import android.appwidget.AppWidgetHostView; import android.content.ComponentName; -import android.content.pm.ActivityInfo; -import android.os.Bundle; -import android.os.Parcelable; /** - * We pass this object with a drag from the customization tray + * Meta data that is used for deferred binding. + * e.g., this object is used to pass information on dragable targets when they are dropped onto + * the workspace from another container. */ -class PendingAddItemInfo extends ItemInfo { +public class PendingAddItemInfo extends ItemInfo { + /** * The component that will be created. */ - ComponentName componentName; -} - -class PendingAddShortcutInfo extends PendingAddItemInfo { - - ActivityInfo shortcutActivityInfo; - - public PendingAddShortcutInfo(ActivityInfo activityInfo) { - shortcutActivityInfo = activityInfo; - } - - @Override - public String toString() { - return "Shortcut: " + shortcutActivityInfo.packageName; - } -} - -class PendingAddWidgetInfo extends PendingAddItemInfo { - int minWidth; - int minHeight; - int minResizeWidth; - int minResizeHeight; - int previewImage; - int icon; - LauncherAppWidgetProviderInfo info; - AppWidgetHostView boundWidget; - Bundle bindOptions = null; - - public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) { - if (i.isCustomWidget) { - itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; - } else { - itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; - } - this.info = i; - componentName = i.provider; - minWidth = i.minWidth; - minHeight = i.minHeight; - minResizeWidth = i.minResizeWidth; - minResizeHeight = i.minResizeHeight; - previewImage = i.previewImage; - icon = i.icon; - - spanX = i.spanX; - spanY = i.spanY; - minSpanX = i.minSpanX; - minSpanY = i.minSpanY; - } - - public boolean isCustomWidget() { - return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; - } - - // Copy constructor - public PendingAddWidgetInfo(PendingAddWidgetInfo copy) { - minWidth = copy.minWidth; - minHeight = copy.minHeight; - minResizeWidth = copy.minResizeWidth; - minResizeHeight = copy.minResizeHeight; - previewImage = copy.previewImage; - icon = copy.icon; - info = copy.info; - boundWidget = copy.boundWidget; - componentName = copy.componentName; - itemType = copy.itemType; - spanX = copy.spanX; - spanY = copy.spanY; - minSpanX = copy.minSpanX; - minSpanY = copy.minSpanY; - bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone(); - } - - @Override - public String toString() { - return "Widget: " + componentName.toShortString(); - } + public ComponentName componentName; } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 1043e2ee0..5c3ed9272 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -32,6 +32,7 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.WidgetCell; import java.lang.ref.WeakReference; import java.util.Collections; @@ -45,6 +46,7 @@ import java.util.concurrent.ExecutionException; public class WidgetPreviewLoader { private static final String TAG = "WidgetPreviewLoader"; + private static final boolean DEBUG = false; private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; @@ -78,7 +80,7 @@ public class WidgetPreviewLoader { * @return a request id which can be used to cancel the request. */ public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight, - PagedViewWidget caller, Bitmap[] immediateResult) { + WidgetCell caller, Bitmap[] immediateResult) { String size = previewWidth + "x" + previewHeight; WidgetCacheKey key = getObjectKey(o, size); @@ -576,21 +578,26 @@ public class WidgetPreviewLoader { private final Object mInfo; private final int mPreviewHeight; private final int mPreviewWidth; - private final PagedViewWidget mCaller; + private final WidgetCell mCaller; PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, - int previewHeight, PagedViewWidget caller) { + int previewHeight, WidgetCell caller) { mKey = key; mInfo = info; mPreviewHeight = previewHeight; mPreviewWidth = previewWidth; mCaller = caller; + if (DEBUG) { + Log.d(TAG, String.format("%s, %s, %d, %d", + mKey, mInfo, mPreviewHeight, mPreviewWidth)); + } } - @Override protected Bitmap doInBackground(Void... params) { Bitmap unusedBitmap = null; + + // TODO(hyunyoungs): Figure out why this path causes concurrency issue. synchronized (mUnusedBitmaps) { // Check if we can use a bitmap for (Bitmap candidate : mUnusedBitmaps) { @@ -608,7 +615,6 @@ public class WidgetPreviewLoader { mUnusedBitmaps.remove(unusedBitmap); } } - if (isCancelled()) { return null; } diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java deleted file mode 100644 index 7004d8b29..000000000 --- a/src/com/android/launcher3/WidgetsContainerView.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.android.launcher3; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.FrameLayout; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - - -class SectionedWidgetsRow { - String section; - List> widgets; - - public SectionedWidgetsRow(String sc) { - section = sc; - } -} - -class SectionedWidgetsAlgorithm { - public List computeSectionedWidgetRows(List sortedWidgets, - int widgetsPerRow) { - List rows = new ArrayList<>(); - LinkedHashMap> sections = computeSectionedApps(sortedWidgets); - for (Map.Entry> sectionEntry : sections.entrySet()) { - String section = sectionEntry.getKey(); - SectionedWidgetsRow row = new SectionedWidgetsRow(section); - List widgets = sectionEntry.getValue(); - int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow); - for (int i = 0; i < numRows; i++) { - List widgetsInRow = new ArrayList<>(); - int offset = i * widgetsPerRow; - for (int j = 0; j < widgetsPerRow; j++) { - widgetsInRow.add(widgets.get(offset + j)); - } - row.widgets.add(widgetsInRow); - } - } - return rows; - } - - private LinkedHashMap> computeSectionedApps(List sortedWidgets) { - LinkedHashMap> sections = new LinkedHashMap<>(); - for (Object info : sortedWidgets) { - String section = getSection(info); - List sectionedWidgets = sections.get(section); - if (sectionedWidgets == null) { - sectionedWidgets = new ArrayList<>(); - sections.put(section, sectionedWidgets); - } - sectionedWidgets.add(info); - } - return sections; - } - - private String getSection(Object widgetOrShortcut) { - return "UNKNOWN"; - } -} - -/** - * The widgets list view container. - */ -public class WidgetsContainerView extends FrameLayout { - - - public WidgetsContainerView(Context context) { - this(context, null); - } - - public WidgetsContainerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onFinishInflate() { - } -} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a79add05f..8cc99a044 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -71,6 +71,8 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; +import com.android.launcher3.widget.PendingAddShortcutInfo; +import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java new file mode 100644 index 000000000..d7edf2294 --- /dev/null +++ b/src/com/android/launcher3/widget/PackageItemInfo.java @@ -0,0 +1,57 @@ +/* + * 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.widget; + +import android.content.ComponentName; +import android.graphics.Bitmap; + +import com.android.launcher3.ItemInfo; + +import java.util.Arrays; + +/** + * Represents a {@link Package} in the widget tray section. + */ +public class PackageItemInfo extends ItemInfo { + private static final String TAG = "PackageInfo"; + + /** + * A bitmap version of the application icon. + */ + public Bitmap iconBitmap; + + /** + * Indicates whether we're using a low res icon + */ + public boolean usingLowResIcon; + + public ComponentName componentName; + + int flags = 0; + + PackageItemInfo() { + } + + @Override + public String toString() { + return "PackageItemInfo(title=" + title.toString() + " id=" + this.id + + " type=" + this.itemType + " container=" + this.container + + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + + " user=" + user + ")"; + } +} diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java new file mode 100644 index 000000000..a56985083 --- /dev/null +++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java @@ -0,0 +1,44 @@ +/* + * 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.widget; + +import android.content.ComponentName; +import android.content.pm.ActivityInfo; + +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.PendingAddItemInfo; + +/** + * Meta data used for late binding of the short cuts. + * + * @see {@link PendingAddItemInfo} + */ +public class PendingAddShortcutInfo extends PendingAddItemInfo { + + ActivityInfo activityInfo; + + public PendingAddShortcutInfo(ActivityInfo activityInfo) { + this.activityInfo = activityInfo; + componentName = new ComponentName(activityInfo.packageName, activityInfo.name); + itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + } + + @Override + public String toString() { + return String.format("PendingAddShortcutInfo package=%s, name=%s", + activityInfo.packageName, activityInfo.name); + } +} diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java new file mode 100644 index 000000000..db1699818 --- /dev/null +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -0,0 +1,91 @@ +/* + * 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.widget; + +import android.appwidget.AppWidgetHostView; +import android.os.Bundle; +import android.os.Parcelable; + +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.PendingAddItemInfo; + +/** + * Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}. + * + * @see {@link PendingAddItemInfo} + */ +public class PendingAddWidgetInfo extends PendingAddItemInfo { + public int minWidth; + public int minHeight; + public int minResizeWidth; + public int minResizeHeight; + public int previewImage; + public int icon; + public LauncherAppWidgetProviderInfo info; + public AppWidgetHostView boundWidget; + public Bundle bindOptions = null; + + public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) { + if (i.isCustomWidget) { + itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + } else { + itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + } + this.info = i; + componentName = i.provider; + minWidth = i.minWidth; + minHeight = i.minHeight; + minResizeWidth = i.minResizeWidth; + minResizeHeight = i.minResizeHeight; + previewImage = i.previewImage; + icon = i.icon; + + spanX = i.spanX; + spanY = i.spanY; + minSpanX = i.minSpanX; + minSpanY = i.minSpanY; + } + + public boolean isCustomWidget() { + return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + } + + // Copy constructor + public PendingAddWidgetInfo(PendingAddWidgetInfo copy) { + minWidth = copy.minWidth; + minHeight = copy.minHeight; + minResizeWidth = copy.minResizeWidth; + minResizeHeight = copy.minResizeHeight; + previewImage = copy.previewImage; + icon = copy.icon; + info = copy.info; + boundWidget = copy.boundWidget; + componentName = copy.componentName; + itemType = copy.itemType; + spanX = copy.spanX; + spanY = copy.spanY; + minSpanX = copy.minSpanX; + minSpanY = copy.minSpanY; + bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone(); + } + + @Override + public String toString() { + return String.format("PendingAddWidgetInfo package=%s, name=%s", + componentName.getPackageName(), componentName.getShortClassName()); + } +} diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java new file mode 100644 index 000000000..ccd67ce41 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -0,0 +1,338 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnLayoutChangeListener; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.FastBitmapDrawable; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.R; +import com.android.launcher3.WidgetPreviewLoader; +import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; +import com.android.launcher3.compat.AppWidgetManagerCompat; + +/** + * The linear layout used strictly for the widget tray. + */ +public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { + + private static final String TAG = "PagedViewWidget"; + private static final boolean DEBUG = false; + + // Temporary preset width and height of the image to keep them aligned. + //private static final int PRESET_PREVIEW_HEIGHT = 480; + //private static final int PRESET_PREVIEW_WIDTH = 480; + + private int mPresetPreviewSize; + + private static WidgetCell sShortpressTarget = null; + + private final Rect mOriginalImagePadding = new Rect(); + + private String mDimensionsFormatString; + private CheckForShortPress mPendingCheckForShortPress = null; + private ShortPressListener mShortPressListener = null; + private boolean mShortPressTriggered = false; + private boolean mIsAppWidget; + private Object mInfo; + + private WidgetPreviewLoader mWidgetPreviewLoader; + private PreviewLoadRequest mActiveRequest; + + public WidgetCell(Context context) { + this(context, null); + } + + public WidgetCell(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetCell(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final Resources r = context.getResources(); + mDimensionsFormatString = r.getString(R.string.widget_dims_format); + mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size); + + setWillNotDraw(false); + setClipToPadding(false); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); + + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final ImageView image = (ImageView) findViewById(R.id.widget_preview); + mOriginalImagePadding.left = image.getPaddingLeft(); + mOriginalImagePadding.top = image.getPaddingTop(); + mOriginalImagePadding.right = image.getPaddingRight(); + mOriginalImagePadding.bottom = image.getPaddingBottom(); + + // Ensure we are using the right text size + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + TextView name = (TextView) findViewById(R.id.widget_name); + if (name != null) { + name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); + } + TextView dims = (TextView) findViewById(R.id.widget_dims); + if (dims != null) { + dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); + } + } + + @Override + protected void onDetachedFromWindow() { + if (DEBUG) { + Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString())); + } + super.onDetachedFromWindow(); + deletePreview(false); + } + + public void deletePreview(boolean recycleImage) { + if (recycleImage) { + final ImageView image = (ImageView) findViewById(R.id.widget_preview); + if (image != null) { + image.setImageDrawable(null); + } + } + + if (mActiveRequest != null) { + mActiveRequest.cancel(recycleImage); + mActiveRequest = null; + } + } + + public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, + int maxWidth, WidgetPreviewLoader loader) { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + + mIsAppWidget = true; + mInfo = info; + final ImageView image = (ImageView) findViewById(R.id.widget_preview); + if (maxWidth > -1) { + image.setMaxWidth(maxWidth); + } + final TextView name = (TextView) findViewById(R.id.widget_name); + name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); + final TextView dims = (TextView) findViewById(R.id.widget_dims); + if (dims != null) { + int hSpan = Math.min(info.spanX, (int) grid.numColumns); + int vSpan = Math.min(info.spanY, (int) grid.numRows); + dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); + } + mWidgetPreviewLoader = loader; + } + + public void applyFromResolveInfo( + PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) { + mIsAppWidget = false; + mInfo = info; + CharSequence label = info.loadLabel(pm); + final TextView name = (TextView) findViewById(R.id.widget_name); + name.setText(label); + final TextView dims = (TextView) findViewById(R.id.widget_dims); + if (dims != null) { + dims.setText(String.format(mDimensionsFormatString, 1, 1)); + } + mWidgetPreviewLoader = loader; + } + + public int[] getPreviewSize() { + final ImageView i = (ImageView) findViewById(R.id.widget_preview); + int[] maxSize = new int[2]; + maxSize[0] = mPresetPreviewSize; + maxSize[1] = mPresetPreviewSize; + return maxSize; + } + + public void applyPreview(Bitmap bitmap) { + FastBitmapDrawable preview = new FastBitmapDrawable(bitmap); + final WidgetImageView image = + (WidgetImageView) findViewById(R.id.widget_preview); + if (DEBUG) { + Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s", + getTagToString(), preview)); + } + if (preview != null) { + image.mAllowRequestLayout = false; + image.setImageDrawable(preview); + if (mIsAppWidget) { + // center horizontally + int[] imageSize = getPreviewSize(); + int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2; + image.setPadding(mOriginalImagePadding.left + centerAmount, + mOriginalImagePadding.top, + mOriginalImagePadding.right, + mOriginalImagePadding.bottom); + } + image.setAlpha(1f); + image.mAllowRequestLayout = true; + image.requestLayout(); + } + } + + void setShortPressListener(ShortPressListener listener) { + mShortPressListener = listener; + } + + interface ShortPressListener { + void onShortPress(View v); + void cleanUpShortPress(View v); + } + + class CheckForShortPress implements Runnable { + public void run() { + if (sShortpressTarget != null) return; + if (mShortPressListener != null) { + mShortPressListener.onShortPress(WidgetCell.this); + sShortpressTarget = WidgetCell.this; + } + mShortPressTriggered = true; + } + } + + private void checkForShortPress() { + if (sShortpressTarget != null) return; + if (mPendingCheckForShortPress == null) { + mPendingCheckForShortPress = new CheckForShortPress(); + } + postDelayed(mPendingCheckForShortPress, 120); + } + + /** + * Remove the longpress detection timer. + */ + private void removeShortPressCallback() { + if (mPendingCheckForShortPress != null) { + removeCallbacks(mPendingCheckForShortPress); + } + } + + private void cleanUpShortPress() { + removeShortPressCallback(); + if (mShortPressTriggered) { + if (mShortPressListener != null) { + mShortPressListener.cleanUpShortPress(WidgetCell.this); + } + mShortPressTriggered = false; + } + } + + static void resetShortPressTarget() { + sShortpressTarget = null; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + cleanUpShortPress(); + break; + case MotionEvent.ACTION_DOWN: + checkForShortPress(); + break; + case MotionEvent.ACTION_CANCEL: + cleanUpShortPress(); + break; + case MotionEvent.ACTION_MOVE: + break; + } + + // We eat up the touch events here, since the PagedView (which uses the same swiping + // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when + // the user is scrolling between pages. This means that if the pages themselves don't + // handle touch events, it gets forwarded up to PagedView itself, and it's own + // onTouchEvent() handling will prevent further intercept touch events from being called + // (it's the same view in that case). This is not ideal, but to prevent more changes, + // we just always mark the touch event as handled. + return true; + } + + public void ensurePreview() { + if (mActiveRequest != null) { + return; + } + int[] size = getPreviewSize(); + if (DEBUG) { + Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):", + getTagToString(), size[0], size[1])); + } + + if (size[0] <= 0 || size[1] <= 0) { + addOnLayoutChangeListener(this); + return; + } + Bitmap[] immediateResult = new Bitmap[1]; + mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this, + immediateResult); + if (immediateResult[0] != null) { + applyPreview(immediateResult[0]); + } + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + removeOnLayoutChangeListener(this); + ensurePreview(); + } + + public int getActualItemWidth() { + ItemInfo info = (ItemInfo) getTag(); + int[] size = getPreviewSize(); + int cellWidth = LauncherAppState.getInstance() + .getDynamicGrid().getDeviceProfile().cellWidthPx; + + return Math.min(size[0], info.spanX * cellWidth); + } + + /** + * Helper method to get the string info of the tag. + */ + private String getTagToString() { + if (getTag() instanceof PendingAddWidgetInfo) { + return ((PendingAddWidgetInfo)getTag()).toString(); + } else if (getTag() instanceof PendingAddShortcutInfo) { + return ((PendingAddShortcutInfo)getTag()).toString(); + } + return ""; + } +} diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java new file mode 100644 index 000000000..75167bc7d --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetImageView.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 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.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.widget.ImageView; + +public class WidgetImageView extends ImageView { + public boolean mAllowRequestLayout = true; + + public WidgetImageView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void requestLayout() { + if (mAllowRequestLayout) { + super.requestLayout(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.save(); + canvas.clipRect(getScrollX() + getPaddingLeft(), + getScrollY() + getPaddingTop(), + getScrollX() + getRight() - getLeft() - getPaddingRight(), + getScrollY() + getBottom() - getTop() - getPaddingBottom()); + + super.onDraw(canvas); + canvas.restore(); + } +} diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java new file mode 100644 index 000000000..6580ab4ff --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -0,0 +1,376 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.launcher3.CellLayout; +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DragController; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.FastBitmapDrawable; +import com.android.launcher3.Folder; +import com.android.launcher3.IconCache; +import com.android.launcher3.Insettable; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.PendingAddItemInfo; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.WidgetPreviewLoader; +import com.android.launcher3.Workspace; + +import java.util.ArrayList; + +/** + * The widgets list view container. + */ +public class WidgetsContainerView extends FrameLayout implements Insettable, View.OnTouchListener, + View.OnLongClickListener, DragSource{ + + private static final String TAG = "WidgetContainerView"; + private static final boolean DEBUG = false; + + /* {@link RecyclerView} will keep following # of views in cache, before recycling. */ + private static final int WIDGET_CACHE_SIZE = 2; + + /* Global instances that are used inside this container. */ + private Launcher mLauncher; + private DragController mDragController; + private IconCache mIconCache; + + /* Data model for the widget */ + private WidgetsModel mWidgets; + + /* Recycler view related member variables */ + private RecyclerView mView; + private WidgetsListAdapter mAdapter; + + /* Dragging related. */ + private boolean mDraggingWidget = false; // TODO(hyunyoungs): seems not needed? check! + private Point mLastTouchDownPos = new Point(); + + /* Rendering related. */ + private WidgetPreviewLoader mWidgetPreviewLoader; + private Rect mPadding = new Rect(); + + public WidgetsContainerView(Context context) { + this(context, null); + } + + public WidgetsContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr); + mLauncher = (Launcher) context; + mDragController = mLauncher.getDragController(); + + mAdapter = new WidgetsListAdapter(context, this, mLauncher, this, mLauncher); + mWidgets = new WidgetsModel(context, mAdapter); + mAdapter.setWidgetsModel(mWidgets); + mIconCache = (LauncherAppState.getInstance()).getIconCache(); + + if (DEBUG) { + Log.d(TAG, "WidgetsContainerView constructor"); + } + } + + @Override + protected void onFinishInflate() { + if (DEBUG) { + Log.d(TAG, String.format("onFinishInflate [widgets size=%d]", + mWidgets.getPackageSize())); + } + mView = (RecyclerView) findViewById(R.id.widgets_list_view); + mView.setAdapter(mAdapter); + mView.setLayoutManager(new LinearLayoutManager(getContext())); + mView.setItemViewCacheSize(WIDGET_CACHE_SIZE); + + mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), + getPaddingBottom()); + } + + // + // Returns views used for launcher transitions. + // + + public View getContentView() { + return findViewById(R.id.widgets_list_view); + } + + public View getRevealView() { + // TODO(hyunyoungs): temporarily use apps view transition. + return findViewById(R.id.widgets_reveal_view); + } + + public void scrollToTop() { + mView.scrollToPosition(0); + if (DEBUG) { + Log.d(TAG, String.format("scrollToTop, [widgets size=%d]", + mWidgets.getPackageSize())); + } + } + + // + // Touch related handling. + // + + @Override + public boolean onLongClick(View v) { + if (DEBUG) { + Log.d(TAG, String.format("onLonglick [v=%s]", v)); + } + + // Return early if this is not initiated from a touch + if (!v.isInTouchMode()) return false; + // When we have exited all apps or are in transition, disregard long clicks + if (!mLauncher.isWidgetsViewVisible() || + mLauncher.getWorkspace().isSwitchingState()) return false; + // Return if global dragging is not enabled + Log.d(TAG, String.format("onLonglick dragging enabled?.", v)); + if (!mLauncher.isDraggingEnabled()) return false; + + return beginDragging(v); + } + + private boolean beginDragging(View v) { + if (v instanceof WidgetCell) { + if (!beginDraggingWidget((WidgetCell) v)) { + return false; + } + } else { + Log.e(TAG, "Unexpected dragging view: " + v); + } + + // We delay entering spring-loaded mode slightly to make sure the UI + // thready is free of any work. + postDelayed(new Runnable() { + @Override + public void run() { + // We don't enter spring-loaded mode if the drag has been cancelled + if (mLauncher.getDragController().isDragging()) { + // Go into spring loaded mode (must happen before we startDrag()) + mLauncher.enterSpringLoadedDragMode(); + } + } + }, 150); + + return true; + } + + private boolean beginDraggingWidget(WidgetCell v) { + mDraggingWidget = true; + // Get the widget preview as the drag representation + ImageView image = (ImageView) v.findViewById(R.id.widget_preview); + PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); + + // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and + // we abort the drag. + if (image.getDrawable() == null) { + mDraggingWidget = false; + return false; + } + + // Compose the drag image + Bitmap preview; + Bitmap outline; + float scale = 1f; + Point previewPadding = null; + + if (createItemInfo instanceof PendingAddWidgetInfo) { + // This can happen in some weird cases involving multi-touch. We can't start dragging + // the widget if this is null, so we break out. + + PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo; + int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true); + + FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); + float minScale = 1.25f; + int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); + + int[] previewSizeBeforeScale = new int[1]; + preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, + maxWidth, null, previewSizeBeforeScale); + // Compare the size of the drag preview to the preview in the AppsCustomize tray + int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], + v.getActualItemWidth()); + scale = previewWidthInAppsCustomize / (float) preview.getWidth(); + + // The bitmap in the AppsCustomize tray is always the the same size, so there + // might be extra pixels around the preview itself - this accounts for that + if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) { + int padding = + (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2; + previewPadding = new Point(padding, 0); + } + } else { + PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag(); + Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo); + preview = Utilities.createIconBitmap(icon, mLauncher); + createItemInfo.spanX = createItemInfo.spanY = 1; + } + + // Don't clip alpha values for the drag outline if we're using the default widget preview + boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo && + (((PendingAddWidgetInfo) createItemInfo).previewImage == 0)); + + // Save the preview for the outline generation, then dim the preview + outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(), + false); + + // Start the drag + mLauncher.lockScreenOrientation(); + mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha); + mDragController.startDrag(image, preview, this, createItemInfo, + DragController.DRAG_ACTION_COPY, previewPadding, scale); + outline.recycle(); + preview.recycle(); + return true; + } + + /* + * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent) + */ + @Override + public boolean onTouch(View v, MotionEvent ev) { + Log.d(TAG, String.format("onTouch [MotionEvent=%s]", ev)); + if (ev.getAction() == MotionEvent.ACTION_DOWN || + ev.getAction() == MotionEvent.ACTION_MOVE) { + mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); + } + return false; + } + + // + // Drag related handling methods that implement {@link DragSource} interface. + // + + @Override + public boolean supportsFlingToDelete() { + return false; + } + + @Override + public boolean supportsAppInfoDropTarget() { + return true; + } + + /* + * Both this method and {@link #supportsFlingToDelete} has to return {@code false} for the + * {@link DeleteDropTarget} to be invisible.) + */ + @Override + public boolean supportsDeleteDropTarget() { + return false; + } + + @Override + public float getIntrinsicIconScaleFactor() { + return 0; + } + + @Override + public void onFlingToDeleteCompleted() { + // We just dismiss the drag when we fling, so cleanup here + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + mLauncher.unlockScreenOrientation(false); + } + + @Override + public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, + boolean success) { + if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && + !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { + // Exit spring loaded mode if we have not successfully dropped or have not handled the + // drop in Workspace + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + mLauncher.unlockScreenOrientation(false); + + // Display an error message if the drag failed due to there not being enough space on the + // target layout we were dropping on. + if (!success) { + boolean showOutOfSpaceMessage = false; + if (target instanceof Workspace) { + int currentScreen = mLauncher.getCurrentWorkspaceScreen(); + Workspace workspace = (Workspace) target; + CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); + ItemInfo itemInfo = (ItemInfo) d.dragInfo; + if (layout != null) { + layout.calculateSpans(itemInfo); + showOutOfSpaceMessage = + !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); + } + } + if (showOutOfSpaceMessage) { + mLauncher.showOutOfSpaceMessage(false); + } + d.deferDragViewCleanupPostAnimation = false; + } + } + + // + // Container rendering related. + // + + /* + * @see Insettable#setInsets(Rect) + */ + @Override + public void setInsets(Rect insets) { + setPadding(mPadding.left + insets.left, mPadding.top + insets.top, + mPadding.right + insets.right, mPadding.bottom + insets.bottom); + } + + /** + * Initialize the widget data model. + */ + public void addWidgets(ArrayList widgetsShortcuts, PackageManager pm) { + mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm); + } + + private WidgetPreviewLoader getWidgetPreviewLoader() { + if (mWidgetPreviewLoader == null) { + mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache(); + } + return mWidgetPreviewLoader; + } + +} \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java new file mode 100644 index 000000000..d0d1e60b4 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -0,0 +1,188 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.support.v7.widget.RecyclerView.Adapter; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.launcher3.IconCache; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.R; +import com.android.launcher3.WidgetPreviewLoader; +import com.android.launcher3.compat.UserHandleCompat; + +import java.util.List; + +/** + * List view adapter for the widget tray. + * + *

Memory vs. Performance: + * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling + * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is + * only a single type of view. + */ +public class WidgetsListAdapter extends Adapter { + + private static final String TAG = "WidgetsListAdapter"; + private static final boolean DEBUG = false; + + private Context mContext; + private Launcher mLauncher; + private LayoutInflater mLayoutInflater; + private IconCache mIconCache; + + private WidgetsModel mWidgetsModel; + private WidgetPreviewLoader mWidgetPreviewLoader; + + private View.OnTouchListener mTouchListener; + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + + + public WidgetsListAdapter(Context context, + View.OnTouchListener touchListener, + View.OnClickListener iconClickListener, + View.OnLongClickListener iconLongClickListener, + Launcher launcher) { + mLayoutInflater = LayoutInflater.from(context); + mContext = context; + + mTouchListener = touchListener; + mIconClickListener = iconClickListener; + mIconLongClickListener = iconLongClickListener; + + mLauncher = launcher; + mIconCache = LauncherAppState.getInstance().getIconCache(); + } + + public void setWidgetsModel(WidgetsModel w) { + mWidgetsModel = w; + } + + @Override + public int getItemCount() { + return mWidgetsModel.getPackageSize(); + } + + @Override + public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) { + String packageName = mWidgetsModel.getPackageName(pos); + List infoList = mWidgetsModel.getSortedWidgets(packageName); + + ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); + if (DEBUG) { + Log.d(TAG, String.format( + "onBindViewHolder [pos=%d, packageName=%s, widget#=%d, row.getChildCount=%d]", + pos, packageName, infoList.size(), row.getChildCount())); + } + + // Add more views. + // if there are too many, hide them. + int diff = infoList.size() - row.getChildCount(); + if (diff > 0) { + for (int i = 0; i < diff; i++) { + WidgetCell widget = new WidgetCell(mContext); + widget = (WidgetCell) mLayoutInflater.inflate( + R.layout.widget_cell, row, false); + + // set up touch. + widget.setOnClickListener(mIconClickListener); + widget.setOnLongClickListener(mIconLongClickListener); + widget.setOnTouchListener(mTouchListener); + row.addView(widget); + } + } else if (diff < 0) { + for (int i=infoList.size() ; i < row.getChildCount(); i++) { + row.getChildAt(i).setVisibility(View.GONE); + } + } + + // Bind the views in the application info section. + PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(packageName); + if (infoOut.usingLowResIcon) { + mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), + false /* useLowResIcon */, infoOut); + } + ((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title); + ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image); + iv.setImageBitmap(infoOut.iconBitmap); + + // Bind the view in the widget horizontal tray region. + for (int i=0; i < infoList.size(); i++) { + WidgetCell widget = (WidgetCell) row.getChildAt(i); + if (getWidgetPreviewLoader() == null || widget == null) { + return; + } + if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i); + PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null); + widget.setTag(pawi); + widget.applyFromAppWidgetProviderInfo(info, -1, mWidgetPreviewLoader); + } else if (infoList.get(i) instanceof ResolveInfo) { + ResolveInfo info = (ResolveInfo) infoList.get(i); + PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo); + widget.setTag(pasi); + widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader); + } + widget.setVisibility(View.VISIBLE); + widget.ensurePreview(); + } + // TODO(hyunyoungs): Draw the scrollable indicator. + } + + @Override + public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (DEBUG) { + Log.v(TAG, String.format("\nonCreateViewHolder, [widget#=%d]", viewType)); + } + + ViewGroup container = (ViewGroup) mLayoutInflater.inflate( + R.layout.widgets_list_row_view, parent, false); + return new WidgetsRowViewHolder(container); + } + + @Override + public long getItemId(int pos) { + return pos; + } + + private WidgetPreviewLoader getWidgetPreviewLoader() { + if (mWidgetPreviewLoader == null) { + mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache(); + } + return mWidgetPreviewLoader; + } + + /** + * TODO(hyunyoungs): this is temporary. Figure out the width of each widget cell + * and then check if the total sum is longer than the parent width. + */ + private void addScrollableIndicator(int contentSize, ViewGroup parent) { + if (contentSize > 2) { + ViewGroup indicator = (ViewGroup) parent.findViewById(R.id.scrollable_indicator); + indicator.setVisibility(View.VISIBLE); + } + } +} diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java new file mode 100644 index 000000000..c400d6366 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsModel.java @@ -0,0 +1,136 @@ + +package com.android.launcher3.widget; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.support.v7.widget.RecyclerView; +import android.util.Log; + +import com.android.launcher3.IconCache; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator; +import com.android.launcher3.compat.UserHandleCompat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Widgets data model that is used by the adapters of the widget views and controllers. + * + *

The widgets and shortcuts are organized using package name as its index. + */ +public class WidgetsModel { + + private static final String TAG = "WidgetsModel"; + private static final boolean DEBUG = false; + + /* List of packages that is tracked by this model. */ + private List mPackageNames = new ArrayList<>(); + + private Map mPackageItemInfoList = new HashMap<>(); + + /* Map of widgets and shortcuts that are tracked per package. */ + private Map> mWidgetsList = new HashMap<>(); + + /* Notifies the adapter when data changes. */ + private RecyclerView.Adapter mAdapter; + + private Comparator mWidgetAndShortcutNameComparator; + + private IconCache mIconCache; + + public WidgetsModel(Context context, RecyclerView.Adapter adapter) { + mAdapter = adapter; + mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context); + mIconCache = LauncherAppState.getInstance().getIconCache(); + } + + // Access methods that may be deleted if the private fields are made package-private. + public int getPackageSize() { + return mPackageNames.size(); + } + + // Access methods that may be deleted if the private fields are made package-private. + public String getPackageName(int pos) { + return mPackageNames.get(pos); + } + + public PackageItemInfo getPackageItemInfo(String packageName) { + return mPackageItemInfoList.get(packageName); + } + + public List getSortedWidgets(String packageName) { + return mWidgetsList.get(packageName); + } + + public void addWidgetsAndShortcuts(ArrayList widgetsShortcuts, PackageManager pm) { + if (DEBUG) { + Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size()); + } + + // clear the lists. + mPackageNames.clear(); + mWidgetsList.clear(); + + // add and update. + for (Object o: widgetsShortcuts) { + String packageName = ""; + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; + packageName = widgetInfo.provider.getPackageName(); + } else if (o instanceof ResolveInfo) { + ResolveInfo resolveInfo = (ResolveInfo) o; + packageName = resolveInfo.activityInfo.packageName; + } else { + Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s", + o.getClass().toString())); + + } + + ArrayList widgetsShortcutsList = mWidgetsList.get(packageName); + if (widgetsShortcutsList != null) { + widgetsShortcutsList.add(o); + } else { + widgetsShortcutsList = new ArrayList(); + widgetsShortcutsList.add(o); + mWidgetsList.put(packageName, widgetsShortcutsList); + mPackageNames.add(packageName); + } + } + for (String packageName: mPackageNames) { + PackageItemInfo pInfo = mPackageItemInfoList.get(packageName); + if (pInfo == null) { + pInfo = new PackageItemInfo(); + mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), + true /* useLowResIcon */, pInfo); + mPackageItemInfoList.put(packageName, pInfo); + } + } + + // sort. + sortPackageList(); + for (String packageName: mPackageNames) { + Collections.sort(mWidgetsList.get(packageName), mWidgetAndShortcutNameComparator); + } + + // notify. + mAdapter.notifyDataSetChanged(); + } + + private void sortPackageList() { + Collections.sort(mPackageNames, new Comparator() { + @Override + public int compare(String lhs, String rhs) { + String lhsTitle = mPackageItemInfoList.get(lhs).title.toString(); + String rhsTitle = mPackageItemInfoList.get(rhs).title.toString(); + return lhsTitle.compareTo(rhsTitle); + } + }); + } +} diff --git a/src/com/android/launcher3/widget/WidgetsRowView.java b/src/com/android/launcher3/widget/WidgetsRowView.java new file mode 100644 index 000000000..54667384b --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsRowView.java @@ -0,0 +1,90 @@ +/* + * 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.widget; + +import android.content.Context; +import android.view.MotionEvent; +import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +import com.android.launcher3.R; + +/** + * Layout used for widget tray rows for each app. For performance, this view can be replaced with + * a {@link RecyclerView} in the future if we settle on scrollable single row for the widgets. + * If we decide on collapsable grid, then HorizontalScrollView can be replaced with a + * {@link GridLayout}. + */ +public class WidgetsRowView extends HorizontalScrollView { + static final String TAG = "WidgetsRow"; + + private Runnable mOnLayoutListener; + private String mAppName; + + public WidgetsRowView(Context context, String appName) { + super(context, null, 0); + mAppName = appName; + } + + /** + * Clears all the key listeners for the individual widgets. + */ + public void resetChildrenOnKeyListeners() { + int childCount = getChildCount(); + for (int j = 0; j < childCount; ++j) { + getChildAt(j).setOnKeyListener(null); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + TextView tv = (TextView) findViewById(R.id.widget_name); + tv.setText(mAppName); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mOnLayoutListener = null; + } + + public void setOnLayoutListener(Runnable r) { + mOnLayoutListener = r; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mOnLayoutListener != null) { + mOnLayoutListener.run(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean result = super.onTouchEvent(event); + return result; + } + + public static class LayoutParams extends FrameLayout.LayoutParams { + public LayoutParams(int width, int height) { + super(width, height); + } + } +} diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java new file mode 100644 index 000000000..99a192c89 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java @@ -0,0 +1,36 @@ +/* + * 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.widget; + +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +public class WidgetsRowViewHolder extends ViewHolder { + + ViewGroup mContent; + + public WidgetsRowViewHolder(ViewGroup v) { + super(v); + mContent = v; + } + + ViewGroup getContent() { + return mContent; + } +} -- cgit v1.2.3 From 1bf0388602638c5d80f2464df0272a916a99d7f9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 9 Apr 2015 11:18:28 -0700 Subject: Removing UserInitializeReceiver from manifest as it does not exist Change-Id: I5f14d93abadfab0bc2822bc0c68648c72b5a082c --- AndroidManifest.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f43106f18..b61b90c5c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -192,15 +192,6 @@ - - - - - - - -- cgit v1.2.3 From 0f785720667ab8afe4b4620a6c333d382d8659ed Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 8 Apr 2015 10:27:49 -0700 Subject: Initial changes to support a fixed all-apps layout. - Dynamically update padding and background depending on fixed bounds and searchbar - Fixes issue with drag layer bg getting clobbered when rotating launcher - Tapping outside the bounds of all apps should close all apps - Fixing typo causing widgets to not show in sw720dp devices Bug: 20127840 Change-Id: I29c3f905bdee940f938ffe054f58434887073092 --- res/drawable/apps_list_bg.xml | 10 +- res/drawable/apps_list_bg_inset.xml | 23 ---- res/drawable/apps_list_search_bg.xml | 4 +- res/drawable/apps_reveal_bg.xml | 9 +- res/drawable/apps_reveal_bg_inset.xml | 21 --- res/drawable/apps_search_bg.xml | 23 ++++ res/layout-sw600dp/apps_view.xml | 1 - res/layout-sw720dp/launcher.xml | 2 +- res/layout/apps_list_view.xml | 7 +- res/layout/apps_reveal_view.xml | 1 - res/layout/apps_view.xml | 3 - res/values/dimens.xml | 2 + .../launcher3/AppsContainerRecyclerView.java | 16 ++- src/com/android/launcher3/AppsContainerView.java | 151 +++++++++++++++++---- src/com/android/launcher3/AppsGridAdapter.java | 17 ++- src/com/android/launcher3/DeviceProfile.java | 16 ++- src/com/android/launcher3/Launcher.java | 47 +++++-- src/com/android/launcher3/LauncherCallbacks.java | 10 ++ src/com/android/launcher3/LauncherExtension.java | 14 ++ .../LauncherStateTransitionAnimation.java | 48 ++----- src/com/android/launcher3/Workspace.java | 9 +- 21 files changed, 288 insertions(+), 146 deletions(-) delete mode 100644 res/drawable/apps_list_bg_inset.xml delete mode 100644 res/drawable/apps_reveal_bg_inset.xml create mode 100644 res/drawable/apps_search_bg.xml diff --git a/res/drawable/apps_list_bg.xml b/res/drawable/apps_list_bg.xml index 64177c16b..0e56684b5 100644 --- a/res/drawable/apps_list_bg.xml +++ b/res/drawable/apps_list_bg.xml @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + + + + \ No newline at end of file diff --git a/res/drawable/apps_list_bg_inset.xml b/res/drawable/apps_list_bg_inset.xml deleted file mode 100644 index 5ea78952f..000000000 --- a/res/drawable/apps_list_bg_inset.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/drawable/apps_list_search_bg.xml b/res/drawable/apps_list_search_bg.xml index eda33a918..63c4d5591 100644 --- a/res/drawable/apps_list_search_bg.xml +++ b/res/drawable/apps_list_search_bg.xml @@ -18,6 +18,6 @@ android:shape="rectangle"> + android:bottomLeftRadius="2dp" + android:bottomRightRadius="2dp" /> \ No newline at end of file diff --git a/res/drawable/apps_reveal_bg.xml b/res/drawable/apps_reveal_bg.xml index 47c608f85..07505a596 100644 --- a/res/drawable/apps_reveal_bg.xml +++ b/res/drawable/apps_reveal_bg.xml @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + + + + \ No newline at end of file diff --git a/res/drawable/apps_reveal_bg_inset.xml b/res/drawable/apps_reveal_bg_inset.xml deleted file mode 100644 index 61f1c083a..000000000 --- a/res/drawable/apps_reveal_bg_inset.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/drawable/apps_search_bg.xml b/res/drawable/apps_search_bg.xml new file mode 100644 index 000000000..405e8447c --- /dev/null +++ b/res/drawable/apps_search_bg.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml index fba170b2e..e6e0ec358 100644 --- a/res/layout-sw600dp/apps_view.xml +++ b/res/layout-sw600dp/apps_view.xml @@ -19,7 +19,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/apps_container_inset" - android:background="@drawable/apps_customize_bg" android:descendantFocusability="afterDescendants"> diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index 3e42f8489..595c46caf 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -25,8 +25,6 @@ android:id="@+id/app_search_box" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/apps_container_inset" - android:layout_marginRight="@dimen/apps_container_inset" android:padding="16dp" android:hint="@string/apps_view_search_bar_hint" android:maxLines="1" @@ -37,7 +35,7 @@ android:textColor="#4c4c4c" android:textColorHint="#9c9c9c" android:imeOptions="actionDone|flagNoExtractUi" - android:background="@drawable/apps_list_search_bg" + android:background="@drawable/apps_search_bg" android:elevation="4dp" /> + android:descendantFocusability="afterDescendants" /> \ No newline at end of file diff --git a/res/layout/apps_reveal_view.xml b/res/layout/apps_reveal_view.xml index bc93359c1..2951ea4f4 100644 --- a/res/layout/apps_reveal_view.xml +++ b/res/layout/apps_reveal_view.xml @@ -21,5 +21,4 @@ android:layout_gravity="center" android:elevation="15dp" android:visibility="invisible" - android:background="@drawable/apps_reveal_bg" android:focusable="false" /> \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml index 86d67e15f..7f09f7795 100644 --- a/res/layout/apps_view.xml +++ b/res/layout/apps_view.xml @@ -21,9 +21,6 @@ android:id="@+id/apps_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="@dimen/apps_container_inset" - android:paddingBottom="@dimen/apps_container_inset" - android:background="@drawable/apps_customize_bg" android:descendantFocusability="afterDescendants"> 0dp 0dp 8dp + + 8dp 52dp 64dp 24sp diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index c93bacf93..bb2aeb096 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -196,13 +196,27 @@ public class AppsContainerRecyclerView extends RecyclerView } break; case MotionEvent.ACTION_UP: + ViewConfiguration viewConfig = ViewConfiguration.get(getContext()); + float dx = ev.getX() - mDownX; + float dy = ev.getY() - mDownY; + float distance = (float) Math.sqrt(dx * dx + dy * dy); + if (distance < viewConfig.getScaledTouchSlop()) { + Rect backgroundPadding = new Rect(); + getBackground().getPadding(backgroundPadding); + boolean isOutsideBounds = ev.getX() < backgroundPadding.left || + ev.getX() > (getWidth() - backgroundPadding.right); + if (isOutsideBounds) { + Launcher launcher = (Launcher) getContext(); + launcher.showWorkspace(true); + } + } + // Fall through case MotionEvent.ACTION_CANCEL: mDraggingFastScroller = false; animateFastScrollerVisibility(false); break; } return mDraggingFastScroller; - } /** diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 2de45cbcc..559f6eb83 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -19,6 +19,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.InsetDrawable; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; @@ -26,12 +27,13 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.TextView; - import com.android.launcher3.util.Thunk; import java.util.List; @@ -59,8 +61,13 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private EditText mSearchBar; private int mNumAppsPerRow; private Point mLastTouchDownPos = new Point(); - private Rect mPadding = new Rect(); + private Rect mInsets = new Rect(); + private Rect mFixedBounds = new Rect(); private int mContentMarginStart; + // Normal container insets + private int mContainerInset; + // Fixed bounds container insets + private int mFixedBoundsContainerInset; public AppsContainerView(Context context) { this(context, null); @@ -76,6 +83,10 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); Resources res = context.getResources(); + mContainerInset = context.getResources().getDimensionPixelSize( + R.dimen.apps_container_inset); + mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize( + R.dimen.apps_container_fixed_bounds_inset); mLauncher = (Launcher) context; mApps = new AlphabeticalAppsList(context); if (USE_LAYOUT == GRID_LAYOUT) { @@ -125,6 +136,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mApps.removeApps(apps); } + /** + * Hides the search bar + */ + public void hideSearchBar() { + mSearchBar.setVisibility(View.GONE); + updateBackgrounds(); + updatePaddings(); + } + /** * Scrolls this list view to the top. */ @@ -154,45 +174,58 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett ((AppsGridAdapter) mAdapter).setRtl(isRtl); } mSearchBar = (EditText) findViewById(R.id.app_search_box); - mSearchBar.addTextChangedListener(this); - mSearchBar.setOnEditorActionListener(this); + if (mSearchBar != null) { + mSearchBar.addTextChangedListener(this); + mSearchBar.setOnEditorActionListener(this); + } mAppsListView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view); mAppsListView.setApps(mApps); mAppsListView.setNumAppsPerRow(mNumAppsPerRow); mAppsListView.setLayoutManager(mLayoutManager); mAppsListView.setAdapter(mAdapter); mAppsListView.setHasFixedSize(true); - if (isRtl) { - mAppsListView.setPadding( - mAppsListView.getPaddingLeft(), - mAppsListView.getPaddingTop(), - mAppsListView.getPaddingRight() + mContentMarginStart, - mAppsListView.getPaddingBottom()); - } else { - mAppsListView.setPadding( - mAppsListView.getPaddingLeft() + mContentMarginStart, - mAppsListView.getPaddingTop(), - mAppsListView.getPaddingRight(), - mAppsListView.getPaddingBottom()); - } if (mItemDecoration != null) { mAppsListView.addItemDecoration(mItemDecoration); } - mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), - getPaddingBottom()); + updateBackgrounds(); + updatePaddings(); } @Override public void setInsets(Rect insets) { - setPadding(mPadding.left + insets.left, mPadding.top + insets.top, - mPadding.right + insets.right, mPadding.bottom + insets.bottom); + mInsets.set(insets); + updatePaddings(); + } + + /** + * Sets the fixed bounds for this Apps view. + */ + public void setFixedBounds(Context context, Rect fixedBounds) { + if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) { + // Update the number of items in the grid + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) { + mNumAppsPerRow = grid.appsViewNumCols; + mAppsListView.setNumAppsPerRow(mNumAppsPerRow); + if (USE_LAYOUT == GRID_LAYOUT) { + ((AppsGridAdapter) mAdapter).setNumAppsPerRow(mNumAppsPerRow); + } + } + + mFixedBounds.set(fixedBounds); + } + updateBackgrounds(); + updatePaddings(); } @Override public boolean onTouch(View v, MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN || - ev.getAction() == MotionEvent.ACTION_MOVE) { - mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); + break; } return false; } @@ -359,7 +392,9 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { if (!toWorkspace) { // Disable the focus so that the search bar doesn't get focus - mSearchBar.setFocusableInTouchMode(false); + if (mSearchBar != null) { + mSearchBar.setFocusableInTouchMode(false); + } } } @@ -375,11 +410,69 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - if (toWorkspace) { - // Clear the search bar - mSearchBar.setText(""); + if (mSearchBar != null) { + if (toWorkspace) { + // Clear the search bar + mSearchBar.setText(""); + } else { + mSearchBar.setFocusableInTouchMode(true); + } + } + } + + /** + * Update the padding of the Apps view and children. To ensure that the RecyclerView has the + * full width to handle touches right to the edge of the screen, we only apply the top and + * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView + * itself. In particular, the left/right padding is applied to the background of the view, + * and then additionally inset by the start margin. + */ + private void updatePaddings() { + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + boolean hasSearchBar = (mSearchBar != null) && (mSearchBar.getVisibility() == View.VISIBLE); + + if (mFixedBounds.isEmpty()) { + // If there are no fixed bounds, then use the default padding and insets + setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right, + mContainerInset + mInsets.bottom); } else { - mSearchBar.setFocusableInTouchMode(true); + // If there are fixed bounds, then we update the padding to reflect the fixed bounds. + setPadding(mFixedBounds.left, mFixedBounds.top + mFixedBoundsContainerInset, + getMeasuredWidth() - mFixedBounds.right, + mInsets.bottom + mFixedBoundsContainerInset); } + + // Update the apps recycler view + int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + if (isRtl) { + mAppsListView.setPadding(inset, inset, inset + mContentMarginStart, inset); + } else { + mAppsListView.setPadding(inset + mContentMarginStart, inset, inset, inset); + } + + // Update the search bar + if (hasSearchBar) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSearchBar.getLayoutParams(); + lp.leftMargin = lp.rightMargin = inset; + } + } + + /** + * Update the background of the Apps view and children. + */ + private void updateBackgrounds() { + int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + boolean hasSearchBar = (mSearchBar != null) && (mSearchBar.getVisibility() == View.VISIBLE); + + // Update the background of the reveal view and list to be inset with the fixed bound + // insets instead of the default insets + mAppsListView.setBackground(new InsetDrawable( + getContext().getResources().getDrawable( + hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg), + inset, 0, inset, 0)); + getRevealView().setBackground(new InsetDrawable( + getContext().getResources().getDrawable(R.drawable.apps_reveal_bg), + inset, 0, inset, 0)); } } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 5b6967c26..c8ce397f2 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -112,6 +112,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { private LayoutInflater mLayoutInflater; @Thunk AlphabeticalAppsList mApps; + private GridLayoutManager mGridLayoutMgr; private GridSpanSizer mGridSizer; private GridItemDecoration mItemDecoration; private View.OnTouchListener mTouchListener; @@ -135,6 +136,9 @@ class AppsGridAdapter extends RecyclerView.Adapter { mApps = apps; mAppsPerRow = appsPerRow; mGridSizer = new GridSpanSizer(); + mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL, + false); + mGridLayoutMgr.setSpanSizeLookup(mGridSizer); mItemDecoration = new GridItemDecoration(); mLayoutInflater = LayoutInflater.from(context); mTouchListener = touchListener; @@ -149,6 +153,14 @@ class AppsGridAdapter extends RecyclerView.Adapter { mSectionTextPaint.setAntiAlias(true); } + /** + * Sets the number of apps per row. + */ + public void setNumAppsPerRow(int appsPerRow) { + mAppsPerRow = appsPerRow; + mGridLayoutMgr.setSpanCount(appsPerRow); + } + /** * Sets whether we are in RTL mode. */ @@ -167,10 +179,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { * Returns the grid layout manager. */ public GridLayoutManager getLayoutManager(Context context) { - GridLayoutManager layoutMgr = new GridLayoutManager(context, mAppsPerRow, - GridLayoutManager.VERTICAL, false); - layoutMgr.setSpanSizeLookup(mGridSizer); - return layoutMgr; + return mGridLayoutMgr; } /** diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index ea2852080..786f2ce03 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -423,13 +423,21 @@ public class DeviceProfile { allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols)); } - int appsContainerViewPx = res.getDimensionPixelSize(R.dimen.apps_container_width); + int appsContainerViewWidthPx = res.getDimensionPixelSize(R.dimen.apps_container_width); + updateAppsViewNumCols(res, appsContainerViewWidthPx); + } + + public boolean updateAppsViewNumCols(Resources res, int containerWidth) { int appsViewLeftMarginPx = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); - int availableAppsWidthPx = (appsContainerViewPx > 0) ? appsContainerViewPx : - availableWidthPx; - appsViewNumCols = (availableAppsWidthPx - appsViewLeftMarginPx) / + int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; + int numCols = (availableAppsWidthPx - appsViewLeftMarginPx) / (allAppsCellWidthPx + 2 * allAppsCellPaddingPx); + if (numCols != appsViewNumCols) { + appsViewNumCols = numCols; + return true; + } + return false; } void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx, diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 068934e1b..3d2a34647 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -520,6 +520,17 @@ public class Launcher extends Activity public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { mLauncherCallbacks = callbacks; + mLauncherCallbacks.setLauncherAppsCallback(new Launcher.LauncherAppsCallbacks() { + @Override + public void onAllAppsBoundsChanged(Rect bounds) { + mAppsView.setFixedBounds(Launcher.this, bounds); + } + + @Override + public void dismissAllApps() { + showWorkspace(true); + } + }); return true; } @@ -1141,6 +1152,19 @@ public class Launcher extends Activity public void forceExitFullImmersion(); } + public interface LauncherAppsCallbacks { + /** + * Updates launcher to the available space that AllApps can take so as not to overlap with + * any other views. + */ + public void onAllAppsBoundsChanged(Rect bounds); + + /** + * Called to dismiss all apps if it is showing. + */ + public void dismissAllApps(); + } + public interface LauncherOverlayCallbacks { /** * This method indicates whether a call to {@link #enterFullImmersion()} will succeed, @@ -1418,6 +1442,9 @@ public class Launcher extends Activity // Setup Apps mAppsView = (AppsContainerView) findViewById(R.id.apps_view); + if (mLauncherCallbacks != null && mLauncherCallbacks.overrideAllAppsSearch()) { + mAppsView.hideSearchBar(); + } // Setup AppsCustomize mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view); @@ -2587,9 +2614,6 @@ public class Launcher extends Activity } else { showAppsView(true /* animated */, false /* resetListToTop */); } - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onClickAllAppsButton(v); - } } private void showBrokenAppInstallDialog(final String packageName, @@ -3270,7 +3294,9 @@ public class Launcher extends Activity } void showWorkspace(boolean animated, Runnable onCompleteRunnable) { - if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) { + boolean changed = mState != State.WORKSPACE || + mWorkspace.getState() != Workspace.State.NORMAL; + if (changed) { boolean wasInSpringLoadedMode = (mState != State.WORKSPACE); mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL, @@ -3295,11 +3321,13 @@ public class Launcher extends Activity mUserPresent = true; updateAutoAdvanceState(); - // Send an accessibility event to announce the context change - getWindow().getDecorView() - .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (changed) { + // Send an accessibility event to announce the context change + getWindow().getDecorView() + .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - onWorkspaceShown(animated); + onWorkspaceShown(animated); + } } void showOverviewMode(boolean animated) { @@ -3350,6 +3378,9 @@ public class Launcher extends Activity if (toState == State.APPS) { mStateTransitionAnimation.startAnimationToAllApps(animated); + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onAllAppsShown(); + } } else { mStateTransitionAnimation.startAnimationToWidgets(animated); } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index d8128d6e5..2fee81c3d 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -50,6 +50,7 @@ public interface LauncherCallbacks { public void onLauncherProviderChange(); public void finishBindingItems(final boolean upgradePath); public void onClickAllAppsButton(View v); + public void onAllAppsShown(); public void bindAllApplications(ArrayList apps); public void onClickFolderIcon(View v); public void onClickAppShortcut(View v); @@ -87,6 +88,7 @@ public interface LauncherCallbacks { public ComponentName getWallpaperPickerComponent(); public boolean overrideWallpaperDimensions(); public boolean isLauncherPreinstalled(); + public boolean overrideAllAppsSearch(); /** * Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup, @@ -106,4 +108,12 @@ public interface LauncherCallbacks { public Launcher.LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container, Launcher.LauncherOverlayCallbacks callbacks); + /** + * Sets the callbacks to allow any extensions to callback to the launcher. + * + * @param callbacks A set of callbacks to the Launcher, is actually a LauncherAppsCallback, but + * for implementation purposes is passed around as an object. + */ + public void setLauncherAppsCallback(Object callbacks); + } diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index fe9bd6c23..e4fdbbc7c 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -123,6 +123,10 @@ public class LauncherExtension extends Launcher { public void onClickAllAppsButton(View v) { } + @Override + public void onAllAppsShown() { + } + @Override public void bindAllApplications(ArrayList apps) { } @@ -245,6 +249,11 @@ public class LauncherExtension extends Launcher { return false; } + @Override + public boolean overrideAllAppsSearch() { + return false; + } + @Override public boolean isLauncherPreinstalled() { return false; @@ -265,6 +274,11 @@ public class LauncherExtension extends Launcher { return mLauncherOverlay; } + @Override + public void setLauncherAppsCallback(Object callbacks) { + // Do nothing + } + class LauncherExtensionOverlay implements LauncherOverlay { LauncherOverlayCallbacks mLauncherOverlayCallbacks; ViewGroup mOverlayView; diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index e92bfb053..57bd5b2c6 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -174,7 +174,7 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), null, animated, cb); + toView.getRevealView(), animated, false /* hideSearchBar */, cb); } /** @@ -198,7 +198,7 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), null, animated, cb); + toView.getRevealView(), animated, true /* hideSearchBar */, cb); } /** @@ -226,8 +226,8 @@ public class LauncherStateTransitionAnimation { * Creates and starts a new animation to a particular overlay view. */ private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView, - final View contentView, final View revealView, final View pageIndicatorsView, - final boolean animated, final PrivateTransitionCallbacks pCb) { + final View contentView, final View revealView, final boolean animated, + final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); @@ -293,15 +293,6 @@ public class LauncherStateTransitionAnimation { layerViews.put(revealView, BUILD_AND_SET_LAYER); mStateAnimation.play(panelAlphaAndDrift); - // Setup the animation for the page indicators - if (pageIndicatorsView != null) { - pageIndicatorsView.setAlpha(0.01f); - ObjectAnimator indicatorsAlpha = - ObjectAnimator.ofFloat(pageIndicatorsView, "alpha", 1f); - indicatorsAlpha.setDuration(revealDuration); - mStateAnimation.play(indicatorsAlpha); - } - // Setup the animation for the content view contentView.setVisibility(View.VISIBLE); contentView.setAlpha(0f); @@ -354,8 +345,9 @@ public class LauncherStateTransitionAnimation { } } - // Hide the search bar - mCb.onStateTransitionHideSearchBar(); + if (hideSearchBar) { + mCb.onStateTransitionHideSearchBar(); + } // This can hold unnecessary references to views. mStateAnimation = null; @@ -414,8 +406,9 @@ public class LauncherStateTransitionAnimation { // Show the content view contentView.setVisibility(View.VISIBLE); - // Hide the search bar - mCb.onStateTransitionHideSearchBar(); + if (hideSearchBar) { + mCb.onStateTransitionHideSearchBar(); + } dispatchOnLauncherTransitionPrepare(fromView, animated, false); dispatchOnLauncherTransitionStart(fromView, animated, false); @@ -484,8 +477,7 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToWorkspaceFromOverlay(toWorkspaceState, appsView, appsView.getContentView(), - appsView.getRevealView(), null /* pageIndicatorsView */, animated, - onCompleteRunnable, cb); + appsView.getRevealView(), animated, onCompleteRunnable, cb); } /** @@ -516,8 +508,8 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, - widgetsView.getContentView(), widgetsView.getRevealView(), - null, animated, onCompleteRunnable, cb); + widgetsView.getContentView(), widgetsView.getRevealView(), animated, + onCompleteRunnable, cb); } /** @@ -525,8 +517,8 @@ public class LauncherStateTransitionAnimation { */ private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, final View fromView, final View contentView, final View revealView, - final View pageIndicatorsView, final boolean animated, - final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { + final boolean animated, final Runnable onCompleteRunnable, + final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); @@ -631,16 +623,6 @@ public class LauncherStateTransitionAnimation { itemsAlpha.setInterpolator(decelerateInterpolator); mStateAnimation.play(itemsAlpha); - // Setup the page indicators animation - if (pageIndicatorsView != null) { - pageIndicatorsView.setAlpha(1f); - ObjectAnimator indicatorsAlpha = - LauncherAnimUtils.ofFloat(pageIndicatorsView, "alpha", 0f); - indicatorsAlpha.setDuration(revealDuration); - indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); - mStateAnimation.play(indicatorsAlpha); - } - if (material) { // Animate the all apps button float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 8cc99a044..91739711b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1700,7 +1700,11 @@ public class Workspace extends SmoothPagedView mLastCustomContentScrollProgress = progress; - mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f); + // We should only update the drag layer background alpha if we are not in all apps or the + // widgets tray + if (mState == State.NORMAL) { + mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f); + } if (mLauncher.getHotseat() != null) { mLauncher.getHotseat().setTranslationX(translationX); @@ -2252,7 +2256,8 @@ public class Workspace extends SmoothPagedView float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; - float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f; + // We keep the search bar visible on the workspace and in AllApps now + float finalSearchBarAlpha = (stateIsNormal || stateIsNormalHidden) ? 1f : 0f; float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? getOverviewModeTranslationY() : 0; -- cgit v1.2.3 From b863415c17aaaf6012647df5ed14803f89f94bcb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 9 Apr 2015 14:17:14 -0700 Subject: Updating folder UI > Removing 2 folder content implementations and merging unscrollable content into FolderPagedView > Disaling folder sorting > Fixing pageIndicator height Change-Id: I36e35e311eaf99b6c69d450c0765bd41b2d68738 --- res/layout/page_indicator_marker.xml | 4 +- res/layout/user_folder.xml | 52 ++-- res/layout/user_folder_scroll.xml | 106 -------- src/com/android/launcher3/Folder.java | 180 ++++--------- src/com/android/launcher3/FolderCellLayout.java | 330 ------------------------ src/com/android/launcher3/FolderPagedView.java | 129 +++++---- 6 files changed, 161 insertions(+), 640 deletions(-) delete mode 100644 res/layout/user_folder_scroll.xml delete mode 100644 src/com/android/launcher3/FolderCellLayout.java diff --git a/res/layout/page_indicator_marker.xml b/res/layout/page_indicator_marker.xml index 686d27569..564a95811 100644 --- a/res/layout/page_indicator_marker.xml +++ b/res/layout/page_indicator_marker.xml @@ -16,8 +16,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 1e1d1eeb4..0eb1fd8bb 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -69,7 +69,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * results in CellLayout being measured as UNSPECIFIED, which it does not support. */ private static final int MIN_CONTENT_DIMEN = 5; - private static final boolean ALLOW_FOLDER_SCROLL = true; static final int STATE_NONE = -1; static final int STATE_SMALL = 0; @@ -101,6 +100,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private final Alarm mReorderAlarm = new Alarm(); private final Alarm mOnExitAlarm = new Alarm(); + private final Alarm mOnScrollHintAlarm = new Alarm(); + @Thunk final Alarm mScrollPauseAlarm = new Alarm(); @Thunk final ArrayList mItemsInReadingOrder = new ArrayList(); @@ -116,7 +117,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Thunk FolderIcon mFolderIcon; - @Thunk FolderContent mContent; + @Thunk FolderPagedView mContent; @Thunk View mContentWrapper; FolderEditText mFolderName; @@ -149,11 +150,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Folder scrolling private int mScrollAreaOffset; - private Alarm mOnScrollHintAlarm; - @Thunk Alarm mScrollPauseAlarm; - - // TODO: Use {@link #mContent} once {@link #ALLOW_FOLDER_SCROLL} is removed. - @Thunk FolderPagedView mPagedView; @Thunk int mScrollHintDir = DragController.SCROLL_NONE; @Thunk int mCurrentScrollDir = DragController.SCROLL_NONE; @@ -186,18 +182,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // name is complete, we have something to focus on, thus hiding the cursor and giving // reliable behavior when clicking the text field (since it will always gain focus on click). setFocusableInTouchMode(true); - - if (ALLOW_FOLDER_SCROLL) { - mOnScrollHintAlarm = new Alarm(); - mScrollPauseAlarm = new Alarm(); - } } @Override protected void onFinishInflate() { super.onFinishInflate(); mContentWrapper = findViewById(R.id.folder_content_wrapper); - mContent = (FolderContent) findViewById(R.id.folder_content); + mContent = (FolderPagedView) findViewById(R.id.folder_content); mContent.setFolder(this); mFolderName = (FolderEditText) findViewById(R.id.folder_name); @@ -211,16 +202,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setInputType(mFolderName.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); - mFooter = ALLOW_FOLDER_SCROLL ? findViewById(R.id.folder_footer) : mFolderName; + mFooter = findViewById(R.id.folder_footer); + updateFooterHeight(); + } + + public void updateFooterHeight() { // We find out how tall footer wants to be (it is set to wrap_content), so that // we can allocate the appropriate amount of space for it. int measureSpec = MeasureSpec.UNSPECIFIED; mFooter.measure(measureSpec, measureSpec); mFooterHeight = mFooter.getMeasuredHeight(); - - if (ALLOW_FOLDER_SCROLL) { - mPagedView = (FolderPagedView) mContent; - } } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -398,8 +389,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * @return A new UserFolder. */ static Folder fromXml(Context context) { - return (Folder) LayoutInflater.from(context).inflate( - ALLOW_FOLDER_SCROLL ? R.layout.user_folder_scroll : R.layout.user_folder, null); + return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null); } /** @@ -424,12 +414,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void animateOpen() { if (!(getParent() instanceof DragLayer)) return; - if (ALLOW_FOLDER_SCROLL) { - mPagedView.completePendingPageChanges(); - if (!(mDragInProgress && mPagedView.mIsSorted)) { - // Open on the first page. - mPagedView.snapToPageImmediately(0); - } + mContent.completePendingPageChanges(); + if (!(mDragInProgress && mContent.mIsSorted)) { + // Open on the first page. + mContent.snapToPageImmediately(0); } Animator openFolderAnim = null; @@ -533,10 +521,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mDragController.forceTouchMove(); } - if (ALLOW_FOLDER_SCROLL) { - FolderPagedView pages = (FolderPagedView) mContent; - pages.verifyVisibleHighResIcons(pages.getNextPage()); - } + FolderPagedView pages = (FolderPagedView) mContent; + pages.verifyVisibleHighResIcons(pages.getNextPage()); } public void beginExternalDrag(ShortcutInfo item) { @@ -544,7 +530,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mEmptyCellRank = mContent.allocateRankForNewItem(item); mIsExternalDrag = true; mDragInProgress = true; - if (ALLOW_FOLDER_SCROLL && mPagedView.mIsSorted) { + + if (mContent.mIsSorted) { mScrollPauseAlarm.setOnAlarmListener(null); mScrollPauseAlarm.cancelAlarm(); mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY); @@ -601,11 +588,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void onDragEnter(DragObject d) { mPrevTargetRank = -1; mOnExitAlarm.cancelAlarm(); - if (ALLOW_FOLDER_SCROLL) { - // Get the area offset such that the folder only closes if half the drag icon width - // is outside the folder area - mScrollAreaOffset = d.dragView.getDragRegionWidth() / 2 - d.xOffset; - } + // Get the area offset such that the folder only closes if half the drag icon width + // is outside the folder area + mScrollAreaOffset = d.dragView.getDragRegionWidth() / 2 - d.xOffset; } OnAlarmListener mReorderAlarmListener = new OnAlarmListener() { @@ -632,7 +617,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } @Thunk void onDragOver(DragObject d, int reorderDelay) { - if (ALLOW_FOLDER_SCROLL && mScrollPauseAlarm.alarmPending()) { + if (mScrollPauseAlarm.alarmPending()) { return; } final float[] r = new float[2]; @@ -645,27 +630,23 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mPrevTargetRank = mTargetRank; } - if (!ALLOW_FOLDER_SCROLL) { - return; - } - float x = r[0]; - int currentPage = mPagedView.getNextPage(); + int currentPage = mContent.getNextPage(); - float cellOverlap = mPagedView.getCurrentCellLayout().getCellWidth() + float cellOverlap = mContent.getCurrentCellLayout().getCellWidth() * ICON_OVERSCROLL_WIDTH_FACTOR; boolean isOutsideLeftEdge = x < cellOverlap; boolean isOutsideRightEdge = x > (getWidth() - cellOverlap); - if (currentPage > 0 && (mPagedView.rtlLayout ? isOutsideRightEdge : isOutsideLeftEdge)) { + if (currentPage > 0 && (mContent.rtlLayout ? isOutsideRightEdge : isOutsideLeftEdge)) { showScrollHint(DragController.SCROLL_LEFT, d); - } else if (currentPage < (mPagedView.getPageCount() - 1) - && (mPagedView.rtlLayout ? isOutsideLeftEdge : isOutsideRightEdge)) { + } else if (currentPage < (mContent.getPageCount() - 1) + && (mContent.rtlLayout ? isOutsideLeftEdge : isOutsideRightEdge)) { showScrollHint(DragController.SCROLL_RIGHT, d); } else { mOnScrollHintAlarm.cancelAlarm(); if (mScrollHintDir != DragController.SCROLL_NONE) { - mPagedView.clearScrollHint(); + mContent.clearScrollHint(); mScrollHintDir = DragController.SCROLL_NONE; } } @@ -674,7 +655,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private void showScrollHint(int direction, DragObject d) { // Show scroll hint on the right if (mScrollHintDir != direction) { - mPagedView.showScrollHint(direction); + mContent.showScrollHint(direction); mScrollHintDir = direction; } @@ -714,13 +695,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } mReorderAlarm.cancelAlarm(); - if (ALLOW_FOLDER_SCROLL) { - mOnScrollHintAlarm.cancelAlarm(); - mScrollPauseAlarm.cancelAlarm(); - if (mScrollHintDir != DragController.SCROLL_NONE) { - mPagedView.clearScrollHint(); - mScrollHintDir = DragController.SCROLL_NONE; - } + mOnScrollHintAlarm.cancelAlarm(); + mScrollPauseAlarm.cancelAlarm(); + if (mScrollHintDir != DragController.SCROLL_NONE) { + mContent.clearScrollHint(); + mScrollHintDir = DragController.SCROLL_NONE; } } @@ -1088,21 +1067,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList }; } - if (ALLOW_FOLDER_SCROLL) { - // If the icon was dropped while the page was being scrolled, we need to compute - // the target location again such that the icon is placed of the final page. - if (!mPagedView.rankOnCurrentPage(mEmptyCellRank)) { - // Reorder again. - mTargetRank = getTargetRank(d, null); + // If the icon was dropped while the page was being scrolled, we need to compute + // the target location again such that the icon is placed of the final page. + if (!mContent.rankOnCurrentPage(mEmptyCellRank)) { + // Reorder again. + mTargetRank = getTargetRank(d, null); - // Rearrange items immediately. - mReorderAlarmListener.onAlarm(mReorderAlarm); + // Rearrange items immediately. + mReorderAlarmListener.onAlarm(mReorderAlarm); - mOnScrollHintAlarm.cancelAlarm(); - mScrollPauseAlarm.cancelAlarm(); - } - mPagedView.completePendingPageChanges(); + mOnScrollHintAlarm.cancelAlarm(); + mScrollPauseAlarm.cancelAlarm(); } + mContent.completePendingPageChanges(); View currentDragView; ShortcutInfo si = mCurrentDragInfo; @@ -1252,10 +1229,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public void onAlarm(Alarm alarm) { if (mCurrentScrollDir == DragController.SCROLL_LEFT) { - mPagedView.scrollLeft(); + mContent.scrollLeft(); mScrollHintDir = DragController.SCROLL_NONE; } else if (mCurrentScrollDir == DragController.SCROLL_RIGHT) { - mPagedView.scrollRight(); + mContent.scrollRight(); mScrollHintDir = DragController.SCROLL_NONE; } else { // This should not happen @@ -1286,69 +1263,4 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList onDragOver(mDragObject, 1); } } - - public static interface FolderContent { - void setFolder(Folder f); - - void removeItem(View v); - - boolean isFull(); - int getItemCount(); - - int getDesiredWidth(); - int getDesiredHeight(); - void setFixedSize(int width, int height); - - /** - * Iterates over all its items in a reading order. - * @return the view for which the operator returned true. - */ - View iterateOverItems(ItemOperator op); - View getLastItem(); - - String getAccessibilityDescription(); - - /** - * Binds items to the layout. - * @return list of items that could not be bound, probably because we hit the max size limit. - */ - ArrayList bindItems(ArrayList children); - - /** - * Create space for a new item, and returns the rank for that item. - * Resizes the content if necessary. - */ - int allocateRankForNewItem(ShortcutInfo info); - - View createAndAddViewForRank(ShortcutInfo item, int rank); - - /** - * Adds the {@param view} to the layout based on {@param rank} and updated the position - * related attributes. It assumes that {@param item} is already attached to the view. - */ - void addViewForRank(View view, ShortcutInfo item, int rank); - - /** - * Reorders the items such that the {@param empty} spot moves to {@param target} - */ - void realTimeReorder(int empty, int target); - - /** - * @return the rank of the cell nearest to the provided pixel position. - */ - int findNearestArea(int pixelX, int pixelY); - - /** - * Updates position and rank of all the children in the view based. - * @param list the ordered list of children. - * @param itemCount if greater than the total children count, empty spaces are left - * at the end. - */ - void arrangeChildren(ArrayList list, int itemCount); - - /** - * Sets the focus on the first visible child. - */ - void setFocusOnFirstChild(); - } } diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java deleted file mode 100644 index 8585addd5..000000000 --- a/src/com/android/launcher3/FolderCellLayout.java +++ /dev/null @@ -1,330 +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.Context; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; - -import com.android.launcher3.Workspace.ItemOperator; - -import java.util.ArrayList; - -public class FolderCellLayout extends CellLayout implements Folder.FolderContent { - - private static final int REORDER_ANIMATION_DURATION = 230; - private static final int START_VIEW_REORDER_DELAY = 30; - private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; - - private static final int[] sTempPosArray = new int[2]; - - private final FolderKeyEventListener mKeyListener = new FolderKeyEventListener(); - private final LayoutInflater mInflater; - private final IconCache mIconCache; - - private final int mMaxCountX; - private final int mMaxCountY; - private final int mMaxNumItems; - - // Indicates the last number of items used to set up the grid size - private int mAllocatedContentSize; - - private Folder mFolder; - private FocusIndicatorView mFocusIndicatorView; - - public FolderCellLayout(Context context) { - this(context, null); - } - - public FolderCellLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public FolderCellLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mMaxCountX = (int) grid.numColumns; - mMaxCountY = (int) grid.numRows; - mMaxNumItems = mMaxCountX * mMaxCountY; - - mInflater = LayoutInflater.from(context); - mIconCache = app.getIconCache(); - - setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); - getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); - setInvertIfRtl(true); - } - - @Override - public void setFolder(Folder folder) { - mFolder = folder; - mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); - } - - /** - * Sets up the grid size such that {@param count} items can fit in the grid. - * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while - * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. - */ - private void setupContentDimensions(int count) { - mAllocatedContentSize = count; - int countX = getCountX(); - int countY = getCountY(); - boolean done = false; - - while (!done) { - int oldCountX = countX; - int oldCountY = countY; - if (countX * countY < count) { - // Current grid is too small, expand it - if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) { - countX++; - } else if (countY < mMaxCountY) { - countY++; - } - if (countY == 0) countY++; - } else if ((countY - 1) * countX >= count && countY >= countX) { - countY = Math.max(0, countY - 1); - } else if ((countX - 1) * countY >= count) { - countX = Math.max(0, countX - 1); - } - done = countX == oldCountX && countY == oldCountY; - } - setGridSize(countX, countY); - } - - @Override - public ArrayList bindItems(ArrayList items) { - ArrayList extra = new ArrayList(); - setupContentDimensions(Math.min(items.size(), mMaxNumItems)); - - int countX = getCountX(); - int rank = 0; - for (ShortcutInfo item : items) { - if (rank >= mMaxNumItems) { - extra.add(item); - continue; - } - - item.rank = rank; - item.cellX = rank % countX; - item.cellY = rank / countX; - addNewView(item); - rank++; - } - return extra; - } - - @Override - public int allocateRankForNewItem(ShortcutInfo info) { - int rank = getItemCount(); - mFolder.rearrangeChildren(rank + 1); - return rank; - } - - @Override - public View createAndAddViewForRank(ShortcutInfo item, int rank) { - updateItemXY(item, rank); - return addNewView(item); - } - - @Override - public void addViewForRank(View view, ShortcutInfo item, int rank) { - updateItemXY(item, rank); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); - lp.cellX = item.cellX; - lp.cellY = item.cellY; - addViewToCellLayout(view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); - } - - @Override - public void removeItem(View v) { - removeView(v); - } - - /** - * Updates the item cellX and cellY position - */ - private void updateItemXY(ShortcutInfo item, int rank) { - item.rank = rank; - int countX = getCountX(); - item.cellX = rank % countX; - item.cellY = rank / countX; - } - - private View addNewView(ShortcutInfo item) { - final BubbleTextView textView = (BubbleTextView) mInflater.inflate( - R.layout.folder_application, getShortcutsAndWidgets(), false); - textView.applyFromShortcutInfo(item, mIconCache, false); - textView.setOnClickListener(mFolder); - textView.setOnLongClickListener(mFolder); - textView.setOnFocusChangeListener(mFocusIndicatorView); - textView.setOnKeyListener(mKeyListener); - - CellLayout.LayoutParams lp = new CellLayout.LayoutParams( - item.cellX, item.cellY, item.spanX, item.spanY); - addViewToCellLayout(textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); - return textView; - } - - /** - * Refer {@link #findNearestArea(int, int, int, int, View, boolean, int[])} - */ - @Override - public int findNearestArea(int pixelX, int pixelY) { - findNearestArea(pixelX, pixelY, 1, 1, null, false, sTempPosArray); - if (mFolder.isLayoutRtl()) { - sTempPosArray[0] = getCountX() - sTempPosArray[0] - 1; - } - - // Convert this position to rank. - return Math.min(mAllocatedContentSize - 1, - sTempPosArray[1] * getCountX() + sTempPosArray[0]); - } - - @Override - public boolean isFull() { - return getItemCount() >= mMaxNumItems; - } - - @Override - public int getItemCount() { - return getShortcutsAndWidgets().getChildCount(); - } - - @Override - public void arrangeChildren(ArrayList list, int itemCount) { - setupContentDimensions(itemCount); - removeAllViews(); - - int newX, newY; - int rank = 0; - int countX = getCountX(); - for (View v : list) { - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - newX = rank % countX; - newY = rank / countX; - ItemInfo info = (ItemInfo) v.getTag(); - if (info.cellX != newX || info.cellY != newY || info.rank != rank) { - info.cellX = newX; - info.cellY = newY; - info.rank = rank; - LauncherModel.addOrMoveItemInDatabase(getContext(), info, - mFolder.mInfo.id, 0, info.cellX, info.cellY); - } - lp.cellX = info.cellX; - lp.cellY = info.cellY; - rank ++; - addViewToCellLayout(v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); - } - } - - @Override - public View iterateOverItems(ItemOperator op) { - for (int j = 0; j < getCountY(); j++) { - for (int i = 0; i < getCountX(); i++) { - View v = getChildAt(i, j); - if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) { - return v; - } - } - } - return null; - } - - @Override - public String getAccessibilityDescription() { - return String.format(getContext().getString(R.string.folder_opened), - getCountX(), getCountY()); - } - - @Override - public void setFocusOnFirstChild() { - View firstChild = getChildAt(0, 0); - if (firstChild != null) { - firstChild.requestFocus(); - } - } - - @Override - public View getLastItem() { - int lastRank = getShortcutsAndWidgets().getChildCount() - 1; - // count can be zero if the folder is not yet laid out. - int count = getCountX(); - if (count > 0) { - return getShortcutsAndWidgets().getChildAt(lastRank % count, lastRank / count); - } else { - return getShortcutsAndWidgets().getChildAt(lastRank); - } - } - - @Override - public void realTimeReorder(int empty, int target) { - boolean wrap; - int startX; - int endX; - int startY; - int delay = 0; - float delayAmount = START_VIEW_REORDER_DELAY; - - int countX = getCountX(); - int emptyX = empty % getCountX(); - int emptyY = empty / countX; - - int targetX = target % countX; - int targetY = target / countX; - - if (target > empty) { - wrap = emptyX == countX - 1; - startY = wrap ? emptyY + 1 : emptyY; - for (int y = startY; y <= targetY; y++) { - startX = y == emptyY ? emptyX + 1 : 0; - endX = y < targetY ? countX - 1 : targetX; - for (int x = startX; x <= endX; x++) { - View v = getChildAt(x,y); - if (animateChildToPosition(v, emptyX, emptyY, - REORDER_ANIMATION_DURATION, delay, true, true)) { - emptyX = x; - emptyY = y; - delay += delayAmount; - delayAmount *= VIEW_REORDER_DELAY_FACTOR; - } - } - } - } else { - wrap = emptyX == 0; - startY = wrap ? emptyY - 1 : emptyY; - for (int y = startY; y >= targetY; y--) { - startX = y == emptyY ? emptyX - 1 : countX - 1; - endX = y > targetY ? 0 : targetX; - for (int x = startX; x >= endX; x--) { - View v = getChildAt(x,y); - if (animateChildToPosition(v, emptyX, emptyY, - REORDER_ANIMATION_DURATION, delay, true, true)) { - emptyX = x; - emptyY = y; - delay += delayAmount; - delayAmount *= VIEW_REORDER_DELAY_FACTOR; - } - } - } - } - } -} diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 1c42d2592..247c55246 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -19,7 +19,6 @@ package com.android.launcher3; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; -import android.util.LayoutDirection; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -42,10 +41,15 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -public class FolderPagedView extends PagedView implements Folder.FolderContent { +public class FolderPagedView extends PagedView { private static final String TAG = "FolderPagedView"; + private static final boolean ALLOW_FOLDER_SCROLL = true; + + // To enable this flag, user_folder.xml needs to be modified to add sort button. + private static final boolean ALLOW_ITEM_SORTING = false; + private static final int REORDER_ANIMATION_DURATION = 230; private static final int START_VIEW_REORDER_DELAY = 30; private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; @@ -96,32 +100,38 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { setDataIsReady(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE); - mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE); + if (ALLOW_FOLDER_SCROLL) { + mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE); + mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE); + } else { + mMaxCountX = (int) grid.numColumns; + mMaxCountY = (int) grid.numRows; + } + mMaxItemsPerPage = mMaxCountX * mMaxCountY; mInflater = LayoutInflater.from(context); mIconCache = app.getIconCache(); - rtlLayout = getResources().getConfiguration().getLayoutDirection() == LayoutDirection.RTL; + rtlLayout = getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL; } - @Override public void setFolder(Folder folder) { mFolder = folder; mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); mKeyListener = new PagedFolderKeyEventListener(folder); + mPageIndicator = folder.findViewById(R.id.folder_page_indicator); - mSortButton = folder.findViewById(R.id.folder_sort); - mSortButton.setOnClickListener(new OnClickListener() { + if (ALLOW_ITEM_SORTING) { + // Initialize {@link #mSortSwitch} and {@link #mSortButton}. + mSortButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onSortClicked(); - } - }); - mPageIndicator = folder.findViewById(R.id.folder_page_indicator); - mSortSwitch = (Switch) folder.findViewById(R.id.folder_sort_switch); + @Override + public void onClick(View v) { + onSortClicked(); + } + }); + } } private void onSortClicked() { @@ -138,9 +148,11 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { private void setIsSorted(boolean isSorted, boolean saveChanges) { mIsSorted = isSorted; - mSortSwitch.setChecked(isSorted); - mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted, - saveChanges ? mFolder.mLauncher : null); + if (ALLOW_ITEM_SORTING) { + mSortSwitch.setChecked(isSorted); + mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted, + saveChanges ? mFolder.mLauncher : null); + } } /** @@ -282,26 +294,34 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } } - @Override + /** + * Binds items to the layout. + * @return list of items that could not be bound, probably because we hit the max size limit. + */ public ArrayList bindItems(ArrayList items) { - mIsSorted = mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED); + mIsSorted = ALLOW_ITEM_SORTING && mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED); ArrayList icons = new ArrayList(); + ArrayList extra = new ArrayList(); + for (ShortcutInfo item : items) { - icons.add(createNewView(item)); + if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) { + extra.add(item); + } else { + icons.add(createNewView(item)); + } } arrangeChildren(icons, icons.size(), false); - return new ArrayList(); + return extra; } /** * Create space for a new item at the end, and returns the rank for that item. * Also sets the current page to the last page. */ - @Override public int allocateRankForNewItem(ShortcutInfo info) { int rank = getItemCount(); ArrayList views = new ArrayList(mFolder.getItemsInReadingOrder()); - if (mIsSorted) { + if (ALLOW_ITEM_SORTING && mIsSorted) { View tmp = new View(getContext()); tmp.setTag(info); int index = Collections.binarySearch(views, tmp, new ViewComparator()); @@ -321,14 +341,16 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { return rank; } - @Override public View createAndAddViewForRank(ShortcutInfo item, int rank) { View icon = createNewView(item); addViewForRank(icon, item, rank); return icon; } - @Override + /** + * Adds the {@param view} to the layout based on {@param rank} and updated the position + * related attributes. It assumes that {@param item} is already attached to the view. + */ public void addViewForRank(View view, ShortcutInfo item, int rank) { int pagePos = rank % mMaxItemsPerPage; int pageNo = rank / mMaxItemsPerPage; @@ -388,14 +410,12 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { return page; } - @Override public void setFixedSize(int width, int height) { for (int i = getChildCount() - 1; i >= 0; i --) { ((CellLayout) getChildAt(i)).setFixedSize(width, height); } } - @Override public void removeItem(View v) { for (int i = getChildCount() - 1; i >= 0; i --) { getPageAt(i).removeView(v); @@ -412,7 +432,6 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { * at the end, otherwise it is ignored. * */ - @Override public void arrangeChildren(ArrayList list, int itemCount) { arrangeChildren(list, itemCount, true); } @@ -488,19 +507,26 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { setCurrentPage(0); } - setIsSorted(isSorted, saveChanges); + setEnableOverscroll(getPageCount() > 1); // Update footer - if (getPageCount() > 1) { - mPageIndicator.setVisibility(View.VISIBLE); - mSortButton.setVisibility(View.VISIBLE); - mFolder.mFolderName.setGravity(rtlLayout ? Gravity.RIGHT : Gravity.LEFT); - setEnableOverscroll(true); + if (ALLOW_ITEM_SORTING) { + setIsSorted(isSorted, saveChanges); + if (getPageCount() > 1) { + mPageIndicator.setVisibility(View.VISIBLE); + mSortButton.setVisibility(View.VISIBLE); + mFolder.mFolderName.setGravity(rtlLayout ? Gravity.RIGHT : Gravity.LEFT); + } else { + mPageIndicator.setVisibility(View.GONE); + mSortButton.setVisibility(View.GONE); + mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL); + } } else { - mPageIndicator.setVisibility(View.GONE); - mSortButton.setVisibility(View.GONE); - mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL); - setEnableOverscroll(false); + int indicatorVisibility = mPageIndicator.getVisibility(); + mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE); + if (indicatorVisibility != mPageIndicator.getVisibility()) { + mFolder.updateFooterHeight(); + } } } @@ -521,7 +547,6 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { return getPageCount() > 0 ? getPageAt(0).getDesiredHeight() : 0; } - @Override public int getItemCount() { int lastPageIndex = getChildCount() - 1; if (lastPageIndex < 0) { @@ -532,7 +557,9 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { + lastPageIndex * mMaxItemsPerPage; } - @Override + /** + * @return the rank of the cell nearest to the provided pixel position. + */ public int findNearestArea(int pixelX, int pixelY) { int pageIndex = getNextPage(); CellLayout page = getPageAt(pageIndex); @@ -550,12 +577,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { R.drawable.ic_pageindicator_default_folder); } - @Override public boolean isFull() { - return false; + return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage; } - @Override public View getLastItem() { if (getChildCount() < 1) { return null; @@ -569,7 +594,10 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } } - @Override + /** + * Iterates over all its items in a reading order. + * @return the view for which the operator returned true. + */ public View iterateOverItems(ItemOperator op) { for (int k = 0 ; k < getChildCount(); k++) { CellLayout page = getPageAt(k); @@ -585,13 +613,14 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { return null; } - @Override public String getAccessibilityDescription() { return String.format(getContext().getString(R.string.folder_opened), mGridCountX, mGridCountY); } - @Override + /** + * Sets the focus on the first visible child. + */ public void setFocusOnFirstChild() { View firstChild = getCurrentCellLayout().getChildAt(0, 0); if (firstChild != null) { @@ -605,7 +634,7 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { if (mFolder != null) { mFolder.updateTextViewFocus(); } - if (mSortOperationPending && getNextPage() == 0) { + if (ALLOW_ITEM_SORTING && mSortOperationPending && getNextPage() == 0) { post(new Runnable() { @Override @@ -680,7 +709,9 @@ public class FolderPagedView extends PagedView implements Folder.FolderContent { } } - @Override + /** + * Reorders the items such that the {@param empty} spot moves to {@param target} + */ public void realTimeReorder(int empty, int target) { completePendingPageChanges(); int delay = 0; -- cgit v1.2.3 From a23dab9c646f2f59b503bc4436183d477b8ad492 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 9 Apr 2015 16:54:31 -0700 Subject: WidgetTray UI improvement - Long application title should be ellipsized - Disable the drawable when the view is no longer attached to the window - Save the PackageItemInfo to the WidgetModel once full res icon bitmap is loaded b/19897708 Change-Id: I96f260f9d165b206905a127fcee9879556c3d0c4 --- res/layout/widget_cell.xml | 2 +- res/layout/widgets_list_row_view.xml | 2 ++ src/com/android/launcher3/widget/WidgetCell.java | 2 +- src/com/android/launcher3/widget/WidgetsListAdapter.java | 1 + src/com/android/launcher3/widget/WidgetsModel.java | 4 ++++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index 1286a622b..9e91f677e 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -41,7 +41,7 @@ android:layout_weight="1" android:gravity="start" android:singleLine="true" - android:ellipsize="marquee" + android:ellipsize="end" android:fadingEdge="horizontal" android:textColor="#FFFFFFFF" diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index c7863c7c3..017b45066 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -52,6 +52,8 @@ android:paddingTop="8dp" android:paddingLeft="16dp" android:paddingRight="16dp" + android:singleLine="true" + android:ellipsize="end" android:gravity="start|center_vertical" android:textColor="@color/widgets_view_section_text_color" android:background="@color/widget_text_panel" diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index ccd67ce41..93ee94a59 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -120,7 +120,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString())); } super.onDetachedFromWindow(); - deletePreview(false); + deletePreview(true); } public void deletePreview(boolean recycleImage) { diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index d0d1e60b4..8d1f20c0c 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -124,6 +124,7 @@ public class WidgetsListAdapter extends Adapter { if (infoOut.usingLowResIcon) { mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), false /* useLowResIcon */, infoOut); + mWidgetsModel.setPackageItemInfo(packageName, infoOut); } ((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title); ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image); diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java index c400d6366..463c79ec2 100644 --- a/src/com/android/launcher3/widget/WidgetsModel.java +++ b/src/com/android/launcher3/widget/WidgetsModel.java @@ -133,4 +133,8 @@ public class WidgetsModel { } }); } + + public void setPackageItemInfo(String packageName, PackageItemInfo infoOut) { + mPackageItemInfoList.put(packageName, infoOut); + } } -- cgit v1.2.3 From 211290119a246c85faccf77cee99a26f79347e1c Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 9 Apr 2015 17:58:14 -0700 Subject: Removing unused View ctor introduced in L. - This prevents us from running on K/pre-K --- src/com/android/launcher3/widget/WidgetsContainerView.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 6580ab4ff..5aa80a9e2 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -92,10 +92,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie } public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr); mLauncher = (Launcher) context; mDragController = mLauncher.getDragController(); -- cgit v1.2.3 From ff312e53abd66e6731157b1a7ddf2e9901ebba53 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 9 Apr 2015 18:32:21 -0700 Subject: Build fix Change-Id: I2cbf90dada48ccff17235fb63d66684d2c4eb791 --- src/com/android/launcher3/FolderPagedView.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 247c55246..617489271 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -124,16 +124,12 @@ public class FolderPagedView extends PagedView { if (ALLOW_ITEM_SORTING) { // Initialize {@link #mSortSwitch} and {@link #mSortButton}. - mSortButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - onSortClicked(); - } - }); } } + /** + * Called when sort button is clicked. + */ private void onSortClicked() { if (mSortOperationPending) { return; -- cgit v1.2.3 From 75cc825f3f1c266ccc654ee89d2dcffb7c65890a Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 10 Apr 2015 10:19:58 -0700 Subject: Fixing regression in AllApps being aggressively dismissed after returning to Launcher. - Adding workaround to prevent the built-in search box from being focused when AllApps is shown again Bug: 20143148 Change-Id: Id546d3e5fedc724d95e067d26115ee716ac1d626 --- res/layout/apps_list_view.xml | 3 +- src/com/android/launcher3/AppsContainerView.java | 79 ++++++++++++++---------- src/com/android/launcher3/Launcher.java | 2 +- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index 595c46caf..dfb7b588d 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -20,7 +20,8 @@ android:layout_height="match_parent" android:orientation="vertical" android:elevation="15dp" - android:visibility="gone"> + android:visibility="gone" + android:focusableInTouchMode="true"> Date: Thu, 2 Apr 2015 10:27:03 -0700 Subject: Deduping shortcuts to app-shortcuts if they have a valid intent > Only deduping shortcuts for the primary user as custom shortcuts for secondary users is not supported. Change-Id: If129dee64a395602006ebb996d4b09b93b89084f --- .../android/launcher3/InstallShortcutReceiver.java | 42 ++++++++++++++ src/com/android/launcher3/LauncherProvider.java | 65 +++++++++++++++++++++- .../compat/LauncherActivityInfoCompat.java | 9 +++ 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 0db22a499..5422de951 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.text.TextUtils; @@ -147,6 +148,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0)); PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context); + info = convertToLauncherActivityIfPossible(info); queuePendingShortcutInfo(info, context); } @@ -373,6 +375,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } return packageName; } + + public boolean isLuncherActivity() { + return activityInfo != null; + } } private static PendingInstallShortcutInfo decode(String encoded, Context context) { @@ -420,4 +426,40 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } return null; } + + /** + * Tries to create a new PendingInstallShortcutInfo which represents the same target, + * but is an app target and not a shortcut. + * @return the newly created info or the original one. + */ + private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible( + PendingInstallShortcutInfo original) { + if (original.isLuncherActivity()) { + // Already an activity target + return original; + } + if (isValidShortcutLaunchIntent(original.launchIntent) + || !original.user.equals(UserHandleCompat.myUserHandle())) { + // We can only convert shortcuts which point to a main activity in the current user. + return original; + } + + PackageManager pm = original.mContext.getPackageManager(); + ResolveInfo info = pm.resolveActivity(original.launchIntent, 0); + + if (info == null) { + return original; + } + + // Ignore any conflicts in the label name, as that can change based on locale. + LauncherActivityInfoCompat launcherInfo = LauncherActivityInfoCompat + .fromResolveInfo(info, original.mContext); + return new PendingInstallShortcutInfo(launcherInfo, original.mContext); + } + + public static boolean isLauncherActivity(Intent intent, Context context) { + Intent data = new Intent().putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent); + PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context); + return convertToLauncherActivityIfPossible(info).isLuncherActivity(); + } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 1f59533cc..d75ef738a 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -37,6 +37,7 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; +import android.database.sqlite.SQLiteStatement; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -63,7 +64,7 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 23; + private static final int DATABASE_VERSION = 24; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -617,7 +618,9 @@ public class LauncherProvider extends ContentProvider { break; } } - case 23: { + case 23: + convertShortcutsToLauncherActivities(db); + case 24: { // DB Upgraded successfully return; } @@ -636,7 +639,6 @@ public class LauncherProvider extends ContentProvider { createEmptyDB(db); } - /** * Clears all the data for a fresh start. */ @@ -646,6 +648,63 @@ public class LauncherProvider extends ContentProvider { onCreate(db); } + /** + * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid + * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}. + */ + private void convertShortcutsToLauncherActivities(SQLiteDatabase db) { + db.beginTransaction(); + Cursor c = null; + SQLiteStatement updateStmt = null; + + try { + // Only consider the primary user as other users can't have a shortcut. + long userSerial = UserManagerCompat.getInstance(mContext) + .getSerialNumberForUser(UserHandleCompat.myUserHandle()); + c = db.query(TABLE_FAVORITES, new String[] { + Favorites._ID, + Favorites.INTENT, + }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial, + null, null, null, null); + + updateStmt = db.compileStatement("UPDATE favorites SET itemType=" + + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?"); + + final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); + final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT); + + while (c.moveToNext()) { + String intentDescription = c.getString(intentIndex); + Intent intent; + try { + intent = Intent.parseUri(intentDescription, 0); + } catch (URISyntaxException e) { + Log.e(TAG, "Unable to parse intent", e); + continue; + } + + if (!InstallShortcutReceiver.isLauncherActivity(intent, mContext)) { + continue; + } + + long id = c.getLong(idIndex); + updateStmt.bindLong(1, id); + updateStmt.execute(); + } + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.w(TAG, "Error deduping shortcuts", ex); + } finally { + db.endTransaction(); + if (c != null) { + c.close(); + } + if (updateStmt != null) { + updateStmt.close(); + } + } + } + /** * Recreates workspace table and migrates data to the new table. */ diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java index 90a4d1a1f..07ef0efb7 100644 --- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java +++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java @@ -17,7 +17,9 @@ package com.android.launcher3.compat; import android.content.ComponentName; +import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; public abstract class LauncherActivityInfoCompat { @@ -32,4 +34,11 @@ public abstract class LauncherActivityInfoCompat { public abstract ApplicationInfo getApplicationInfo(); public abstract long getFirstInstallTime(); public abstract Drawable getBadgedIcon(int density); + + /** + * Creates a LauncherActivityInfoCompat for the primary user. + */ + public static LauncherActivityInfoCompat fromResolveInfo(ResolveInfo info, Context context) { + return new LauncherActivityInfoCompatV16(context, info); + } } -- cgit v1.2.3 From 18bf8e2ffde3444d53aaa9654da02cdedd0b7cd1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 8 Apr 2015 18:13:46 -0700 Subject: Automatically adding managed profile shortcuts to homescreen. When the managed profile is created, a "Work" folder is created and added to the homescreen. All work profile apps are added to this folder and icons for subsequent installs (withing a fixed time frame) are automatically added to this folder. If this folder get deleted or the time-frame expires, icon for any new install is placed on the homescreen. Bug: 17410319 Change-Id: I49f4e437707d5eabe4eec85320765bf6ba7fde97 --- res/values/strings.xml | 2 + src/com/android/launcher3/FolderInfo.java | 9 +- .../android/launcher3/InstallShortcutReceiver.java | 13 +- .../launcher3/LauncherAccessibilityDelegate.java | 2 +- src/com/android/launcher3/LauncherAppState.java | 2 +- src/com/android/launcher3/LauncherFiles.java | 2 + src/com/android/launcher3/LauncherModel.java | 108 +++----- src/com/android/launcher3/ShortcutInfo.java | 16 ++ .../launcher3/compat/UserManagerCompat.java | 1 + .../launcher3/compat/UserManagerCompatV16.java | 5 + .../launcher3/compat/UserManagerCompatVL.java | 20 +- .../launcher3/util/ManagedProfileHeuristic.java | 277 +++++++++++++++++++++ 12 files changed, 370 insertions(+), 87 deletions(-) create mode 100644 src/com/android/launcher3/util/ManagedProfileHeuristic.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 1b58d75b9..52306e30e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -38,6 +38,8 @@ Android Core Apps + + Work App isn\'t installed. diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 3240cbf22..80b156413 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -36,6 +36,11 @@ public class FolderInfo extends ItemInfo { */ public static final int FLAG_ITEMS_SORTED = 0x00000001; + /** + * It is a work folder + */ + public static final int FLAG_WORK_FOLDER = 0x00000002; + /** * Whether this folder has been opened */ @@ -46,11 +51,11 @@ public class FolderInfo extends ItemInfo { /** * The apps and shortcuts */ - ArrayList contents = new ArrayList(); + public ArrayList contents = new ArrayList(); ArrayList listeners = new ArrayList(); - FolderInfo() { + public FolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; user = UserHandleCompat.myUserHandle(); } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 5422de951..0c69154aa 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -210,7 +210,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // Add the new apps to the model and bind them if (!addShortcuts.isEmpty()) { LauncherAppState app = LauncherAppState.getInstance(); - app.getModel().addAndBindAddedWorkspaceApps(context, addShortcuts); + app.getModel().addAndBindAddedWorkspaceItems(context, addShortcuts); } } } @@ -352,16 +352,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { public ShortcutInfo getShortcutInfo() { if (activityInfo != null) { - final ShortcutInfo info = new ShortcutInfo(); - info.user = user; - info.title = label; - info.contentDescription = label; - info.customIcon = false; - info.intent = launchIntent; - info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; - info.flags = AppInfo.initFlags(activityInfo); - info.firstInstallTime = activityInfo.getFirstInstallTime(); - return info; + return ShortcutInfo.fromActivityInfo(activityInfo, mContext); } else { return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data); } diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index 42f1914c6..cfc1bd96c 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -124,7 +124,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { mLauncher.showWorkspace(true, new Runnable() { @Override public void run() { - mLauncher.getModel().addAndBindAddedWorkspaceApps( + mLauncher.getModel().addAndBindAddedWorkspaceItems( mLauncher, addShortcuts, screenProvider, 0, true); announceConfirmation(R.string.item_added_to_workspace); } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 3bd385028..6e77d0628 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -149,7 +149,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mIconCache; } - LauncherModel getModel() { + public LauncherModel getModel() { return mModel; } diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 699cb37ff..9dd8dc50c 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -21,6 +21,7 @@ public class LauncherFiles { public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; public static final String WALLPAPER_CROP_PREFERENCES_KEY = "com.android.launcher3.WallpaperCropActivity"; + public static final String MANAGED_USER_PREFERENCES_KEY = "com.android.launcher3.managedusers.prefs"; public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db"; public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; @@ -35,6 +36,7 @@ public class LauncherFiles { WALLPAPER_CROP_PREFERENCES_KEY + XML, WALLPAPER_IMAGES_DB, WIDGET_PREVIEWS_DB, + MANAGED_USER_PREFERENCES_KEY, APP_ICONS_DB)); // TODO: Delete these files on upgrade diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 98ba09bc6..37f1ea86e 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -59,6 +59,7 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; @@ -87,7 +88,6 @@ public class LauncherModel extends BroadcastReceiver static final boolean DEBUG_LOADERS = false; private static final boolean DEBUG_RECEIVER = false; private static final boolean REMOVE_UNRESTORED_ICONS = true; - private static final boolean ADD_MANAGED_PROFILE_SHORTCUTS = false; static final String TAG = "Launcher.Model"; @@ -107,11 +107,6 @@ public class LauncherModel extends BroadcastReceiver @Thunk LoaderTask mLoaderTask; @Thunk boolean mIsLoaderTaskRunning; - /** - * Maintain a set of packages per user, for which we added a shortcut on the workspace. - */ - private static final String INSTALLED_SHORTCUTS_SET_PREFIX = "installed_shortcuts_set_for_user_"; - // Specific runnable types that are run on the main thread deferred handler, this allows us to // clear all queued binding runnables when the Launcher activity is destroyed. private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0; @@ -338,9 +333,9 @@ public class LauncherModel extends BroadcastReceiver runOnWorkerThread(r); } - public void addAndBindAddedWorkspaceApps(final Context context, + public void addAndBindAddedWorkspaceItems(final Context context, final ArrayList workspaceApps) { - addAndBindAddedWorkspaceApps(context, workspaceApps, + addAndBindAddedWorkspaceItems(context, workspaceApps, new ScreenPosProvider() { @Override @@ -518,7 +513,7 @@ public class LauncherModel extends BroadcastReceiver * @param fallbackStartScreen the screen to start search for empty space if * preferredScreen is not available. */ - public void addAndBindAddedWorkspaceApps(final Context context, + public void addAndBindAddedWorkspaceItems(final Context context, final ArrayList workspaceApps, final ScreenPosProvider preferredScreen, final int fallbackStartScreen, @@ -539,7 +534,7 @@ public class LauncherModel extends BroadcastReceiver ArrayList workspaceScreens = loadWorkspaceScreensDb(context); synchronized(sBgLock) { for (ItemInfo item : workspaceApps) { - if (!allowDuplicate) { + if (!allowDuplicate && item instanceof ShortcutInfo) { // Short-circuit this logic if the icon exists somewhere on the workspace if (shortcutExists(context, item.title.toString(), item.getIntent(), item.user)) { @@ -554,21 +549,21 @@ public class LauncherModel extends BroadcastReceiver long screenId = coords.first; int[] cordinates = coords.second; - ShortcutInfo shortcutInfo; - if (item instanceof ShortcutInfo) { - shortcutInfo = (ShortcutInfo) item; + ItemInfo itemInfo; + if (item instanceof ShortcutInfo || item instanceof FolderInfo) { + itemInfo = item; } else if (item instanceof AppInfo) { - shortcutInfo = ((AppInfo) item).makeShortcut(); + itemInfo = ((AppInfo) item).makeShortcut(); } else { throw new RuntimeException("Unexpected info type"); } // Add the shortcut to the db - addItemToDatabase(context, shortcutInfo, + addItemToDatabase(context, itemInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, cordinates[0], cordinates[1]); // Save the ShortcutInfo for binding in the workspace - addedShortcutsFinal.add(shortcutInfo); + addedShortcutsFinal.add(itemInfo); } } @@ -993,7 +988,7 @@ public class LauncherModel extends BroadcastReceiver * Add an item to the database in a specified container. Sets the container, screen, cellX and * cellY fields of the item. Also assigns an ID to the item. */ - static void addItemToDatabase(Context context, final ItemInfo item, final long container, + public static void addItemToDatabase(Context context, final ItemInfo item, final long container, final long screenId, final int cellX, final int cellY) { item.container = container; item.cellX = cellX; @@ -1097,7 +1092,6 @@ public class LauncherModel extends BroadcastReceiver */ static void deleteItemsFromDatabase(Context context, final ArrayList items) { final ContentResolver cr = context.getContentResolver(); - Runnable r = new Runnable() { public void run() { for (ItemInfo item : items) { @@ -2845,23 +2839,11 @@ public class LauncherModel extends BroadcastReceiver mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache)); } - if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) { - // Add shortcuts for packages which were installed while launcher was dead. - String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX - + mUserManager.getSerialNumberForUser(user); - Set packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET); - HashSet newPackageSet = new HashSet(); - - for (LauncherActivityInfoCompat info : apps) { - String packageName = info.getComponentName().getPackageName(); - if (!packagesAdded.contains(packageName) - && !newPackageSet.contains(packageName)) { - InstallShortcutReceiver.queueInstallShortcut(info, mContext); - } - newPackageSet.add(packageName); + if (!user.equals(UserHandleCompat.myUserHandle())) { + ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); + if (heuristic != null) { + heuristic.processUserApps(apps); } - - prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit(); } } // Huh? Shouldn't this be inside the Runnable below? @@ -2884,6 +2866,8 @@ public class LauncherModel extends BroadcastReceiver } } }); + // Cleanup any data stored for a deleted user. + ManagedProfileHeuristic.processAllUsers(profiles, mContext); if (DEBUG_LOADERS) { Log.d(TAG, "Icons processed in " @@ -2971,38 +2955,19 @@ public class LauncherModel extends BroadcastReceiver final String[] packages = mPackages; final int N = packages.length; switch (mOp) { - case OP_ADD: + case OP_ADD: { for (int i=0; i shortcutSet = new HashSet( - prefs.getStringSet(shortcutsSetKey,Collections.EMPTY_SET)); - - for (int i=0; i activities = - mLauncherApps.getActivityList(packages[i], mUser); - if (activities != null && !activities.isEmpty()) { - InstallShortcutReceiver.queueInstallShortcut( - activities.get(0), context); - } - } - } - - prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit(); + ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser); + if (heuristic != null) { + heuristic.processPackageAdd(mPackages); } break; + } case OP_UPDATE: for (int i=0; i shortcutSet = new HashSet( - prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET)); - shortcutSet.removeAll(Arrays.asList(mPackages)); - prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit(); + case OP_REMOVE: { + ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser); + if (heuristic != null) { + heuristic.processPackageRemoved(mPackages); } for (int i=0; i= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW; } + + public static ShortcutInfo fromActivityInfo(LauncherActivityInfoCompat info, Context context) { + final ShortcutInfo shortcut = new ShortcutInfo(); + shortcut.user = info.getUser(); + shortcut.title = info.getLabel().toString(); + shortcut.contentDescription = UserManagerCompat.getInstance(context) + .getBadgedLabelForUser(info.getLabel(), info.getUser()); + shortcut.customIcon = false; + shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser()); + shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; + shortcut.flags = AppInfo.initFlags(info); + shortcut.firstInstallTime = info.getFirstInstallTime(); + return shortcut; + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java index 1374b4e49..a79d94646 100644 --- a/src/com/android/launcher3/compat/UserManagerCompat.java +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -43,4 +43,5 @@ public abstract class UserManagerCompat { public abstract UserHandleCompat getUserForSerialNumber(long serialNumber); public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user); public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user); + public abstract long getUserCreationTime(UserHandleCompat user); } diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java index 32f972e85..ffe698c8b 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatV16.java +++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java @@ -48,4 +48,9 @@ public class UserManagerCompatV16 extends UserManagerCompat { public CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user) { return label; } + + @Override + public long getUserCreationTime(UserHandleCompat user) { + return 0; + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index 19eeabdcf..884d6fe2a 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -18,21 +18,27 @@ package com.android.launcher3.compat; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.UserHandle; -import android.os.UserManager; + +import com.android.launcher3.LauncherAppState; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UserManagerCompatVL extends UserManagerCompatV17 { + private static final String USER_CREATION_TIME_KEY = "user_creation_time_"; + private final PackageManager mPm; + private final Context mContext; UserManagerCompatVL(Context context) { super(context); mPm = context.getPackageManager(); + mContext = context; } @Override @@ -61,5 +67,17 @@ public class UserManagerCompatVL extends UserManagerCompatV17 { } return mPm.getUserBadgedLabel(label, user.getUser()); } + + @Override + public long getUserCreationTime(UserHandleCompat user) { + // TODO: Use system API once available. + SharedPreferences prefs = mContext.getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); + String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user); + if (!prefs.contains(key)) { + prefs.edit().putLong(key, System.currentTimeMillis()).apply(); + } + return prefs.getLong(key, 0); + } } diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java new file mode 100644 index 000000000..cefa71c39 --- /dev/null +++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java @@ -0,0 +1,277 @@ +package com.android.launcher3.util; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.util.Log; + +import com.android.launcher3.FolderInfo; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherFiles; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.LauncherActivityInfoCompat; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +/** + * Handles addition of app shortcuts for managed profiles. + * Methods of class should only be called on {@link LauncherModel#sWorkerThread}. + */ +public class ManagedProfileHeuristic { + + private static final String TAG = "ManagedProfileHeuristic"; + + /** + * Maintain a set of packages installed per user. + */ + private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_"; + + private static final String USER_FOLDER_ID_PREFIX = "user_folder_"; + + /** + * Duration (in milliseconds) for which app shortcuts will be added to work folder. + */ + private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000; + + public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) { + if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) { + return new ManagedProfileHeuristic(context, user); + } + return null; + } + + private final Context mContext; + private final UserHandleCompat mUser; + private final LauncherModel mModel; + + private final SharedPreferences mPrefs; + private final long mUserSerial; + private final long mUserCreationTime; + private final String mPackageSetKey; + + private ArrayList mHomescreenApps; + private ArrayList mWorkFolderApps; + + private ManagedProfileHeuristic(Context context, UserHandleCompat user) { + mContext = context; + mUser = user; + mModel = LauncherAppState.getInstance().getModel(); + + UserManagerCompat userManager = UserManagerCompat.getInstance(context); + mUserSerial = userManager.getSerialNumberForUser(user); + mUserCreationTime = userManager.getUserCreationTime(user); + mPackageSetKey = INSTALLED_PACKAGES_PREFIX + mUserSerial; + + mPrefs = mContext.getSharedPreferences(LauncherFiles.MANAGED_USER_PREFERENCES_KEY, + Context.MODE_PRIVATE); + } + + /** + * Checks the list of user apps and adds icons for newly installed apps on the homescreen or + * workfolder. + */ + public void processUserApps(List apps) { + mHomescreenApps = new ArrayList(); + mWorkFolderApps = new ArrayList(); + HashSet packageSet = getPackageSet(); + boolean newPackageAdded = false; + + for (LauncherActivityInfoCompat info : apps) { + String packageName = info.getComponentName().getPackageName(); + if (!packageSet.contains(packageName)) { + packageSet.add(packageName); + newPackageAdded = true; + + try { + PackageInfo pkgInfo = mContext.getPackageManager() + .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + markForAddition(info, pkgInfo.firstInstallTime); + } catch (NameNotFoundException e) { + Log.e(TAG, "Unknown package " + packageName, e); + } + } + } + + if (newPackageAdded) { + mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); + finalizeAdditions(); + } + } + + private void markForAddition(LauncherActivityInfoCompat info, long installTime) { + ArrayList targetList = + (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ? + mWorkFolderApps : mHomescreenApps; + targetList.add(ShortcutInfo.fromActivityInfo(info, mContext)); + } + + /** + * Adds and binds shortcuts marked to be added to the work folder. + */ + private void finalizeWorkFolder() { + if (mWorkFolderApps.isEmpty()) { + return; + } + + // Try to get a work folder. + String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial; + if (mPrefs.contains(folderIdKey)) { + long folderId = mPrefs.getLong(folderIdKey, 0); + final FolderInfo workFolder = mModel.findFolderById(folderId); + + if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) { + // Could not get a work folder. Add all the icons to homescreen. + mHomescreenApps.addAll(mWorkFolderApps); + return; + } + saveWorkFolderShortcuts(folderId, workFolder.contents.size()); + + final ArrayList shortcuts = mWorkFolderApps; + // FolderInfo could already be bound. We need to add shortcuts on the UI thread. + new MainThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + for (ItemInfo info : shortcuts) { + workFolder.add((ShortcutInfo) info); + } + } + }); + } else { + // Create a new folder. + final FolderInfo workFolder = new FolderInfo(); + workFolder.title = mContext.getText(R.string.work_folder_name); + workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null); + + // Add all shortcuts before adding it to the UI, as an empty folder might get deleted. + for (ItemInfo info : mWorkFolderApps) { + workFolder.add((ShortcutInfo) info); + } + + // Add the item to home screen and DB. This also generates an item id synchronously. + ArrayList itemList = new ArrayList(1); + itemList.add(workFolder); + mModel.addAndBindAddedWorkspaceItems(mContext, itemList); + mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply(); + + saveWorkFolderShortcuts(workFolder.id, 0); + } + } + + /** + * Add work folder shortcuts to the DB. + */ + private void saveWorkFolderShortcuts(long workFolderId, int startingRank) { + for (ItemInfo info : mWorkFolderApps) { + info.rank = startingRank++; + LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0); + } + } + + /** + * Adds and binds all shortcuts marked for addition. + */ + private void finalizeAdditions() { + finalizeWorkFolder(); + + if (!mHomescreenApps.isEmpty()) { + mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps); + } + } + + /** + * Updates the list of installed apps and adds any new icons on homescreen or work folder. + */ + public void processPackageAdd(String[] packages) { + mHomescreenApps = new ArrayList(); + mWorkFolderApps = new ArrayList(); + HashSet packageSet = getPackageSet(); + boolean newPackageAdded = false; + long installTime = System.currentTimeMillis(); + LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext); + + for (String packageName : packages) { + if (!packageSet.contains(packageName)) { + packageSet.add(packageName); + newPackageAdded = true; + + List activities = + launcherApps.getActivityList(packageName, mUser); + if (!activities.isEmpty()) { + markForAddition(activities.get(0), installTime); + } + } + } + + if (newPackageAdded) { + mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); + finalizeAdditions(); + } + } + + /** + * Updates the list of installed packages for the user. + */ + public void processPackageRemoved(String[] packages) { + HashSet packageSet = getPackageSet(); + boolean packageRemoved = false; + + for (String packageName : packages) { + if (packageSet.remove(packageName)) { + packageRemoved = true; + } + } + + if (packageRemoved) { + mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); + } + } + + @SuppressWarnings("unchecked") + private HashSet getPackageSet() { + return new HashSet(mPrefs.getStringSet(mPackageSetKey, Collections.EMPTY_SET)); + } + + /** + * Verifies that entries corresponding to {@param users} exist and removes all invalid entries. + */ + public static void processAllUsers(List users, Context context) { + if (!Utilities.isLmpOrAbove()) { + return; + } + UserManagerCompat userManager = UserManagerCompat.getInstance(context); + HashSet validKeys = new HashSet(); + for (UserHandleCompat user : users) { + addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys); + } + + SharedPreferences prefs = context.getSharedPreferences( + LauncherFiles.MANAGED_USER_PREFERENCES_KEY, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + for (String key : prefs.getAll().keySet()) { + if (!validKeys.contains(key)) { + editor.remove(key); + } + } + editor.apply(); + } + + private static void addAllUserKeys(long userSerial, HashSet keysOut) { + keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial); + keysOut.add(USER_FOLDER_ID_PREFIX + userSerial); + } +} -- cgit v1.2.3 From ece6c8b20b7bcfa0734c2fd4cd3fc93bc227699f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 13 Apr 2015 11:47:00 -0700 Subject: Wrong icon getting set for an app on secondary user during updates Bug: 20163841 Change-Id: Ief91db56f7831f89e34c4540981a9d2db96b8bed --- src/com/android/launcher3/IconCache.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index f6238dab2..48b38f182 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -283,8 +283,8 @@ public class IconCache { } ContentValues values = updateCacheAndGetContentValues(app); mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, - IconDB.COLUMN_COMPONENT + " = ?", - new String[] { cn }); + IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", + new String[] {cn, Long.toString(userSerial)}); updatedPackages.add(component.getPackageName()); } -- cgit v1.2.3 From 4e8fb91cf19b7d621de8cbed2bde2c8dac734121 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Sat, 11 Apr 2015 15:44:32 -0700 Subject: WidgetTray improvement UI - Fixing the issue of widget row not rendering - Moved the resetting of previous bitmap to the start of onBind instead of the onDetachedWindow. Performance - WidgetModel do not have to keep packageName list around. Will com in immediate follow up CLs - use lowRes widgets bitmap before fully loading the fullRes one. - application icons are fully loaded in background thread. b/19897708 Change-Id: I5b3b43a5c543a5135c395f90df32e66d57dc86e0 --- res/layout/widget_cell.xml | 1 - src/com/android/launcher3/Launcher.java | 2 +- .../android/launcher3/widget/PackageItemInfo.java | 5 +- src/com/android/launcher3/widget/WidgetCell.java | 13 ++++- .../launcher3/widget/WidgetsListAdapter.java | 30 +++------- src/com/android/launcher3/widget/WidgetsModel.java | 64 +++++++++------------- 6 files changed, 49 insertions(+), 66 deletions(-) diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index 9e91f677e..a5b25aadb 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -57,7 +57,6 @@ android:id="@+id/widget_dims" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_marginStart="5dp" android:layout_marginLeft="5dp" android:layout_weight="0" diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3d2a34647..42e145bc8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -132,7 +132,7 @@ public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener, LauncherStateTransitionAnimation.Callbacks { - static final String TAG = "Launcher - MERONG"; + static final String TAG = "Launcher"; static final boolean LOGD = true; static final boolean PROFILE_STARTUP = false; diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java index d7edf2294..1a1de55c2 100644 --- a/src/com/android/launcher3/widget/PackageItemInfo.java +++ b/src/com/android/launcher3/widget/PackageItemInfo.java @@ -39,11 +39,12 @@ public class PackageItemInfo extends ItemInfo { */ public boolean usingLowResIcon; - public ComponentName componentName; + public String packageName; int flags = 0; - PackageItemInfo() { + PackageItemInfo(String packageName) { + this.packageName = packageName; } @Override diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 93ee94a59..d10c3049e 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -47,7 +47,7 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; */ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { - private static final String TAG = "PagedViewWidget"; + private static final String TAG = "WidgetCell"; private static final boolean DEBUG = false; // Temporary preset width and height of the image to keep them aligned. @@ -120,7 +120,16 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString())); } super.onDetachedFromWindow(); - deletePreview(true); + deletePreview(false); + } + + public void reset() { + ImageView image = (ImageView) findViewById(R.id.widget_preview); + final TextView name = (TextView) findViewById(R.id.widget_name); + final TextView dims = (TextView) findViewById(R.id.widget_dims); + image.setImageDrawable(null); + name.setText(null); + dims.setText(null); } public void deletePreview(boolean recycleImage) { diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 8d1f20c0c..afeb2d385 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -88,14 +88,13 @@ public class WidgetsListAdapter extends Adapter { @Override public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) { - String packageName = mWidgetsModel.getPackageName(pos); - List infoList = mWidgetsModel.getSortedWidgets(packageName); + List infoList = mWidgetsModel.getSortedWidgets(pos); ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); if (DEBUG) { Log.d(TAG, String.format( - "onBindViewHolder [pos=%d, packageName=%s, widget#=%d, row.getChildCount=%d]", - pos, packageName, infoList.size(), row.getChildCount())); + "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]", + pos, infoList.size(), row.getChildCount())); } // Add more views. @@ -120,11 +119,11 @@ public class WidgetsListAdapter extends Adapter { } // Bind the views in the application info section. - PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(packageName); + PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(pos); if (infoOut.usingLowResIcon) { - mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), - false /* useLowResIcon */, infoOut); - mWidgetsModel.setPackageItemInfo(packageName, infoOut); + // TODO(hyunyoungs): call this in none UI thread in the same way as BubbleTextView. + mIconCache.getTitleAndIconForApp(infoOut.packageName, + UserHandleCompat.myUserHandle(), false /* useLowResIcon */, infoOut); } ((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title); ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image); @@ -133,7 +132,8 @@ public class WidgetsListAdapter extends Adapter { // Bind the view in the widget horizontal tray region. for (int i=0; i < infoList.size(); i++) { WidgetCell widget = (WidgetCell) row.getChildAt(i); - if (getWidgetPreviewLoader() == null || widget == null) { + widget.reset(); + if (getWidgetPreviewLoader() == null) { return; } if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) { @@ -150,7 +150,6 @@ public class WidgetsListAdapter extends Adapter { widget.setVisibility(View.VISIBLE); widget.ensurePreview(); } - // TODO(hyunyoungs): Draw the scrollable indicator. } @Override @@ -175,15 +174,4 @@ public class WidgetsListAdapter extends Adapter { } return mWidgetPreviewLoader; } - - /** - * TODO(hyunyoungs): this is temporary. Figure out the width of each widget cell - * and then check if the total sum is longer than the parent width. - */ - private void addScrollableIndicator(int contentSize, ViewGroup parent) { - if (contentSize > 2) { - ViewGroup indicator = (ViewGroup) parent.findViewById(R.id.scrollable_indicator); - indicator.setVisibility(View.VISIBLE); - } - } } diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java index 463c79ec2..1447befb2 100644 --- a/src/com/android/launcher3/widget/WidgetsModel.java +++ b/src/com/android/launcher3/widget/WidgetsModel.java @@ -31,12 +31,10 @@ public class WidgetsModel { private static final boolean DEBUG = false; /* List of packages that is tracked by this model. */ - private List mPackageNames = new ArrayList<>(); - - private Map mPackageItemInfoList = new HashMap<>(); + private List mPackageItemInfos = new ArrayList<>(); /* Map of widgets and shortcuts that are tracked per package. */ - private Map> mWidgetsList = new HashMap<>(); + private Map> mWidgetsList = new HashMap<>(); /* Notifies the adapter when data changes. */ private RecyclerView.Adapter mAdapter; @@ -53,20 +51,16 @@ public class WidgetsModel { // Access methods that may be deleted if the private fields are made package-private. public int getPackageSize() { - return mPackageNames.size(); + return mPackageItemInfos.size(); } // Access methods that may be deleted if the private fields are made package-private. - public String getPackageName(int pos) { - return mPackageNames.get(pos); - } - - public PackageItemInfo getPackageItemInfo(String packageName) { - return mPackageItemInfoList.get(packageName); + public PackageItemInfo getPackageItemInfo(int pos) { + return mPackageItemInfos.get(pos); } - public List getSortedWidgets(String packageName) { - return mWidgetsList.get(packageName); + public List getSortedWidgets(int pos) { + return mWidgetsList.get(mPackageItemInfos.get(pos)); } public void addWidgetsAndShortcuts(ArrayList widgetsShortcuts, PackageManager pm) { @@ -74,8 +68,10 @@ public class WidgetsModel { Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size()); } + // Temporary list for {@link PackageItemInfos} to avoid having to go through + // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList} + HashMap tmpPackageItemInfos = new HashMap<>(); // clear the lists. - mPackageNames.clear(); mWidgetsList.clear(); // add and update. @@ -90,51 +86,41 @@ public class WidgetsModel { } else { Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s", o.getClass().toString())); - } - ArrayList widgetsShortcutsList = mWidgetsList.get(packageName); + PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); + ArrayList widgetsShortcutsList = mWidgetsList.get(pInfo); if (widgetsShortcutsList != null) { widgetsShortcutsList.add(o); } else { widgetsShortcutsList = new ArrayList(); widgetsShortcutsList.add(o); - mWidgetsList.put(packageName, widgetsShortcutsList); - mPackageNames.add(packageName); - } - } - for (String packageName: mPackageNames) { - PackageItemInfo pInfo = mPackageItemInfoList.get(packageName); - if (pInfo == null) { - pInfo = new PackageItemInfo(); + + pInfo = new PackageItemInfo(packageName); mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), true /* useLowResIcon */, pInfo); - mPackageItemInfoList.put(packageName, pInfo); + mWidgetsList.put(pInfo, widgetsShortcutsList); + tmpPackageItemInfos.put(packageName, pInfo); + mPackageItemInfos.add(pInfo); } } // sort. - sortPackageList(); - for (String packageName: mPackageNames) { - Collections.sort(mWidgetsList.get(packageName), mWidgetAndShortcutNameComparator); + sortPackageItemInfos(); + for (PackageItemInfo p: mPackageItemInfos) { + Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator); } // notify. mAdapter.notifyDataSetChanged(); } - private void sortPackageList() { - Collections.sort(mPackageNames, new Comparator() { + private void sortPackageItemInfos() { + Collections.sort(mPackageItemInfos, new Comparator() { @Override - public int compare(String lhs, String rhs) { - String lhsTitle = mPackageItemInfoList.get(lhs).title.toString(); - String rhsTitle = mPackageItemInfoList.get(rhs).title.toString(); - return lhsTitle.compareTo(rhsTitle); + public int compare(PackageItemInfo lhs, PackageItemInfo rhs) { + return lhs.title.toString().compareTo(rhs.title.toString()); } }); } - - public void setPackageItemInfo(String packageName, PackageItemInfo infoOut) { - mPackageItemInfoList.put(packageName, infoOut); - } -} +} \ No newline at end of file -- cgit v1.2.3 From ada50984dc149c1f4337f965fbb59bdeaac8d09f Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 10 Apr 2015 14:35:23 -0700 Subject: Focus handling - RTL support - Support LEFT and RIGHT keys to work across workspaces when in RTL mode. - Folder icons navigate correctly on DPAD_LEFT/RIGHT events - Folder navigation across pages also works correctly - Deleted dead code inside FocusHelper b/20120358 Change-Id: I7f851cb7ed31f666a91b2f856458d7966ea5f712 --- src/com/android/launcher3/DeviceProfile.java | 2 +- src/com/android/launcher3/FocusHelper.java | 291 +++++++++++-------------- src/com/android/launcher3/util/FocusLogic.java | 34 ++- 3 files changed, 148 insertions(+), 179 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 786f2ce03..22fb6a049 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -82,7 +82,7 @@ public class DeviceProfile { boolean isLandscape; boolean isTablet; boolean isLargeTablet; - boolean isLayoutRtl; + public boolean isLayoutRtl; boolean transposeLayoutWithOrientation; diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 327fac460..8791c896a 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -37,16 +37,6 @@ class IconKeyEventListener implements View.OnKeyListener { } } -/** - * A keyboard listener we set on all the workspace icons. - */ -class FolderKeyEventListener implements View.OnKeyListener { - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - return FocusHelper.handleFolderKeyEvent(v, keyCode, event); - } -} - /** * A keyboard listener we set on all the hotseat buttons. */ @@ -91,110 +81,120 @@ public class FocusHelper { */ public static class PagedViewKeyListener implements View.OnKeyListener { - @Override - public boolean onKey(View v, int keyCode, KeyEvent e) { - boolean consume = FocusLogic.shouldConsume(keyCode); - if (e.getAction() == KeyEvent.ACTION_UP) { - return consume; - } - if (DEBUG) { - Log.v(TAG, String.format("Handle ALL APPS keyevent=[%s].", - KeyEvent.keyCodeToString(keyCode))); - } + @Override + public boolean onKey(View v, int keyCode, KeyEvent e) { + boolean consume = FocusLogic.shouldConsume(keyCode); + if (e.getAction() == KeyEvent.ACTION_UP) { + return consume; + } + if (DEBUG) { + Log.v(TAG, String.format("Handle ALL APPS and Folders keyevent=[%s].", + KeyEvent.keyCodeToString(keyCode))); + } - // Initialize variables. - ViewGroup parentLayout; - ViewGroup itemContainer; - int countX; - int countY; - if (v.getParent() instanceof ShortcutAndWidgetContainer) { - itemContainer = (ViewGroup) v.getParent(); - parentLayout = (ViewGroup) itemContainer.getParent(); - countX = ((CellLayout) parentLayout).getCountX(); - countY = ((CellLayout) parentLayout).getCountY(); - } else { - if (LauncherAppState.isDogfoodBuild()) { - throw new IllegalStateException("Parent of the focused item is not supported."); + // Initialize variables. + ViewGroup parentLayout; + ViewGroup itemContainer; + int countX; + int countY; + if (v.getParent() instanceof ShortcutAndWidgetContainer) { + itemContainer = (ViewGroup) v.getParent(); + parentLayout = (ViewGroup) itemContainer.getParent(); + countX = ((CellLayout) parentLayout).getCountX(); + countY = ((CellLayout) parentLayout).getCountY(); } else { - return false; + if (LauncherAppState.isDogfoodBuild()) { + throw new IllegalStateException("Parent of the focused item is not supported."); + } else { + return false; + } } - } - - final int iconIndex = itemContainer.indexOfChild(v); - final PagedView container = (PagedView) parentLayout.getParent(); - final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout)); - final int pageCount = container.getChildCount(); - ViewGroup newParent = null; - View child = null; - // TODO(hyunyoungs): this matrix is not applicable on the last page. - int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true); - // Process focus. - int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, - iconIndex, pageIndex, pageCount); - if (newIconIndex == FocusLogic.NOOP) { - handleNoopKey(keyCode, v); + final int iconIndex = itemContainer.indexOfChild(v); + final PagedView container = (PagedView) parentLayout.getParent(); + final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout)); + final int pageCount = container.getChildCount(); + ViewGroup newParent = null; + View child = null; + // TODO(hyunyoungs): this matrix is not applicable on the last page. + int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true); + + // Process focus. + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, + iconIndex, pageIndex, pageCount); + if (newIconIndex == FocusLogic.NOOP) { + handleNoopKey(keyCode, v); + return consume; + } + switch (newIconIndex) { + case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: + case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: + int newPageIndex = pageIndex - 1; + if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) { + newPageIndex = pageIndex + 1; + } + newParent = getAppsCustomizePage(container, newPageIndex); + if (newParent != null) { + int row = FocusLogic.findRow(matrix, iconIndex); + container.snapToPage(newPageIndex); + // no need to create a new matrix. + child = newParent.getChildAt(matrix[countX-1][row]); + } + break; + case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: + newParent = getAppsCustomizePage(container, pageIndex - 1); + if (newParent != null) { + container.snapToPage(pageIndex - 1); + child = newParent.getChildAt(0); + } + break; + case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: + newParent = getAppsCustomizePage(container, pageIndex - 1); + if (newParent != null) { + container.snapToPage(pageIndex - 1); + child = newParent.getChildAt(newParent.getChildCount() - 1); + } + break; + case FocusLogic.NEXT_PAGE_FIRST_ITEM: + newParent = getAppsCustomizePage(container, pageIndex + 1); + if (newParent != null) { + container.snapToPage(pageIndex + 1); + child = newParent.getChildAt(0); + } + break; + case FocusLogic.NEXT_PAGE_LEFT_COLUMN: + case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: + newPageIndex = pageIndex + 1; + if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) { + newPageIndex = pageIndex -1; + } + newParent = getAppsCustomizePage(container, newPageIndex); + if (newParent != null) { + container.snapToPage(newPageIndex); + int row = FocusLogic.findRow(matrix, iconIndex); + child = newParent.getChildAt(matrix[0][row]); + } + break; + case FocusLogic.CURRENT_PAGE_FIRST_ITEM: + child = container.getChildAt(0); + break; + case FocusLogic.CURRENT_PAGE_LAST_ITEM: + child = itemContainer.getChildAt(itemContainer.getChildCount() - 1); + break; + default: // Go to some item on the current page. + child = itemContainer.getChildAt(newIconIndex); + break; + } + if (child != null) { + child.requestFocus(); + playSoundEffect(keyCode, v); + } else { + handleNoopKey(keyCode, v); + } return consume; } - switch (newIconIndex) { - case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: - newParent = getAppsCustomizePage(container, pageIndex -1); - if (newParent != null) { - int row = FocusLogic.findRow(matrix, iconIndex); - container.snapToPage(pageIndex - 1); - // no need to create a new matrix. - child = newParent.getChildAt(matrix[countX-1][row]); - } - break; - case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: - newParent = getAppsCustomizePage(container, pageIndex - 1); - if (newParent != null) { - container.snapToPage(pageIndex - 1); - child = newParent.getChildAt(0); - } - break; - case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: - newParent = getAppsCustomizePage(container, pageIndex - 1); - if (newParent != null) { - container.snapToPage(pageIndex - 1); - child = newParent.getChildAt(newParent.getChildCount() - 1); - } - break; - case FocusLogic.NEXT_PAGE_FIRST_ITEM: - newParent = getAppsCustomizePage(container, pageIndex + 1); - if (newParent != null) { - container.snapToPage(pageIndex + 1); - child = newParent.getChildAt(0); - } - break; - case FocusLogic.NEXT_PAGE_LEFT_COLUMN: - newParent = getAppsCustomizePage(container, pageIndex + 1); - if (newParent != null) { - container.snapToPage(pageIndex + 1); - int row = FocusLogic.findRow(matrix, iconIndex); - child = newParent.getChildAt(matrix[0][row]); - } - break; - case FocusLogic.CURRENT_PAGE_FIRST_ITEM: - child = container.getChildAt(0); - break; - case FocusLogic.CURRENT_PAGE_LAST_ITEM: - child = itemContainer.getChildAt(itemContainer.getChildCount() - 1); - break; - default: // Go to some item on the current page. - child = itemContainer.getChildAt(newIconIndex); - break; - } - if (child != null) { - child.requestFocus(); - playSoundEffect(keyCode, v); - } else { - handleNoopKey(keyCode, v); - } - return consume; - } - public void handleNoopKey(int keyCode, View v) { } + public void handleNoopKey(int keyCode, View v) { } } /** @@ -209,8 +209,7 @@ public class FocusHelper { return consume; } - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile profile = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); if (DEBUG) { Log.v(TAG, String.format( "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s", @@ -358,14 +357,19 @@ public class FocusHelper { } break; case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: + case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: + int newPageIndex = pageIndex - 1; + if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) { + newPageIndex = pageIndex + 1; + } int row = FocusLogic.findRow(matrix, iconIndex); - parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1); + parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); if (parent != null) { iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, iconLayout.getCountX(), row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, - FocusLogic.PIVOT, pageIndex - 1, pageCount); + FocusLogic.PIVOT, newPageIndex, pageCount); newIcon = parent.getChildAt(newIconIndex); } break; @@ -385,13 +389,18 @@ public class FocusHelper { workspace.snapToPage(pageIndex + 1); break; case FocusLogic.NEXT_PAGE_LEFT_COLUMN: + case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: + newPageIndex = pageIndex + 1; + if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) { + newPageIndex = pageIndex - 1; + } row = FocusLogic.findRow(matrix, iconIndex); - parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1); + parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); if (parent != null) { iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, - FocusLogic.PIVOT, pageIndex, pageCount); + FocusLogic.PIVOT, newPageIndex, pageCount); newIcon = parent.getChildAt(newIconIndex); } break; @@ -418,62 +427,6 @@ public class FocusHelper { return consume; } - /** - * Handles key events for items in a Folder. - */ - static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) { - boolean consume = FocusLogic.shouldConsume(keyCode); - if (e.getAction() == KeyEvent.ACTION_UP || !consume) { - return consume; - } - if (DEBUG) { - Log.v(TAG, String.format("Handle FOLDER keyevent=[%s].", - KeyEvent.keyCodeToString(keyCode))); - } - - // Initialize the variables. - ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); - final CellLayout layout = (CellLayout) parent.getParent(); - final Folder folder = (Folder) layout.getParent().getParent(); - View title = folder.mFolderName; - Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); - final int countX = layout.getCountX(); - final int countY = layout.getCountY(); - final int iconIndex = findIndexOfView(parent, v); - int pageIndex = workspace.indexOfChild(layout); - int pageCount = workspace.getChildCount(); - int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order */); - - // Process the focus. - int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex, - pageIndex, pageCount); - View newIcon = null; - switch (newIconIndex) { - case FocusLogic.NOOP: - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - newIcon = title; - } - break; - case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: - case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: - case FocusLogic.NEXT_PAGE_FIRST_ITEM: - case FocusLogic.CURRENT_PAGE_FIRST_ITEM: - case FocusLogic.CURRENT_PAGE_LAST_ITEM: - if (DEBUG) { - Log.v(TAG, "Page advance handling not supported on folder icons."); - } - break; - default: // current page some item. - newIcon = parent.getChildAt(newIconIndex); - break; - } - if (newIcon != null) { - newIcon.requestFocus(); - playSoundEffect(keyCode, v); - } - return consume; - } - // // Helper methods. // diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index 6e80c2f21..8a08a4e72 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -16,12 +16,13 @@ package com.android.launcher3.util; -import android.content.res.Configuration; import android.util.Log; import android.view.KeyEvent; import android.view.ViewGroup; import com.android.launcher3.CellLayout; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherAppState; /** * Calculates the next item that a {@link KeyEvent} should change the focus to. @@ -42,7 +43,7 @@ import com.android.launcher3.CellLayout; */ public class FocusLogic { - private static final String TAG = "Focus"; + private static final String TAG = "FocusLogic"; private static final boolean DEBUG = false; // Item and page index related constant used by {@link #handleKeyEvent}. @@ -51,12 +52,14 @@ public class FocusLogic { public static final int PREVIOUS_PAGE_RIGHT_COLUMN = -2; public static final int PREVIOUS_PAGE_FIRST_ITEM = -3; public static final int PREVIOUS_PAGE_LAST_ITEM = -4; + public static final int PREVIOUS_PAGE_LEFT_COLUMN = -5; - public static final int CURRENT_PAGE_FIRST_ITEM = -5; - public static final int CURRENT_PAGE_LAST_ITEM = -6; + public static final int CURRENT_PAGE_FIRST_ITEM = -6; + public static final int CURRENT_PAGE_LAST_ITEM = -7; - public static final int NEXT_PAGE_FIRST_ITEM = -7; - public static final int NEXT_PAGE_LEFT_COLUMN = -8; + public static final int NEXT_PAGE_FIRST_ITEM = -8; + public static final int NEXT_PAGE_LEFT_COLUMN = -9; + public static final int NEXT_PAGE_RIGHT_COLUMN = -10; // Matrix related constant. public static final int EMPTY = -1; @@ -85,18 +88,24 @@ public class FocusLogic { cntX, cntY, iconIdx, pageIndex, pageCount)); } + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid() + .getDeviceProfile(); int newIndex = NOOP; switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/); - if (newIndex == NOOP && pageIndex > 0) { + if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) { newIndex = PREVIOUS_PAGE_RIGHT_COLUMN; + } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) { + newIndex = NEXT_PAGE_RIGHT_COLUMN; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/); - if (newIndex == NOOP && pageIndex < pageCount - 1) { + if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) { newIndex = NEXT_PAGE_LEFT_COLUMN; + } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) { + newIndex = PREVIOUS_PAGE_LEFT_COLUMN; } break; case KeyEvent.KEYCODE_DPAD_DOWN: @@ -140,11 +149,18 @@ public class FocusLogic { */ // TODO: get rid of dynamic matrix creation. public static int[][] createFullMatrix(int m, int n, boolean incrementOrder) { + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid() + .getDeviceProfile(); int[][] matrix = new int [m][n]; + for (int i=0; i < m;i++) { for (int j=0; j < n; j++) { if (incrementOrder) { - matrix[i][j] = j * m + i; + if (!profile.isLayoutRtl) { + matrix[i][j] = j * m + i; + } else { + matrix[i][j] = j * m + m - i -1; + } } else { matrix[i][j] = EMPTY; } -- cgit v1.2.3 From 6da9c32204347d0980d3e76a5ff03bc3431b49a7 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 14 Apr 2015 11:50:44 -0700 Subject: Fix null pointer exception in WidgetsModel Due to not clearing the PackageItemInfos list when uninstall happened, uninstalled PackageItemInfo lingered around when there wasn't associated Widgets to it. Hence causing null pointer exception. b/20216900 Change-Id: I5601cf618079044d24ffc3ee835c1468fa2e9371 --- src/com/android/launcher3/widget/WidgetsModel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java index 1447befb2..71a7b9446 100644 --- a/src/com/android/launcher3/widget/WidgetsModel.java +++ b/src/com/android/launcher3/widget/WidgetsModel.java @@ -71,8 +71,10 @@ public class WidgetsModel { // Temporary list for {@link PackageItemInfos} to avoid having to go through // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList} HashMap tmpPackageItemInfos = new HashMap<>(); + // clear the lists. mWidgetsList.clear(); + mPackageItemInfos.clear(); // add and update. for (Object o: widgetsShortcuts) { -- cgit v1.2.3 From a9c0978e3eeccd6961feeb443da857fb4ed7e578 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 14 Apr 2015 16:49:41 -0700 Subject: Updating pageindicator assets Change-Id: I22d6a53f0c372a128b47ec95857793477317fc07 --- res/drawable-hdpi/ic_pageindicator_add.png | Bin 416 -> 945 bytes res/drawable-hdpi/ic_pageindicator_current.png | Bin 684 -> 1038 bytes res/drawable-hdpi/ic_pageindicator_current_folder.png | Bin 678 -> 610 bytes res/drawable-hdpi/ic_pageindicator_default.png | Bin 509 -> 895 bytes res/drawable-hdpi/ic_pageindicator_default_folder.png | Bin 501 -> 529 bytes res/drawable-mdpi/ic_pageindicator_add.png | Bin 243 -> 669 bytes res/drawable-mdpi/ic_pageindicator_current.png | Bin 421 -> 704 bytes res/drawable-mdpi/ic_pageindicator_current_folder.png | Bin 415 -> 484 bytes res/drawable-mdpi/ic_pageindicator_default.png | Bin 322 -> 694 bytes res/drawable-mdpi/ic_pageindicator_default_folder.png | Bin 315 -> 482 bytes res/drawable-xhdpi/ic_pageindicator_add.png | Bin 588 -> 1192 bytes res/drawable-xhdpi/ic_pageindicator_current.png | Bin 922 -> 1414 bytes .../ic_pageindicator_current_folder.png | Bin 896 -> 782 bytes res/drawable-xhdpi/ic_pageindicator_default.png | Bin 682 -> 1168 bytes .../ic_pageindicator_default_folder.png | Bin 654 -> 644 bytes res/drawable-xxhdpi/ic_pageindicator_add.png | Bin 858 -> 1607 bytes res/drawable-xxhdpi/ic_pageindicator_current.png | Bin 1458 -> 2105 bytes .../ic_pageindicator_current_folder.png | Bin 1416 -> 1023 bytes res/drawable-xxhdpi/ic_pageindicator_default.png | Bin 1069 -> 1634 bytes .../ic_pageindicator_default_folder.png | Bin 1051 -> 832 bytes 20 files changed, 0 insertions(+), 0 deletions(-) diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png index 971361d56..ab0e5dbd5 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_add.png and b/res/drawable-hdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png index 09405d83a..423ca2b45 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_current.png and b/res/drawable-hdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_current_folder.png b/res/drawable-hdpi/ic_pageindicator_current_folder.png index c3a6f699d..43fbb0e79 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_current_folder.png and b/res/drawable-hdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png index 251de44c9..83fa73fc6 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_default.png and b/res/drawable-hdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-hdpi/ic_pageindicator_default_folder.png b/res/drawable-hdpi/ic_pageindicator_default_folder.png index 48a321898..55cab1c65 100644 Binary files a/res/drawable-hdpi/ic_pageindicator_default_folder.png and b/res/drawable-hdpi/ic_pageindicator_default_folder.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png index 50e1ba524..11659a3b2 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_add.png and b/res/drawable-mdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png index 19a89727c..ca889c4d1 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_current.png and b/res/drawable-mdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_current_folder.png b/res/drawable-mdpi/ic_pageindicator_current_folder.png index 8d6c0b1ff..5bbba9140 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_current_folder.png and b/res/drawable-mdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png index 6b0a9c9e3..34493b155 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_default.png and b/res/drawable-mdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-mdpi/ic_pageindicator_default_folder.png b/res/drawable-mdpi/ic_pageindicator_default_folder.png index c9edf8d88..0a987a4d0 100644 Binary files a/res/drawable-mdpi/ic_pageindicator_default_folder.png and b/res/drawable-mdpi/ic_pageindicator_default_folder.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png index 9fb5359fe..af1da2d42 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_add.png and b/res/drawable-xhdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png index c44b88fe9..3054f2f4f 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_current.png and b/res/drawable-xhdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_current_folder.png b/res/drawable-xhdpi/ic_pageindicator_current_folder.png index fa2168f0c..cd92e9f21 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_current_folder.png and b/res/drawable-xhdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png index 6d1b5be6b..38538dcf0 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_default.png and b/res/drawable-xhdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-xhdpi/ic_pageindicator_default_folder.png b/res/drawable-xhdpi/ic_pageindicator_default_folder.png index ff0ed3991..e7c46e3a1 100644 Binary files a/res/drawable-xhdpi/ic_pageindicator_default_folder.png and b/res/drawable-xhdpi/ic_pageindicator_default_folder.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png index f3f6fbede..c28895229 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_add.png and b/res/drawable-xxhdpi/ic_pageindicator_add.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png index 3a3a3ae29..5941c8e4f 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_current.png and b/res/drawable-xxhdpi/ic_pageindicator_current.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png index 4ff8ec6bc..602b89a40 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png and b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png index b057c6313..3fa9e5fd7 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_default.png and b/res/drawable-xxhdpi/ic_pageindicator_default.png differ diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png index 756ad090d..bbcd7f91e 100644 Binary files a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png and b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png differ -- cgit v1.2.3 From ba41458f5c696194d547ac8f24e9f8e58cfa64fc Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Mon, 13 Apr 2015 14:32:47 +0100 Subject: Remove usage of FloatMath Bug: https://code.google.com/p/android/issues/detail?id=36199 Change-Id: I2effc3f498fb13e66de9a905b9a40ac5688a5850 --- WallpaperPicker/src/com/android/launcher3/CropView.java | 5 ++--- src/com/android/launcher3/LauncherScroller.java | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java index 578b8eafd..50f779add 100644 --- a/WallpaperPicker/src/com/android/launcher3/CropView.java +++ b/WallpaperPicker/src/com/android/launcher3/CropView.java @@ -21,7 +21,6 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.FloatMath; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; @@ -300,12 +299,12 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { adjustment[0] = (edges.right - getWidth()) / scale; } if (edges.top > 0) { - adjustment[1] = FloatMath.ceil(edges.top / scale); + adjustment[1] = (float) Math.ceil(edges.top / scale); } else if (edges.bottom < getHeight()) { adjustment[1] = (edges.bottom - getHeight()) / scale; } for (int dim = 0; dim <= 1; dim++) { - if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]); + if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]); } mInverseRotateMatrix.mapPoints(adjustment); diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java index 3bd0a78c4..a9b49556b 100644 --- a/src/com/android/launcher3/LauncherScroller.java +++ b/src/com/android/launcher3/LauncherScroller.java @@ -20,7 +20,6 @@ import android.animation.TimeInterpolator; import android.content.Context; import android.hardware.SensorManager; import android.os.Build; -import android.util.FloatMath; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -409,7 +408,7 @@ public class LauncherScroller { float dx = (float) (mFinalX - mStartX); float dy = (float) (mFinalY - mStartY); - float hyp = FloatMath.sqrt(dx * dx + dy * dy); + float hyp = (float) Math.hypot(dx, dy); float ndx = dx / hyp; float ndy = dy / hyp; @@ -426,7 +425,7 @@ public class LauncherScroller { mMode = FLING_MODE; mFinished = false; - float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY); + float velocity = (float) Math.hypot(velocityX, velocityY); mVelocity = velocity; mDuration = getSplineFlingDuration(velocity); -- cgit v1.2.3 From 3fa3c127618ed928179be1bac138466d0e2bbce8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Apr 2015 13:15:06 -0700 Subject: Fixing pallpaper parallex in rtl. The rtl mode, the first screen should correspond to the last opage offset (since page offset are always defined as left-to-right) Change-Id: Ie31139166bd71c423c63968760493ec572af81e9 --- src/com/android/launcher3/Workspace.java | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 91739711b..043ecb075 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1410,7 +1410,22 @@ public class Workspace extends SmoothPagedView } private float wallpaperOffsetForCurrentScroll() { + // TODO: do different behavior if it's a live wallpaper? + // Don't use up all the wallpaper parallax until you have at least + // MIN_PARALLAX_PAGE_SPAN pages + int numScrollingPages = getNumScreensExcludingEmptyAndCustom(); + int parallaxPageSpan; + if (mWallpaperIsLiveWallpaper) { + parallaxPageSpan = numScrollingPages - 1; + } else { + parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1); + } + mNumPagesForWallpaperParallax = parallaxPageSpan; + if (getChildCount() <= 1) { + if (isLayoutRtl()) { + return 1 - 1.0f/mNumPagesForWallpaperParallax; + } return 0; } @@ -1430,28 +1445,20 @@ public class Workspace extends SmoothPagedView if (scrollRange == 0) { return 0; } else { - // TODO: do different behavior if it's a live wallpaper? // Sometimes the left parameter of the pages is animated during a layout transition; // this parameter offsets it to keep the wallpaper from animating as well int adjustedScroll = getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0); float offset = Math.min(1, adjustedScroll / (float) scrollRange); offset = Math.max(0, offset); - // Don't use up all the wallpaper parallax until you have at least - // MIN_PARALLAX_PAGE_SPAN pages - int numScrollingPages = getNumScreensExcludingEmptyAndCustom(); - int parallaxPageSpan; - if (mWallpaperIsLiveWallpaper) { - parallaxPageSpan = numScrollingPages - 1; - } else { - parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1); - } - mNumPagesForWallpaperParallax = parallaxPageSpan; // On RTL devices, push the wallpaper offset to the right if we don't have enough // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN) - int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0; - return offset * (padding + numScrollingPages - 1) / parallaxPageSpan; + if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN + && isLayoutRtl()) { + return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan; + } + return offset * (numScrollingPages - 1) / parallaxPageSpan; } } -- cgit v1.2.3 From 65776c43590e506f2ce1906b1421caad95448010 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Apr 2015 13:42:21 -0700 Subject: Disable setWallpaper button until the wallpaper is loaded Bug: 20187333 Change-Id: Id7a42616560e15eddedad5846ef927a3be52ed1c --- .../android/launcher3/WallpaperPickerActivity.java | 71 +++++++++++++--------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index c49286a75..93320919e 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -137,39 +137,32 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static class UriWallpaperInfo extends WallpaperTileInfo { private Uri mUri; - private boolean mFirstClick = true; - @Thunk BitmapRegionTileSource.UriBitmapSource mBitmapSource; public UriWallpaperInfo(Uri uri) { mUri = uri; } @Override public void onClick(final WallpaperPickerActivity a) { - final Runnable onLoad; - if (!mFirstClick) { - onLoad = null; - } else { - mFirstClick = false; - a.mSetWallpaperButton.setEnabled(false); - onLoad = new Runnable() { - public void run() { - if (mBitmapSource != null && - mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) { - a.selectTile(mView); - a.mSetWallpaperButton.setEnabled(true); - } else { - ViewGroup parent = (ViewGroup) mView.getParent(); - if (parent != null) { - parent.removeView(mView); - Toast.makeText(a.getContext(), R.string.image_load_fail, - Toast.LENGTH_SHORT).show(); - } + a.setWallpaperButtonEnabled(false); + final BitmapRegionTileSource.UriBitmapSource bitmapSource = + new BitmapRegionTileSource.UriBitmapSource( + a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); + a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() { + + @Override + public void run() { + if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + a.selectTile(mView); + a.setWallpaperButtonEnabled(true); + } else { + ViewGroup parent = (ViewGroup) mView.getParent(); + if (parent != null) { + parent.removeView(mView); + Toast.makeText(a.getContext(), R.string.image_load_fail, + Toast.LENGTH_SHORT).show(); } } - }; - } - mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( - a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); - a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad); + } + }); } @Override public void onSave(final WallpaperPickerActivity a) { @@ -203,11 +196,18 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mThumb = thumb; } @Override - public void onClick(WallpaperPickerActivity a) { + public void onClick(final WallpaperPickerActivity a) { + a.setWallpaperButtonEnabled(false); BitmapRegionTileSource.UriBitmapSource bitmapSource = new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile), 1024); - a.setCropViewTileSource(bitmapSource, false, true, null, null); + a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() { + + @Override + public void run() { + a.setWallpaperButtonEnabled(true); + } + }); } @Override public void onSave(WallpaperPickerActivity a) { @@ -234,6 +234,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } @Override public void onClick(final WallpaperPickerActivity a) { + a.setWallpaperButtonEnabled(false); BitmapRegionTileSource.ResourceBitmapSource bitmapSource = new BitmapRegionTileSource.ResourceBitmapSource( mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); @@ -248,7 +249,13 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { wallpaperSize.x, wallpaperSize.y, false); return wallpaperSize.x / crop.width(); } - }, null); + }, new Runnable() { + + @Override + public void run() { + a.setWallpaperButtonEnabled(true); + } + }); } @Override public void onSave(WallpaperPickerActivity a) { @@ -420,7 +427,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } return; } - mSetWallpaperButton.setEnabled(true); + setWallpaperButtonEnabled(true); WallpaperTileInfo info = (WallpaperTileInfo) v.getTag(); if (info.isSelectable() && v.getVisibility() == View.VISIBLE) { selectTile(v); @@ -639,6 +646,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { }; } + public void setWallpaperButtonEnabled(boolean enabled) { + mSetWallpaperButton.setEnabled(enabled); + } + @Thunk void selectTile(View v) { if (mSelectedTile != null) { mSelectedTile.setSelected(false); -- cgit v1.2.3 From b4b01efa438953b0a9fcb5b83a6a619e24c93ea3 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Apr 2015 23:38:17 +0000 Subject: Revert "Disable setWallpaper button until the wallpaper is loaded" Will submit again after the drop This reverts commit 65776c43590e506f2ce1906b1421caad95448010. Change-Id: I5f85d0c70f3f5faf85d9ba16e82d4714b694a193 --- .../android/launcher3/WallpaperPickerActivity.java | 71 +++++++++------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 93320919e..c49286a75 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -137,32 +137,39 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static class UriWallpaperInfo extends WallpaperTileInfo { private Uri mUri; + private boolean mFirstClick = true; + @Thunk BitmapRegionTileSource.UriBitmapSource mBitmapSource; public UriWallpaperInfo(Uri uri) { mUri = uri; } @Override public void onClick(final WallpaperPickerActivity a) { - a.setWallpaperButtonEnabled(false); - final BitmapRegionTileSource.UriBitmapSource bitmapSource = - new BitmapRegionTileSource.UriBitmapSource( - a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); - a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() { - - @Override - public void run() { - if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { - a.selectTile(mView); - a.setWallpaperButtonEnabled(true); - } else { - ViewGroup parent = (ViewGroup) mView.getParent(); - if (parent != null) { - parent.removeView(mView); - Toast.makeText(a.getContext(), R.string.image_load_fail, - Toast.LENGTH_SHORT).show(); + final Runnable onLoad; + if (!mFirstClick) { + onLoad = null; + } else { + mFirstClick = false; + a.mSetWallpaperButton.setEnabled(false); + onLoad = new Runnable() { + public void run() { + if (mBitmapSource != null && + mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + a.selectTile(mView); + a.mSetWallpaperButton.setEnabled(true); + } else { + ViewGroup parent = (ViewGroup) mView.getParent(); + if (parent != null) { + parent.removeView(mView); + Toast.makeText(a.getContext(), R.string.image_load_fail, + Toast.LENGTH_SHORT).show(); + } } } - } - }); + }; + } + mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( + a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); + a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad); } @Override public void onSave(final WallpaperPickerActivity a) { @@ -196,18 +203,11 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mThumb = thumb; } @Override - public void onClick(final WallpaperPickerActivity a) { - a.setWallpaperButtonEnabled(false); + public void onClick(WallpaperPickerActivity a) { BitmapRegionTileSource.UriBitmapSource bitmapSource = new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile), 1024); - a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() { - - @Override - public void run() { - a.setWallpaperButtonEnabled(true); - } - }); + a.setCropViewTileSource(bitmapSource, false, true, null, null); } @Override public void onSave(WallpaperPickerActivity a) { @@ -234,7 +234,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } @Override public void onClick(final WallpaperPickerActivity a) { - a.setWallpaperButtonEnabled(false); BitmapRegionTileSource.ResourceBitmapSource bitmapSource = new BitmapRegionTileSource.ResourceBitmapSource( mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); @@ -249,13 +248,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { wallpaperSize.x, wallpaperSize.y, false); return wallpaperSize.x / crop.width(); } - }, new Runnable() { - - @Override - public void run() { - a.setWallpaperButtonEnabled(true); - } - }); + }, null); } @Override public void onSave(WallpaperPickerActivity a) { @@ -427,7 +420,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } return; } - setWallpaperButtonEnabled(true); + mSetWallpaperButton.setEnabled(true); WallpaperTileInfo info = (WallpaperTileInfo) v.getTag(); if (info.isSelectable() && v.getVisibility() == View.VISIBLE) { selectTile(v); @@ -646,10 +639,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { }; } - public void setWallpaperButtonEnabled(boolean enabled) { - mSetWallpaperButton.setEnabled(enabled); - } - @Thunk void selectTile(View v) { if (mSelectedTile != null) { mSelectedTile.setSelected(false); -- cgit v1.2.3 From 89a6ec2af2857115fb6d5bb65bb9df75432f7972 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Apr 2015 23:40:57 +0000 Subject: Revert "Revert "Disable setWallpaper button until the wallpaper is loaded" Will submit again after the drop" Actual fix This reverts commit b4b01efa438953b0a9fcb5b83a6a619e24c93ea3. Change-Id: I65cc75e0f1a95f69be607bb5b11d7f4627ff25c1 --- .../android/launcher3/WallpaperPickerActivity.java | 71 +++++++++++++--------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index c49286a75..93320919e 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -137,39 +137,32 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static class UriWallpaperInfo extends WallpaperTileInfo { private Uri mUri; - private boolean mFirstClick = true; - @Thunk BitmapRegionTileSource.UriBitmapSource mBitmapSource; public UriWallpaperInfo(Uri uri) { mUri = uri; } @Override public void onClick(final WallpaperPickerActivity a) { - final Runnable onLoad; - if (!mFirstClick) { - onLoad = null; - } else { - mFirstClick = false; - a.mSetWallpaperButton.setEnabled(false); - onLoad = new Runnable() { - public void run() { - if (mBitmapSource != null && - mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) { - a.selectTile(mView); - a.mSetWallpaperButton.setEnabled(true); - } else { - ViewGroup parent = (ViewGroup) mView.getParent(); - if (parent != null) { - parent.removeView(mView); - Toast.makeText(a.getContext(), R.string.image_load_fail, - Toast.LENGTH_SHORT).show(); - } + a.setWallpaperButtonEnabled(false); + final BitmapRegionTileSource.UriBitmapSource bitmapSource = + new BitmapRegionTileSource.UriBitmapSource( + a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); + a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() { + + @Override + public void run() { + if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + a.selectTile(mView); + a.setWallpaperButtonEnabled(true); + } else { + ViewGroup parent = (ViewGroup) mView.getParent(); + if (parent != null) { + parent.removeView(mView); + Toast.makeText(a.getContext(), R.string.image_load_fail, + Toast.LENGTH_SHORT).show(); } } - }; - } - mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( - a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); - a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad); + } + }); } @Override public void onSave(final WallpaperPickerActivity a) { @@ -203,11 +196,18 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mThumb = thumb; } @Override - public void onClick(WallpaperPickerActivity a) { + public void onClick(final WallpaperPickerActivity a) { + a.setWallpaperButtonEnabled(false); BitmapRegionTileSource.UriBitmapSource bitmapSource = new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile), 1024); - a.setCropViewTileSource(bitmapSource, false, true, null, null); + a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() { + + @Override + public void run() { + a.setWallpaperButtonEnabled(true); + } + }); } @Override public void onSave(WallpaperPickerActivity a) { @@ -234,6 +234,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } @Override public void onClick(final WallpaperPickerActivity a) { + a.setWallpaperButtonEnabled(false); BitmapRegionTileSource.ResourceBitmapSource bitmapSource = new BitmapRegionTileSource.ResourceBitmapSource( mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); @@ -248,7 +249,13 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { wallpaperSize.x, wallpaperSize.y, false); return wallpaperSize.x / crop.width(); } - }, null); + }, new Runnable() { + + @Override + public void run() { + a.setWallpaperButtonEnabled(true); + } + }); } @Override public void onSave(WallpaperPickerActivity a) { @@ -420,7 +427,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } return; } - mSetWallpaperButton.setEnabled(true); + setWallpaperButtonEnabled(true); WallpaperTileInfo info = (WallpaperTileInfo) v.getTag(); if (info.isSelectable() && v.getVisibility() == View.VISIBLE) { selectTile(v); @@ -639,6 +646,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { }; } + public void setWallpaperButtonEnabled(boolean enabled) { + mSetWallpaperButton.setEnabled(enabled); + } + @Thunk void selectTile(View v) { if (mSelectedTile != null) { mSelectedTile.setSelected(false); -- cgit v1.2.3 From f406615354a92367481a60966b6b873cae37fe4c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Apr 2015 09:42:19 -0700 Subject: Fixing drag-n-drop for folders in hotseat. The current drag-n-drop model assums that when a folder is open it completely covers the folderIcon and its not possible to drop anything on the icon. But its not true when a folder is in the hotseat. > When DnD finishes revert folder to the normal state > Ensure that only one folder is open at a time > Prevent folder icon from moving when the folder is open Bug: 13646281,14680549 Change-Id: I016a4c3f0d9b57c388eaa28f40df9e7b7bbab5fc --- src/com/android/launcher3/DragController.java | 4 ++-- src/com/android/launcher3/Folder.java | 26 +++++++++++++++++++++++--- src/com/android/launcher3/Launcher.java | 12 ++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index eb1686182..347374738 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -377,7 +377,7 @@ public class DragController { // Only end the drag if we are not deferred if (!isDeferred) { - for (DragListener listener : mListeners) { + for (DragListener listener : new ArrayList<>(mListeners)) { listener.onDragEnd(); } } @@ -394,7 +394,7 @@ public class DragController { if (mDragObject.deferDragViewCleanupPostAnimation) { // If we skipped calling onDragEnd() before, do it now - for (DragListener listener : mListeners) { + for (DragListener listener : new ArrayList<>(mListeners)) { listener.onDragEnd(); } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 0eb1fd8bb..116332438 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -49,6 +49,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.launcher3.DragController.DragListener; import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.util.Thunk; @@ -61,7 +62,7 @@ import java.util.Collections; */ public class Folder extends LinearLayout implements DragSource, View.OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener { + View.OnFocusChangeListener, DragListener { private static final String TAG = "Launcher.Folder"; /** @@ -537,6 +538,20 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY); } + // Since this folder opened by another controller, it might not get onDrop or + // onDropComplete. Perform cleanup once drag-n-drop ends. + mDragController.addDragListener(this); + } + + @Override + public void onDragStart(DragSource source, Object info, int dragAction) { } + + @Override + public void onDragEnd() { + if (mIsExternalDrag && mDragInProgress) { + completeDragExit(); + } + mDragController.removeDragListener(this); } @Thunk void sendCustomAccessibilityEvent(int type, String text) { @@ -678,11 +693,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList }; public void completeDragExit() { - mLauncher.closeFolder(); + if (mInfo.opened) { + mLauncher.closeFolder(); + mRearrangeOnClose = true; + } else { + rearrangeChildren(); + } mCurrentDragInfo = null; mCurrentDragView = null; mSuppressOnAdd = false; - mRearrangeOnClose = true; mIsExternalDrag = false; } @@ -1124,6 +1143,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mSuppressOnAdd = false; // Clear the drag info, as it is no longer being dragged. mCurrentDragInfo = null; + mDragInProgress = false; } // This is used so the item doesn't immediately appear in the folder when added. In one case diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 42e145bc8..9ad87c3a6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3112,10 +3112,19 @@ public class Launcher extends Activity */ public void openFolder(FolderIcon folderIcon) { Folder folder = folderIcon.getFolder(); + Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null; + if (openFolder != null && openFolder != folder) { + // Close any open folder before opening a folder. + closeFolder(); + } + FolderInfo info = folder.mInfo; info.opened = true; + // While the folder is open, the position of the icon cannot change. + ((CellLayout.LayoutParams) folderIcon.getLayoutParams()).canReorder = false; + // Just verify that the folder hasn't already been added to the DragLayer. // There was a one-off crash where the folder had a parent already. if (folder.getParent() == null) { @@ -3151,6 +3160,9 @@ public class Launcher extends Activity if (parent != null) { FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo); shrinkAndFadeInFolderIcon(fi); + if (fi != null) { + ((CellLayout.LayoutParams) fi.getLayoutParams()).canReorder = true; + } } folder.animateClosed(); -- cgit v1.2.3 From f24f2501582ab776071187deb85b1f9c33686b98 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 16 Apr 2015 03:12:19 -0700 Subject: WidgetTray on Kitkat - Fix "getDrawable" not found runtime exception by getting rid of dead code Change-Id: Id511a6c0c4ea1070f5f498cf6458548ace7c54f7 --- src/com/android/launcher3/LauncherStateTransitionAnimation.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 57bd5b2c6..78272a8ec 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -183,11 +183,6 @@ public class LauncherStateTransitionAnimation { public void startAnimationToWidgets(final boolean animated) { final WidgetsContainerView toView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { - @Override - public void onRevealViewVisible(View revealView, View contentView, - View allAppsButtonView) { - revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); - } @Override public float getMaterialRevealViewFinalAlpha(View revealView) { return 0.3f; -- cgit v1.2.3 From b76cd628e657fd050ccf3f4dc31b2e8bc36356e5 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 16 Apr 2015 14:34:09 -0700 Subject: Focus handling null pointer exception during monkey tests. - Also fix a bug where the focus is not navigating to the next page when there isn't an icon within +45 and -45 range of the origin. b/20294717 Change-Id: I16dac5c6a0463fbc9f56a447abedad18abadde98 --- src/com/android/launcher3/FocusHelper.java | 10 +++++++++- src/com/android/launcher3/PagedView.java | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 8791c896a..32ed98c17 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -222,7 +222,7 @@ public class FocusHelper { Hotseat hotseat = (Hotseat) hotseatLayout.getParent(); Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace); - int pageIndex = workspace.getCurrentPage(); + int pageIndex = workspace.getNextPage(); int pageCount = workspace.getChildCount(); int countX = -1; int countY = -1; @@ -231,6 +231,12 @@ public class FocusHelper { .getChildAt(iconIndex).getLayoutParams()).cellX; final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex); + if (iconLayout == null) { + // This check is to guard against cases where key strokes rushes in when workspace + // child creation/deletion is still in flux. (e.g., during drop or fling + // animation.) + return consume; + } final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); ViewGroup parent = null; @@ -364,6 +370,7 @@ public class FocusHelper { } int row = FocusLogic.findRow(matrix, iconIndex); parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); + workspace.snapToPage(newPageIndex); if (parent != null) { iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, @@ -394,6 +401,7 @@ public class FocusHelper { if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) { newPageIndex = pageIndex - 1; } + workspace.snapToPage(newPageIndex); row = FocusLogic.findRow(matrix, iconIndex); parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); if (parent != null) { diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 158a30c40..5dc39309d 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -475,13 +475,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** * Returns the index of the currently displayed page. - * - * @return The index of the currently displayed page. */ int getCurrentPage() { return mCurrentPage; } + /** + * Returns the index of page to be shown immediately afterwards. + */ int getNextPage() { return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; } -- cgit v1.2.3 From 3e2ff8afef31dd48f2ab8666d389f1654bca6368 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 Apr 2015 16:31:22 -0700 Subject: Updating maxscroll only after layouttranstion has finished Bug: 20092568 Change-Id: I6ff8d9759b71b0b00ebfdaa75b558254dbeb0ef9 --- src/com/android/launcher3/PagedView.java | 46 +++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 158a30c40..6e9f27a0a 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; @@ -961,8 +962,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc LayoutParams nextLp; int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft()); - if (mPageScrolls == null || getChildCount() != mChildCountOnLastLayout) { - mPageScrolls = new int[getChildCount()]; + if (mPageScrolls == null || childCount != mChildCountOnLastLayout) { + mPageScrolls = new int[childCount]; } for (int i = startIndex; i != endIndex; i += delta) { @@ -1009,19 +1010,36 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { + if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) { updateCurrentPageScroll(); mFirstLayout = false; } - if (childCount > 0) { - final int index = isLayoutRtl() ? 0 : childCount - 1; - mMaxScrollX = getScrollForPage(index); + final LayoutTransition transition = getLayoutTransition(); + // If the transition is running defer updating max scroll, as some empty pages could + // still be present, and a max scroll change could cause sudden jumps in scroll. + if (transition != null && transition.isRunning()) { + transition.addTransitionListener(new LayoutTransition.TransitionListener() { + + @Override + public void startTransition(LayoutTransition transition, ViewGroup container, + View view, int transitionType) { } + + @Override + public void endTransition(LayoutTransition transition, ViewGroup container, + View view, int transitionType) { + // Wait until all transitions are complete. + if (!transition.isRunning()) { + transition.removeTransitionListener(this); + updateMaxScrollX(); + } + } + }); } else { - mMaxScrollX = 0; + updateMaxScrollX(); } - if (mScroller.isFinished() && mChildCountOnLastLayout != getChildCount() && + if (mScroller.isFinished() && mChildCountOnLastLayout != childCount && !mDeferringForDelete) { if (mRestorePage != INVALID_RESTORE_PAGE) { setCurrentPage(mRestorePage); @@ -1030,13 +1048,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setCurrentPage(getNextPage()); } } - mChildCountOnLastLayout = getChildCount(); + mChildCountOnLastLayout = childCount; if (isReordering(true)) { updateDragViewTranslationDuringDrag(); } } + private void updateMaxScrollX() { + int childCount = getChildCount(); + if (childCount > 0) { + final int index = isLayoutRtl() ? 0 : childCount - 1; + mMaxScrollX = getScrollForPage(index); + } else { + mMaxScrollX = 0; + } + } + public void setPageSpacing(int pageSpacing) { mPageSpacing = pageSpacing; requestLayout(); -- cgit v1.2.3 From fc3c1edf7bbc3f7cb23e79520731d13ccc2da046 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 9 Apr 2015 18:48:21 -0700 Subject: Fixing folder focus logic > Folder items no longer remain in a linear order when a folder gets rearranged, and se we need to use createSparseMatrix instead of createFullArray. Also because of this we need to use getChildAt(x, y) instead of getChildAt(index) > Removing traces of AppsCustomizePage (all apps) from FocusHelper Change-Id: I9007f6b95cb823e27ef4a43ce725fda8ef1b7cf8 --- src/com/android/launcher3/FocusHelper.java | 155 ++++++++------------- .../launcher3/ShortcutAndWidgetContainer.java | 2 +- src/com/android/launcher3/util/FocusLogic.java | 74 +++++----- 3 files changed, 88 insertions(+), 143 deletions(-) diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 32ed98c17..c77d41657 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -16,14 +16,12 @@ package com.android.launcher3; -import android.content.res.Configuration; import android.util.Log; import android.view.KeyEvent; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; -import com.android.launcher3.FocusHelper.PagedViewKeyListener; import com.android.launcher3.util.FocusLogic; import com.android.launcher3.util.Thunk; @@ -52,14 +50,10 @@ public class FocusHelper { private static final String TAG = "FocusHelper"; private static final boolean DEBUG = false; - // - // Key code handling methods. - // - /** - * A keyboard listener for scrollable folders + * Handles key events in paged folder. */ - public static class PagedFolderKeyEventListener extends PagedViewKeyListener { + public static class PagedFolderKeyEventListener implements View.OnKeyListener { private final Folder mFolder; @@ -67,20 +61,6 @@ public class FocusHelper { mFolder = folder; } - @Override - public void handleNoopKey(int keyCode, View v) { - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - mFolder.mFolderName.requestFocus(); - playSoundEffect(keyCode, v); - } - } - } - - /** - * Handles key events in the all apps screen. - */ - public static class PagedViewKeyListener implements View.OnKeyListener { - @Override public boolean onKey(View v, int keyCode, KeyEvent e) { boolean consume = FocusLogic.shouldConsume(keyCode); @@ -88,21 +68,12 @@ public class FocusHelper { return consume; } if (DEBUG) { - Log.v(TAG, String.format("Handle ALL APPS and Folders keyevent=[%s].", + Log.v(TAG, String.format("Handle ALL Folders keyevent=[%s].", KeyEvent.keyCodeToString(keyCode))); } - // Initialize variables. - ViewGroup parentLayout; - ViewGroup itemContainer; - int countX; - int countY; - if (v.getParent() instanceof ShortcutAndWidgetContainer) { - itemContainer = (ViewGroup) v.getParent(); - parentLayout = (ViewGroup) itemContainer.getParent(); - countX = ((CellLayout) parentLayout).getCountX(); - countY = ((CellLayout) parentLayout).getCountY(); - } else { + + if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) { if (LauncherAppState.isDogfoodBuild()) { throw new IllegalStateException("Parent of the focused item is not supported."); } else { @@ -110,15 +81,19 @@ public class FocusHelper { } } + // Initialize variables. + final ShortcutAndWidgetContainer itemContainer = (ShortcutAndWidgetContainer) v.getParent(); + final CellLayout cellLayout = (CellLayout) itemContainer.getParent(); + final int countX = cellLayout.getCountX(); + final int countY = cellLayout.getCountY(); + final int iconIndex = itemContainer.indexOfChild(v); - final PagedView container = (PagedView) parentLayout.getParent(); - final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout)); - final int pageCount = container.getChildCount(); - ViewGroup newParent = null; - View child = null; - // TODO(hyunyoungs): this matrix is not applicable on the last page. - int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true); + final FolderPagedView pagedView = (FolderPagedView) cellLayout.getParent(); + final int pageIndex = pagedView.indexOfChild(cellLayout); + final int pageCount = pagedView.getPageCount(); + + int[][] matrix = FocusLogic.createSparseMatrix(cellLayout); // Process focus. int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, iconIndex, pageIndex, pageCount); @@ -126,60 +101,55 @@ public class FocusHelper { handleNoopKey(keyCode, v); return consume; } + ShortcutAndWidgetContainer newParent = null; + View child = null; + switch (newIconIndex) { case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: - case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: - int newPageIndex = pageIndex - 1; - if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) { - newPageIndex = pageIndex + 1; - } - newParent = getAppsCustomizePage(container, newPageIndex); + case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: + newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1); if (newParent != null) { - int row = FocusLogic.findRow(matrix, iconIndex); - container.snapToPage(newPageIndex); - // no need to create a new matrix. - child = newParent.getChildAt(matrix[countX-1][row]); + int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY; + pagedView.snapToPage(pageIndex - 1); + child = newParent.getChildAt( + ((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) + ^ newParent.invertLayoutHorizontally()) ? 0 : countX - 1, row); } break; case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM: - newParent = getAppsCustomizePage(container, pageIndex - 1); + newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1); if (newParent != null) { - container.snapToPage(pageIndex - 1); - child = newParent.getChildAt(0); + pagedView.snapToPage(pageIndex - 1); + child = newParent.getChildAt(0, 0); } break; case FocusLogic.PREVIOUS_PAGE_LAST_ITEM: - newParent = getAppsCustomizePage(container, pageIndex - 1); + newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1); if (newParent != null) { - container.snapToPage(pageIndex - 1); - child = newParent.getChildAt(newParent.getChildCount() - 1); + pagedView.snapToPage(pageIndex - 1); + child = newParent.getChildAt(countX - 1, countY - 1); } break; case FocusLogic.NEXT_PAGE_FIRST_ITEM: - newParent = getAppsCustomizePage(container, pageIndex + 1); + newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1); if (newParent != null) { - container.snapToPage(pageIndex + 1); - child = newParent.getChildAt(0); + pagedView.snapToPage(pageIndex + 1); + child = newParent.getChildAt(0, 0); } break; case FocusLogic.NEXT_PAGE_LEFT_COLUMN: - case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN: - newPageIndex = pageIndex + 1; - if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) { - newPageIndex = pageIndex -1; - } - newParent = getAppsCustomizePage(container, newPageIndex); + case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: + newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1); if (newParent != null) { - container.snapToPage(newPageIndex); - int row = FocusLogic.findRow(matrix, iconIndex); - child = newParent.getChildAt(matrix[0][row]); + pagedView.snapToPage(pageIndex + 1); + child = FocusLogic.getAdjacentChildInNextPage(newParent, v, newIconIndex); } break; case FocusLogic.CURRENT_PAGE_FIRST_ITEM: - child = container.getChildAt(0); + child = cellLayout.getChildAt(0, 0); break; case FocusLogic.CURRENT_PAGE_LAST_ITEM: - child = itemContainer.getChildAt(itemContainer.getChildCount() - 1); + child = pagedView.getLastItem(); break; default: // Go to some item on the current page. child = itemContainer.getChildAt(newIconIndex); @@ -194,7 +164,12 @@ public class FocusHelper { return consume; } - public void handleNoopKey(int keyCode, View v) { } + public void handleNoopKey(int keyCode, View v) { + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + mFolder.mFolderName.requestFocus(); + playSoundEffect(keyCode, v); + } + } } /** @@ -226,7 +201,7 @@ public class FocusHelper { int pageCount = workspace.getChildCount(); int countX = -1; int countY = -1; - int iconIndex = findIndexOfView(hotseatParent, v); + int iconIndex = hotseatParent.indexOfChild(v); int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets() .getChildAt(iconIndex).getLayoutParams()).cellX; @@ -323,11 +298,12 @@ public class FocusHelper { final ViewGroup launcher = (ViewGroup) workspace.getParent(); final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar); final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat); - int pageIndex = workspace.indexOfChild(iconLayout); - int pageCount = workspace.getChildCount(); + + final int iconIndex = parent.indexOfChild(v); + final int pageIndex = workspace.indexOfChild(iconLayout); + final int pageCount = workspace.getChildCount(); int countX = iconLayout.getCountX(); int countY = iconLayout.getCountY(); - final int iconIndex = findIndexOfView(parent, v); CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0); ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets(); @@ -368,10 +344,11 @@ public class FocusHelper { if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) { newPageIndex = pageIndex + 1; } - int row = FocusLogic.findRow(matrix, iconIndex); + int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY; parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); workspace.snapToPage(newPageIndex); if (parent != null) { + workspace.snapToPage(newPageIndex); iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, iconLayout.getCountX(), row); @@ -402,9 +379,10 @@ public class FocusHelper { newPageIndex = pageIndex - 1; } workspace.snapToPage(newPageIndex); - row = FocusLogic.findRow(matrix, iconIndex); + row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY; parent = getCellLayoutChildrenForIndex(workspace, newPageIndex); if (parent != null) { + workspace.snapToPage(newPageIndex); iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, @@ -439,18 +417,6 @@ public class FocusHelper { // Helper methods. // - /** - * Returns the Viewgroup containing page contents for the page at the index specified. - */ - @Thunk static ViewGroup getAppsCustomizePage(ViewGroup container, int index) { - ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index); - if (page instanceof CellLayout) { - // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren - page = ((CellLayout) page).getShortcutsAndWidgets(); - } - return page; - } - /** * Private helper method to get the CellLayoutChildren given a CellLayout index. */ @@ -460,15 +426,6 @@ public class FocusHelper { return parent.getShortcutsAndWidgets(); } - private static int findIndexOfView(ViewGroup parent, View v) { - for (int i = 0; i < parent.getChildCount(); i++) { - if (v != null && v.equals(parent.getChildAt(i))) { - return i; - } - } - return -1; - } - /** * Helper method to be used for playing sound effects. */ diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index ff0604540..15b617683 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -177,7 +177,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup { child.measure(childWidthMeasureSpec, childheightMeasureSpec); } - private boolean invertLayoutHorizontally() { + public boolean invertLayoutHorizontally() { return mInvertIfRtl && isLayoutRtl(); } diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index 8a08a4e72..a84e7df03 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -18,11 +18,15 @@ package com.android.launcher3.util; import android.util.Log; import android.view.KeyEvent; +import android.view.View; import android.view.ViewGroup; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.ShortcutAndWidgetContainer; + +import java.util.Arrays; /** * Calculates the next item that a {@link KeyEvent} should change the focus to. @@ -69,14 +73,11 @@ public class FocusLogic { * Returns true only if this utility class handles the key code. */ public static boolean shouldConsume(int keyCode) { - if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || + return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END || keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || - keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { - return true; - } - return false; + keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL); } public static int handleKeyEvent(int keyCode, int cntX, int cntY, int [][] map, @@ -138,33 +139,17 @@ public class FocusLogic { } /** - * Returns a matrix of size (m x n) that has been initialized with incremental index starting - * with 0 or a matrix where all the values are initialized to {@link #EMPTY}. + * Returns a matrix of size (m x n) that has been initialized with {@link #EMPTY}. * * @param m number of columns in the matrix * @param n number of rows in the matrix - * @param incrementOrder {@code true} if the matrix contents should increment in reading - * order with 0 indexing. {@code false} if each cell should be - * initialized to {@link #EMPTY}; */ // TODO: get rid of dynamic matrix creation. - public static int[][] createFullMatrix(int m, int n, boolean incrementOrder) { - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid() - .getDeviceProfile(); + private static int[][] createFullMatrix(int m, int n) { int[][] matrix = new int [m][n]; for (int i=0; i < m;i++) { - for (int j=0; j < n; j++) { - if (incrementOrder) { - if (!profile.isLayoutRtl) { - matrix[i][j] = j * m + i; - } else { - matrix[i][j] = j * m + m - i -1; - } - } else { - matrix[i][j] = EMPTY; - } - } + Arrays.fill(matrix[i], EMPTY); } return matrix; } @@ -175,17 +160,18 @@ public class FocusLogic { */ // TODO: get rid of the dynamic matrix creation public static int[][] createSparseMatrix(CellLayout layout) { - ViewGroup parent = layout.getShortcutsAndWidgets(); + ShortcutAndWidgetContainer parent = layout.getShortcutsAndWidgets(); final int m = layout.getCountX(); final int n = layout.getCountY(); + final boolean invert = parent.invertLayoutHorizontally(); - int[][] matrix = createFullMatrix(m, n, false /* initialize to #EMPTY */); + int[][] matrix = createFullMatrix(m, n); // Iterate thru the children. for (int i = 0; i < parent.getChildCount(); i++ ) { int cx = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellX; int cy = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellY; - matrix[cx][cy] = i; + matrix[invert ? (m - cx - 1) : cx][cy] = i; } if (DEBUG) { printMatrix(matrix); @@ -213,7 +199,7 @@ public class FocusLogic { m = iconLayout.getCountX() + hotseatLayout.getCountX(); n = iconLayout.getCountY(); } - int[][] matrix = createFullMatrix(m, n, false /* set all cell to empty */); + int[][] matrix = createFullMatrix(m, n); // Iterate thru the children of the top parent. for (int i = 0; i < iconParent.getChildCount(); i++) { @@ -267,8 +253,7 @@ public class FocusLogic { ViewGroup iconParent = iconLayout.getShortcutsAndWidgets(); - int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY(), - false /* set all cell to empty */); + int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY()); // Iterate thru the children of the top parent. for (int i = 0; i < iconParent.getChildCount(); i++) { @@ -499,22 +484,25 @@ public class FocusLogic { } /** - * Figure out the location of the icon. - * + * @param edgeColumn the column of the new icon. either {@link #NEXT_PAGE_LEFT_COLUMN} or + * {@link #NEXT_PAGE_RIGHT_COLUMN} + * @return the view adjacent to {@param oldView} in the {@param nextPage}. */ - //TODO(hyunyoungs): this helper method should move to CellLayout class while removing the - // dynamic matrix creation all together. - public static int findRow(int[][] matrix, int iconIndex) { - int cntX = matrix.length; - int cntY = matrix[0].length; - - for (int i = 0; i < cntX; i++) { - for (int j = 0; j < cntY; j++) { - if (matrix[i][j] == iconIndex) { - return j; + public static View getAdjacentChildInNextPage( + ShortcutAndWidgetContainer nextPage, View oldView, int edgeColumn) { + final int newRow = ((CellLayout.LayoutParams) oldView.getLayoutParams()).cellY; + + int column = (edgeColumn == NEXT_PAGE_LEFT_COLUMN) ^ nextPage.invertLayoutHorizontally() + ? 0 : (((CellLayout) nextPage.getParent()).getCountX() - 1); + + for (; column >= 0; column--) { + for (int row = newRow; row >= 0; row--) { + View newView = nextPage.getChildAt(column, row); + if (newView != null) { + return newView; } } } - return -1; + return null; } } -- cgit v1.2.3 From fa401a10e7e9341daf6f3c5949bf9331902c26d0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 10 Apr 2015 13:45:42 -0700 Subject: Updating drop button targets > Splitting DeleteDropTarget into delete and uninstall > Showing UninstallDropTarget for app shortcuts on workspace > Showing InfoDropTarget only when developer options is enabled Change-Id: I4396571d2199d1581bb9c733aef88ab9b0ebd79d --- res/layout/search_drop_target_bar.xml | 13 + src/com/android/launcher3/AppsContainerView.java | 3 +- src/com/android/launcher3/ButtonDropTarget.java | 132 ++++++++-- src/com/android/launcher3/DeleteDropTarget.java | 278 +-------------------- src/com/android/launcher3/DragController.java | 9 +- src/com/android/launcher3/DropTarget.java | 4 +- src/com/android/launcher3/Folder.java | 5 +- src/com/android/launcher3/InfoDropTarget.java | 85 +------ .../launcher3/LauncherAccessibilityDelegate.java | 3 +- src/com/android/launcher3/LauncherModel.java | 10 +- src/com/android/launcher3/SearchDropTargetBar.java | 17 +- src/com/android/launcher3/UninstallDropTarget.java | 122 +++++++++ src/com/android/launcher3/Workspace.java | 5 +- 13 files changed, 312 insertions(+), 374 deletions(-) create mode 100644 src/com/android/launcher3/UninstallDropTarget.java diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml index 0d7167e5b..9b0da1d4e 100644 --- a/res/layout/search_drop_target_bar.xml +++ b/res/layout/search_drop_target_bar.xml @@ -41,6 +41,19 @@ android:text="@string/delete_target_label" /> + + + + + + + diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 52bc6b6ef..5f1594f28 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -34,6 +34,7 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; + import com.android.launcher3.util.Thunk; import java.util.List; @@ -286,7 +287,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett @Override public boolean supportsDeleteDropTarget() { - return true; + return false; } @Override diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 019f86c21..5b399087a 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -17,19 +17,29 @@ package com.android.launcher3; import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; import android.widget.TextView; +import com.android.launcher3.R; +import com.android.launcher3.util.Thunk; /** * Implements a DropTarget. */ -public class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener { +public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener { + + private static int DRAG_VIEW_DROP_DURATION = 285; protected final int mTransitionDuration; @@ -44,6 +54,9 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro /** The paint applied to the drag view on hover */ protected int mHoverColor = 0; + protected ColorStateList mOriginalTextColor; + protected TransitionDrawable mDrawable; + public ButtonDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -56,12 +69,37 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro mBottomDragPadding = r.getDimensionPixelSize(R.dimen.drop_target_drag_padding); } - void setLauncher(Launcher launcher) { - mLauncher = launcher; + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mOriginalTextColor = getTextColors(); + + // Remove the text in the Phone UI in landscape + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + if (!LauncherAppState.getInstance().isScreenLarge()) { + setText(""); + } + } } - public boolean acceptDrop(DragObject d) { - return false; + protected void setDrawable(int resId) { + // Get the hover color + mDrawable = (TransitionDrawable) getCurrentDrawable(); + + if (mDrawable == null) { + // TODO: investigate why this is ever happening. Presently only on one known device. + mDrawable = (TransitionDrawable) getResources().getDrawable(resId); + setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null); + } + + if (null != mDrawable) { + mDrawable.setCrossFadeEnabled(true); + } + } + + public void setLauncher(Launcher launcher) { + mLauncher = launcher; } public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) { @@ -78,37 +116,94 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro return null; } - public void onDrop(DragObject d) { + @Override + public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { } + + @Override + public final void onDragEnter(DragObject d) { + d.dragView.setColor(mHoverColor); + mDrawable.startTransition(mTransitionDuration); + setTextColor(mHoverColor); } - public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { + @Override + public void onDragOver(DragObject d) { // Do nothing } - public void onDragEnter(DragObject d) { - d.dragView.setColor(mHoverColor); + protected void resetHoverColor() { + mDrawable.resetTransition(); + setTextColor(mOriginalTextColor); } - public void onDragOver(DragObject d) { - // Do nothing + @Override + public final void onDragExit(DragObject d) { + if (!d.dragComplete) { + d.dragView.setColor(0); + resetHoverColor(); + } else { + // Restore the hover color + d.dragView.setColor(mHoverColor); + } } - public void onDragExit(DragObject d) { - d.dragView.setColor(0); + @Override + public final void onDragStart(DragSource source, Object info, int dragAction) { + mActive = supportsDrop(source, info); + mDrawable.resetTransition(); + setTextColor(mOriginalTextColor); + ((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE); } - public void onDragStart(DragSource source, Object info, int dragAction) { - // Do nothing + @Override + public final boolean acceptDrop(DragObject dragObject) { + return supportsDrop(dragObject.dragSource, dragObject.dragInfo); } + protected abstract boolean supportsDrop(DragSource source, Object info); + + @Override public boolean isDropEnabled() { return mActive; } + @Override public void onDragEnd() { - // Do nothing + mActive = false; } + /** + * On drop animate the dropView to the icon. + */ + @Override + public void onDrop(final DragObject d) { + final DragLayer dragLayer = mLauncher.getDragLayer(); + final Rect from = new Rect(); + dragLayer.getViewRectRelativeToSelf(d.dragView, from); + + int width = mDrawable.getIntrinsicWidth(); + int height = mDrawable.getIntrinsicHeight(); + final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), + width, height); + final float scale = (float) to.width() / from.width(); + mSearchDropTargetBar.deferOnDragEnd(); + + Runnable onAnimationEndRunnable = new Runnable() { + @Override + public void run() { + completeDrop(d); + mSearchDropTargetBar.onDragEnd(); + mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null); + } + }; + dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, + DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2), + new LinearInterpolator(), onAnimationEndRunnable, + DragLayer.ANIMATION_END_DISAPPEAR, null); + } + + @Thunk abstract void completeDrop(DragObject d); + @Override public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) { super.getHitRect(outRect); @@ -120,10 +215,10 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro } private boolean isRtl() { - return (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); + return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } - Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) { + protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) { DragLayer dragLayer = mLauncher.getDragLayer(); // Find the rect to animate to (the view is center aligned) @@ -157,6 +252,7 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro return to; } + @Override public void getLocationInDragLayer(int[] loc) { mLauncher.getDragLayer().getLocationInDragLayer(this, loc); } diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 62aa285ab..aa3e66c09 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -19,33 +19,22 @@ package com.android.launcher3; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.TargetApi; -import android.content.ComponentName; import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.TransitionDrawable; import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.UserManager; import android.util.AttributeSet; import android.view.View; import android.view.ViewConfiguration; -import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; -import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.R; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetsContainerView; public class DeleteDropTarget extends ButtonDropTarget { - private static int DELETE_ANIMATION_DURATION = 285; + private static int FLING_DELETE_ANIMATION_DURATION = 350; private static float FLING_TO_DELETE_FRICTION = 0.035f; private static int MODE_FLING_DELETE_TO_TRASH = 0; @@ -53,13 +42,6 @@ public class DeleteDropTarget extends ButtonDropTarget { private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR; - private ColorStateList mOriginalTextColor; - private TransitionDrawable mUninstallDrawable; - private TransitionDrawable mRemoveDrawable; - private TransitionDrawable mCurrentDrawable; - - @Thunk boolean mWaitingForUninstall = false; - public DeleteDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -71,258 +53,27 @@ public class DeleteDropTarget extends ButtonDropTarget { @Override protected void onFinishInflate() { super.onFinishInflate(); - - // Get the drawable - mOriginalTextColor = getTextColors(); - // Get the hover color - Resources r = getResources(); - mHoverColor = r.getColor(R.color.delete_target_hover_tint); - mUninstallDrawable = (TransitionDrawable) - r.getDrawable(R.drawable.uninstall_target_selector); - mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector); - - mRemoveDrawable.setCrossFadeEnabled(true); - mUninstallDrawable.setCrossFadeEnabled(true); - - // The current drawable is set to either the remove drawable or the uninstall drawable - // and is initially set to the remove drawable, as set in the layout xml. - mCurrentDrawable = (TransitionDrawable) getCurrentDrawable(); - - // Remove the text in the Phone UI in landscape - int orientation = getResources().getConfiguration().orientation; - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - if (!LauncherAppState.getInstance().isScreenLarge()) { - setText(""); - } - } - } - - private boolean isAllAppsApplication(DragSource source, Object info) { - return source.supportsAppInfoDropTarget() && (info instanceof AppInfo); - } - - private boolean isWidget(DragSource source, Object info) { - if (source instanceof WidgetsContainerView) { - if (info instanceof PendingAddItemInfo) { - PendingAddItemInfo addInfo = (PendingAddItemInfo) info; - switch (addInfo.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - return true; - } - } - } - return false; - } - private boolean isDragSourceWorkspaceOrFolder(DragObject d) { - return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder); - } - - private void setHoverColor() { - if (mCurrentDrawable != null) { - mCurrentDrawable.startTransition(mTransitionDuration); - } - setTextColor(mHoverColor); - } - private void resetHoverColor() { - if (mCurrentDrawable != null) { - mCurrentDrawable.resetTransition(); - } - setTextColor(mOriginalTextColor); - } + mHoverColor = getResources().getColor(R.color.delete_target_hover_tint); - @Override - public boolean acceptDrop(DragObject d) { - return willAcceptDrop(d.dragInfo); + setDrawable(R.drawable.remove_target_selector); } - public static boolean willAcceptDrop(Object info) { - if (info instanceof ItemInfo) { - ItemInfo item = (ItemInfo) info; - if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET || - item.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET || - item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { - return true; - } - - if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { - return true; - } - - if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && - item instanceof AppInfo) { - AppInfo appInfo = (AppInfo) info; - return (appInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0; - } - - if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && - item instanceof ShortcutInfo) { - return true; - } - } - return false; + public static boolean willAcceptDrop(DragSource source, Object info) { + return (info instanceof ItemInfo) && source.supportsDeleteDropTarget(); } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Override - public void onDragStart(DragSource source, Object info, int dragAction) { - boolean isVisible = true; - boolean useUninstallLabel = isAllAppsApplication(source, info); - boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget(); - - // If we are dragging an application from AppsCustomize, only show the control if we can - // delete the app (it was downloaded), and rename the string to "uninstall" in such a case. - // Hide the delete target if it is a widget from AppsCustomize. - if (!willAcceptDrop(info) || isWidget(source, info)) { - isVisible = false; - } - if (useUninstallLabel) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - UserManager userManager = (UserManager) - getContext().getSystemService(Context.USER_SERVICE); - Bundle restrictions = userManager.getUserRestrictions(); - if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) - || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) { - isVisible = false; - } - } - } - - if (useUninstallLabel) { - setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null); - } else if (useDeleteLabel) { - setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null); - } else { - isVisible = false; - } - mCurrentDrawable = (TransitionDrawable) getCurrentDrawable(); - - mActive = isVisible; - resetHoverColor(); - ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE); - if (isVisible && getText().length() > 0) { - setText(useUninstallLabel ? R.string.delete_target_uninstall_label - : R.string.delete_target_label); - } + protected boolean supportsDrop(DragSource source, Object info) { + return willAcceptDrop(source, info); } @Override - public void onDragEnd() { - super.onDragEnd(); - mActive = false; - } - - public void onDragEnter(DragObject d) { - super.onDragEnter(d); - - setHoverColor(); - } - - public void onDragExit(DragObject d) { - super.onDragExit(d); - - if (!d.dragComplete) { - resetHoverColor(); - } else { - // Restore the hover color if we are deleting - d.dragView.setColor(mHoverColor); - } - } - - private void animateToTrashAndCompleteDrop(final DragObject d) { - final DragLayer dragLayer = mLauncher.getDragLayer(); - final Rect from = new Rect(); - dragLayer.getViewRectRelativeToSelf(d.dragView, from); - - int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth(); - int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight(); - final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), - width, height); - final float scale = (float) to.width() / from.width(); - - mSearchDropTargetBar.deferOnDragEnd(); - deferCompleteDropIfUninstalling(d); - - Runnable onAnimationEndRunnable = new Runnable() { - @Override - public void run() { - completeDrop(d); - mSearchDropTargetBar.onDragEnd(); - mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null); - } - }; - dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, - DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2), - new LinearInterpolator(), onAnimationEndRunnable, - DragLayer.ANIMATION_END_DISAPPEAR, null); - } - - private void deferCompleteDropIfUninstalling(DragObject d) { - mWaitingForUninstall = false; - if (isUninstallFromWorkspace(d)) { - if (d.dragSource instanceof Folder) { - ((Folder) d.dragSource).deferCompleteDropAfterUninstallActivity(); - } else if (d.dragSource instanceof Workspace) { - ((Workspace) d.dragSource).deferCompleteDropAfterUninstallActivity(); - } - mWaitingForUninstall = true; - } - } - - private boolean isUninstallFromWorkspace(DragObject d) { - return false; - } - @Thunk void completeDrop(DragObject d) { ItemInfo item = (ItemInfo) d.dragInfo; - boolean wasWaitingForUninstall = mWaitingForUninstall; - mWaitingForUninstall = false; - if (isAllAppsApplication(d.dragSource, item)) { - uninstallApp(mLauncher, (AppInfo) item); - } else if (isUninstallFromWorkspace(d)) { - ShortcutInfo shortcut = (ShortcutInfo) item; - if (shortcut.intent != null && shortcut.intent.getComponent() != null) { - final ComponentName componentName = shortcut.intent.getComponent(); - final DragSource dragSource = d.dragSource; - final UserHandleCompat user = shortcut.user; - mWaitingForUninstall = mLauncher.startApplicationUninstallActivity( - componentName, shortcut.flags, user); - if (mWaitingForUninstall) { - final Runnable checkIfUninstallWasSuccess = new Runnable() { - @Override - public void run() { - mWaitingForUninstall = false; - String packageName = componentName.getPackageName(); - boolean uninstallSuccessful = !AllAppsList.packageHasActivities( - getContext(), packageName, user); - if (dragSource instanceof Folder) { - ((Folder) dragSource). - onUninstallActivityReturned(uninstallSuccessful); - } else if (dragSource instanceof Workspace) { - ((Workspace) dragSource). - onUninstallActivityReturned(uninstallSuccessful); - } - } - }; - mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess); - } - } - } else if (isDragSourceWorkspaceOrFolder(d)) { + if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) { removeWorkspaceOrFolderItem(mLauncher, item, null); } - if (wasWaitingForUninstall && !mWaitingForUninstall) { - if (d.dragSource instanceof Folder) { - ((Folder) d.dragSource).onUninstallActivityReturned(false); - } else if (d.dragSource instanceof Workspace) { - ((Workspace) d.dragSource).onUninstallActivityReturned(false); - } - } - } - - public static void uninstallApp(Launcher launcher, AppInfo info) { - launcher.startApplicationUninstallActivity(info.componentName, info.flags, info.user); } /** @@ -354,7 +105,7 @@ public class DeleteDropTarget extends ButtonDropTarget { appWidgetHost.deleteAppWidgetId(widget.appWidgetId); return null; } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } else { return false; @@ -367,18 +118,14 @@ public class DeleteDropTarget extends ButtonDropTarget { return true; } - public void onDrop(DragObject d) { - animateToTrashAndCompleteDrop(d); - } - /** * Creates an animation from the current drag view to the delete trash icon. */ private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer, DragObject d, PointF vel, ViewConfiguration config) { - int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth(); - int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight(); + int width = mDrawable.getIntrinsicWidth(); + int height = mDrawable.getIntrinsicHeight(); final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), width, height); final Rect from = new Rect(); @@ -541,7 +288,6 @@ public class DeleteDropTarget extends ButtonDropTarget { updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime, duration, config); } - deferCompleteDropIfUninstalling(d); Runnable onAnimationEndRunnable = new Runnable() { @Override diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 347374738..b24608cb1 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -125,7 +125,7 @@ public class DragController { /** * Interface to receive notifications when a drag starts or stops */ - interface DragListener { + public interface DragListener { /** * A drag has begun * @@ -400,7 +400,7 @@ public class DragController { } } - void onDeferredEndFling(DropTarget.DragObject d) { + public void onDeferredEndFling(DropTarget.DragObject d) { d.dragSource.onFlingToDeleteCompleted(); } @@ -462,7 +462,8 @@ public class DragController { mLastTouchUpTime = System.currentTimeMillis(); if (mDragging) { PointF vec = isFlingingToDelete(mDragObject.dragSource); - if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) { + if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource, + mDragObject.dragInfo)) { vec = null; } if (vec != null) { @@ -616,7 +617,7 @@ public class DragController { if (mDragging) { PointF vec = isFlingingToDelete(mDragObject.dragSource); - if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) { + if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource, mDragObject.dragInfo)) { vec = null; } if (vec != null) { diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 94ae82b82..c5cca3b28 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -139,7 +139,7 @@ public interface DropTarget { /** * Handle an object being dropped on the DropTarget - * + * * @param source DragSource where the drag started * @param x X coordinate of the drop location * @param y Y coordinate of the drop location @@ -169,7 +169,7 @@ public interface DropTarget { /** * Check if a drop action can occur at, or near, the requested location. * This will be called just before onDrop. - * + * * @param source DragSource where the drag started * @param x X coordinate of the drop location * @param y Y coordinate of the drop location diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 116332438..c35ce944f 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -51,6 +51,7 @@ import android.widget.TextView; import com.android.launcher3.DragController.DragListener; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.util.Thunk; @@ -62,7 +63,7 @@ import java.util.Collections; */ public class Folder extends LinearLayout implements DragSource, View.OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener, DragListener { + View.OnFocusChangeListener, DragListener, UninstallSource { private static final String TAG = "Launcher.Folder"; /** @@ -772,10 +773,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList updateItemLocationsInDatabaseBatch(); } + @Override public void deferCompleteDropAfterUninstallActivity() { mDeferDropAfterUninstall = true; } + @Override public void onUninstallActivityReturned(boolean success) { mDeferDropAfterUninstall = false; mUninstallSuccessful = success; diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index 3c36361aa..e48640c93 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -18,21 +18,14 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.drawable.TransitionDrawable; +import android.provider.Settings; import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; +import com.android.launcher3.R; import com.android.launcher3.compat.UserHandleCompat; public class InfoDropTarget extends ButtonDropTarget { - private ColorStateList mOriginalTextColor; - private TransitionDrawable mDrawable; - public InfoDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -44,43 +37,10 @@ public class InfoDropTarget extends ButtonDropTarget { @Override protected void onFinishInflate() { super.onFinishInflate(); - - mOriginalTextColor = getTextColors(); - // Get the hover color - Resources r = getResources(); - mHoverColor = r.getColor(R.color.info_target_hover_tint); - mDrawable = (TransitionDrawable) getCurrentDrawable(); - - if (mDrawable == null) { - // TODO: investigate why this is ever happening. Presently only on one known device. - mDrawable = (TransitionDrawable) r.getDrawable(R.drawable.info_target_selector); - setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null); - } - - if (null != mDrawable) { - mDrawable.setCrossFadeEnabled(true); - } - - // Remove the text in the Phone UI in landscape - int orientation = getResources().getConfiguration().orientation; - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - if (!LauncherAppState.getInstance().isScreenLarge()) { - setText(""); - } - } - } - - @Override - public boolean acceptDrop(DragObject d) { - // acceptDrop is called just before onDrop. We do the work here, rather than - // in onDrop, because it allows us to reject the drop (by returning false) - // so that the object being dragged isn't removed from the drag source. + mHoverColor = getResources().getColor(R.color.info_target_hover_tint); - startDetailsActivityForInfo(d.dragInfo, mLauncher); - // There is no post-drop animation, so clean up the DragView now - d.deferDragViewCleanupPostAnimation = false; - return false; + setDrawable(R.drawable.info_target_selector); } public static void startDetailsActivityForInfo(Object info, Launcher launcher) { @@ -105,39 +65,14 @@ public class InfoDropTarget extends ButtonDropTarget { } @Override - public void onDragStart(DragSource source, Object info, int dragAction) { - boolean isVisible = true; - - // Hide this button unless we are dragging something from AllApps - if (!source.supportsAppInfoDropTarget()) { - isVisible = false; - } - - mActive = isVisible; - mDrawable.resetTransition(); - setTextColor(mOriginalTextColor); - ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE); + protected boolean supportsDrop(DragSource source, Object info) { + return source.supportsAppInfoDropTarget() && + Settings.Global.getInt(getContext().getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1; } @Override - public void onDragEnd() { - super.onDragEnd(); - mActive = false; - } - - public void onDragEnter(DragObject d) { - super.onDragEnter(d); - - mDrawable.startTransition(mTransitionDuration); - setTextColor(mHoverColor); - } - - public void onDragExit(DragObject d) { - super.onDragExit(d); - - if (!d.dragComplete) { - mDrawable.resetTransition(); - setTextColor(mOriginalTextColor); - } + void completeDrop(DragObject d) { + startDetailsActivityForInfo(d.dragInfo, mLauncher); } } diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index cfc1bd96c..8ba02ea5f 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -105,7 +105,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); return true; } else if (action == UNINSTALL) { - DeleteDropTarget.uninstallApp(mLauncher, (AppInfo) item); + AppInfo info = (AppInfo) item; + mLauncher.startApplicationUninstallActivity(info.componentName, info.flags, info.user); return true; } else if (action == MOVE) { beginAccessibleDrag(host, item); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 37f1ea86e..f7df6bc1a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1079,7 +1079,7 @@ public class LauncherModel extends BroadcastReceiver * @param context * @param item */ - static void deleteItemFromDatabase(Context context, final ItemInfo item) { + public static void deleteItemFromDatabase(Context context, final ItemInfo item) { ArrayList items = new ArrayList(); items.add(item); deleteItemsFromDatabase(context, items); @@ -1185,7 +1185,7 @@ public class LauncherModel extends BroadcastReceiver /** * Remove the contents of the specified folder from the database */ - static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) { + public static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) { final ContentResolver cr = context.getContentResolver(); Runnable r = new Runnable() { @@ -3106,6 +3106,9 @@ public class LauncherModel extends BroadcastReceiver si.status &= ~ShortcutInfo.FLAG_RESTORED_ICON & ~ShortcutInfo.FLAG_AUTOINTALL_ICON & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; + if (appInfo != null) { + si.flags = appInfo.flags; + } infoUpdated = true; si.updateIcon(mIconCache); @@ -3414,6 +3417,9 @@ public class LauncherModel extends BroadcastReceiver info.user = user; info.contentDescription = mUserManager.getBadgedLabelForUser( info.title.toString(), info.user); + if (lai != null) { + info.flags = AppInfo.initFlags(lai); + } return info; } diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index cc17820ff..a8dcd0f06 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -44,11 +44,14 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D private boolean mIsSearchBarHidden; private View mQSBSearchBar; private View mDropTargetBar; - private ButtonDropTarget mInfoDropTarget; - private ButtonDropTarget mDeleteDropTarget; private int mBarHeight; private boolean mDeferOnDragEnd = false; + // Drop targets + private ButtonDropTarget mInfoDropTarget; + private ButtonDropTarget mDeleteDropTarget; + private ButtonDropTarget mUninstallDropTarget; + private boolean mEnableDropDownDropTargets; public SearchDropTargetBar(Context context, AttributeSet attrs) { @@ -61,13 +64,19 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D public void setup(Launcher launcher, DragController dragController) { dragController.addDragListener(this); + dragController.setFlingToDeleteDropTarget(mDeleteDropTarget); + dragController.addDragListener(mInfoDropTarget); dragController.addDragListener(mDeleteDropTarget); + dragController.addDragListener(mUninstallDropTarget); + dragController.addDropTarget(mInfoDropTarget); dragController.addDropTarget(mDeleteDropTarget); - dragController.setFlingToDeleteDropTarget(mDeleteDropTarget); + dragController.addDropTarget(mUninstallDropTarget); + mInfoDropTarget.setLauncher(launcher); mDeleteDropTarget.setLauncher(launcher); + mUninstallDropTarget.setLauncher(launcher); } public void setQsbSearchBar(View qsb) { @@ -116,9 +125,11 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D mDropTargetBar = findViewById(R.id.drag_target_bar); mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text); mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text); + mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text); mInfoDropTarget.setSearchDropTargetBar(this); mDeleteDropTarget.setSearchDropTargetBar(this); + mUninstallDropTarget.setSearchDropTargetBar(this); mEnableDropDownDropTargets = getResources().getBoolean(R.bool.config_useDropTargetDownTransition); diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java new file mode 100644 index 000000000..4a7fffeb2 --- /dev/null +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -0,0 +1,122 @@ +package com.android.launcher3; + +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.os.UserManager; +import android.util.AttributeSet; +import android.util.Pair; + +import com.android.launcher3.R; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.Thunk; + +public class UninstallDropTarget extends ButtonDropTarget { + + public UninstallDropTarget(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + // Get the hover color + mHoverColor = getResources().getColor(R.color.delete_target_hover_tint); + + setDrawable(R.drawable.uninstall_target_selector); + } + + @Override + protected boolean supportsDrop(DragSource source, Object info) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + UserManager userManager = (UserManager) + getContext().getSystemService(Context.USER_SERVICE); + Bundle restrictions = userManager.getUserRestrictions(); + if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) + || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) { + return false; + } + } + + Pair componentInfo = getAppInfoFlags(info); + return componentInfo != null && (componentInfo.second & AppInfo.DOWNLOADED_FLAG) != 0; + } + + /** + * @return the component name and flags if {@param info} is an AppInfo or an app shortcut. + */ + private static Pair getAppInfoFlags(Object item) { + if (item instanceof AppInfo) { + AppInfo info = (AppInfo) item; + return Pair.create(info.componentName, info.flags); + } else if (item instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo) item; + ComponentName component = info.getTargetComponent(); + if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION + && component != null) { + return Pair.create(component, info.flags); + } + } + return null; + } + + @Override + public void onDrop(DragObject d) { + // Differ item deletion + if (d.dragSource instanceof UninstallSource) { + ((UninstallSource) d.dragSource).deferCompleteDropAfterUninstallActivity(); + } + super.onDrop(d); + } + + @Override + void completeDrop(final DragObject d) { + final Pair componentInfo = getAppInfoFlags(d.dragInfo); + final UserHandleCompat user = ((ItemInfo) d.dragInfo).user; + if (mLauncher.startApplicationUninstallActivity( + componentInfo.first, componentInfo.second, user)) { + + final Runnable checkIfUninstallWasSuccess = new Runnable() { + @Override + public void run() { + String packageName = componentInfo.first.getPackageName(); + boolean uninstallSuccessful = !AllAppsList.packageHasActivities( + getContext(), packageName, user); + sendUninstallResult(d.dragSource, uninstallSuccessful); + } + }; + mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess); + } else { + sendUninstallResult(d.dragSource, false); + } + } + + @Thunk void sendUninstallResult(DragSource target, boolean result) { + if (target instanceof UninstallSource) { + ((UninstallSource) target).onUninstallActivityReturned(result); + } + } + + /** + * Interface defining an object that can provide uninstallable drag objects. + */ + public static interface UninstallSource { + + /** + * A pending uninstall operation was complete. + * @param result true if uninstall was successful, false otherwise. + */ + void onUninstallActivityReturned(boolean result); + + /** + * Indicates that an uninstall request are made and the actual result may come + * after some time. + */ + void deferCompleteDropAfterUninstallActivity(); + } +} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 043ecb075..568fbb301 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -66,6 +66,7 @@ import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; @@ -88,7 +89,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class Workspace extends SmoothPagedView implements DropTarget, DragSource, DragScroller, View.OnTouchListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, - Insettable { + Insettable, UninstallSource { private static final String TAG = "Launcher.Workspace"; private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0; @@ -4269,11 +4270,13 @@ public class Workspace extends SmoothPagedView } } + @Override public void deferCompleteDropAfterUninstallActivity() { mDeferDropAfterUninstall = true; } /// maybe move this into a smaller part + @Override public void onUninstallActivityReturned(boolean success) { mDeferDropAfterUninstall = false; mUninstallSuccessful = success; -- cgit v1.2.3 From 9cbea78b19badc86299b0ac4254799d8a3bf9684 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 16 Apr 2015 21:18:34 -0700 Subject: Ensuring that our animation callbacks are not stripped by pro guard. Bug 20296035 --- proguard.flags | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proguard.flags b/proguard.flags index 83a491dfd..ee6bfa5d1 100644 --- a/proguard.flags +++ b/proguard.flags @@ -62,3 +62,8 @@ public int getBrightness(); public void setBrightness(int); } + +-keep class com.android.launcher3.AppsContainerRecyclerView { + public void setFastScrollerAlpha(float); + public float getFastScrollerAlpha(); +} \ No newline at end of file -- cgit v1.2.3 From 18b640c3710723df8a8528a667c373474e0a06ee Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 17 Apr 2015 09:24:01 -0700 Subject: Removing LauncherProvider dependency from LauncherSettings > This ensures that the classLoader does not need to load LauncherProvider to access LauncherSettings > Making a few fields public. Change-Id: I2e8ee6e169552739f09e500d3b6acbcee7cd0718 --- src/com/android/launcher3/LauncherProvider.java | 4 ++-- src/com/android/launcher3/LauncherSettings.java | 32 +++++++++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index d75ef738a..f9f5ae1bb 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -69,8 +69,8 @@ public class LauncherProvider extends ContentProvider { static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; - static final String TABLE_FAVORITES = "favorites"; - static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens"; + static final String TABLE_FAVORITES = LauncherSettings.Favorites.TABLE_NAME; + static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 111de409e..90e60e450 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -19,6 +19,8 @@ package com.android.launcher3; import android.net.Uri; import android.provider.BaseColumns; +import com.android.launcher3.config.ProviderConfig; + /** * Settings related utilities. */ @@ -45,7 +47,7 @@ public class LauncherSettings { * an Intent that can be launched. *

Type: TEXT

*/ - static final String INTENT = "intent"; + public static final String INTENT = "intent"; /** * The type of the gesture @@ -104,29 +106,35 @@ public class LauncherSettings { * * Tracks the order of workspace screens. */ - static final class WorkspaceScreens implements ChangeLogColumns { + public static final class WorkspaceScreens implements ChangeLogColumns { + + public static final String TABLE_NAME = "workspaceScreens"; + /** * The content:// style URL for this table */ static final Uri CONTENT_URI = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_WORKSPACE_SCREENS); + ProviderConfig.AUTHORITY + "/" + TABLE_NAME); /** * The rank of this screen -- ie. how it is ordered relative to the other screens. *

Type: INTEGER

*/ - static final String SCREEN_RANK = "screenRank"; + public static final String SCREEN_RANK = "screenRank"; } /** * Favorites. */ public static final class Favorites implements BaseLauncherColumns { + + public static final String TABLE_NAME = "favorites"; + /** * The content:// style URL for this table */ - static final Uri CONTENT_URI = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_FAVORITES); + public static final Uri CONTENT_URI = Uri.parse("content://" + + ProviderConfig.AUTHORITY + "/" + TABLE_NAME); /** * The content:// style URL for a given row, identified by its id. @@ -136,21 +144,21 @@ public class LauncherSettings { * @return The unique content URL for the specified row. */ static Uri getContentUri(long id) { - return Uri.parse("content://" + LauncherProvider.AUTHORITY + - "/" + LauncherProvider.TABLE_FAVORITES + "/" + id); + return Uri.parse("content://" + ProviderConfig.AUTHORITY + + "/" + TABLE_NAME + "/" + id); } /** * The container holding the favorite *

Type: INTEGER

*/ - static final String CONTAINER = "container"; + public static final String CONTAINER = "container"; /** * The icon is a resource identified by a package name and an integer id. */ - static final int CONTAINER_DESKTOP = -100; - static final int CONTAINER_HOTSEAT = -101; + public static final int CONTAINER_DESKTOP = -100; + public static final int CONTAINER_HOTSEAT = -101; static final String containerToString(int container) { switch (container) { @@ -164,7 +172,7 @@ public class LauncherSettings { * The screen holding the favorite (if container is CONTAINER_DESKTOP) *

Type: INTEGER

*/ - static final String SCREEN = "screen"; + public static final String SCREEN = "screen"; /** * The X coordinate of the cell holding the favorite -- cgit v1.2.3 From 546bbf2f8d31261c720ed7b06d3f4e4b5bea268f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 17 Apr 2015 12:51:24 -0700 Subject: Removing market icon Change-Id: I7837cd8083cf2c07444b509fcfb7447151ba76b5 --- proguard.flags | 1 - res/drawable-hdpi/ic_launcher_market_holo.png | Bin 1644 -> 0 bytes res/drawable-mdpi/ic_launcher_market_holo.png | Bin 1124 -> 0 bytes res/drawable-xhdpi/ic_launcher_market_holo.png | Bin 2187 -> 0 bytes 4 files changed, 1 deletion(-) delete mode 100644 res/drawable-hdpi/ic_launcher_market_holo.png delete mode 100644 res/drawable-mdpi/ic_launcher_market_holo.png delete mode 100644 res/drawable-xhdpi/ic_launcher_market_holo.png diff --git a/proguard.flags b/proguard.flags index ee6bfa5d1..e2a4b5b31 100644 --- a/proguard.flags +++ b/proguard.flags @@ -6,7 +6,6 @@ public void onClickVoiceButton(android.view.View); public void onClickConfigureButton(android.view.View); public void onClickAllAppsButton(android.view.View); - public void onClickAppMarketButton(android.view.View); public void dismissFirstRunCling(android.view.View); public void dismissMigrationClingCopyApps(android.view.View); public void dismissMigrationClingUseDefault(android.view.View); diff --git a/res/drawable-hdpi/ic_launcher_market_holo.png b/res/drawable-hdpi/ic_launcher_market_holo.png deleted file mode 100644 index dc7825114..000000000 Binary files a/res/drawable-hdpi/ic_launcher_market_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_market_holo.png b/res/drawable-mdpi/ic_launcher_market_holo.png deleted file mode 100644 index cacb37484..000000000 Binary files a/res/drawable-mdpi/ic_launcher_market_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_market_holo.png b/res/drawable-xhdpi/ic_launcher_market_holo.png deleted file mode 100644 index 958f0de3c..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_market_holo.png and /dev/null differ -- cgit v1.2.3 From 466663edeec8d889aca37612aab2ab2d67da4ad8 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 17 Apr 2015 12:11:01 -0700 Subject: Accounting for recent changes in padding when detecting backgound taps to close AllApps. - Also ensuring that we keep the search bar visible in all apps only if it is being overridden. Change-Id: Iba980ecec255da80aff8ff57b42ad99d70a2122a --- .../launcher3/AppsContainerRecyclerView.java | 15 ----- src/com/android/launcher3/AppsContainerView.java | 65 +++++++++++++++++++++- src/com/android/launcher3/Workspace.java | 4 +- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index bb2aeb096..16244ee35 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -196,21 +196,6 @@ public class AppsContainerRecyclerView extends RecyclerView } break; case MotionEvent.ACTION_UP: - ViewConfiguration viewConfig = ViewConfiguration.get(getContext()); - float dx = ev.getX() - mDownX; - float dy = ev.getY() - mDownY; - float distance = (float) Math.sqrt(dx * dx + dy * dy); - if (distance < viewConfig.getScaledTouchSlop()) { - Rect backgroundPadding = new Rect(); - getBackground().getPadding(backgroundPadding); - boolean isOutsideBounds = ev.getX() < backgroundPadding.left || - ev.getX() > (getWidth() - backgroundPadding.right); - if (isOutsideBounds) { - Launcher launcher = (Launcher) getContext(); - launcher.showWorkspace(true); - } - } - // Fall through case MotionEvent.ACTION_CANCEL: mDraggingFastScroller = false; animateFastScrollerVisibility(false); diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 52bc6b6ef..376f888f6 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -63,7 +63,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private EditText mSearchBarView; private int mNumAppsPerRow; - private Point mLastTouchDownPos = new Point(); + private Point mLastTouchDownPos = new Point(-1, -1); + private Point mLastTouchPos = new Point(); private Rect mInsets = new Rect(); private Rect mFixedBounds = new Rect(); private int mContentMarginStart; @@ -234,12 +235,22 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett updatePaddings(); } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return handleTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return handleTouchEvent(ev); + } + @Override public boolean onTouch(View v, MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: - mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); + mLastTouchPos.set((int) ev.getX(), (int) ev.getY()); break; } return false; @@ -256,7 +267,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett if (!mLauncher.isDraggingEnabled()) return false; // Start the drag - mLauncher.getWorkspace().beginDragShared(v, mLastTouchDownPos, this, false); + mLauncher.getWorkspace().beginDragShared(v, mLastTouchPos, this, false); // We delay entering spring-loaded mode slightly to make sure the UI // thready is free of any work. @@ -428,6 +439,54 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett } } + /** + * Handles the touch events to dismiss all apps when clicking outside the bounds of the + * recycler view. + */ + private boolean handleTouchEvent(MotionEvent ev) { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + if (!mFixedBounds.isEmpty()) { + // Outset the fixed bounds and check if the touch is outside all apps + Rect tmpRect = new Rect(mFixedBounds); + tmpRect.inset(-grid.allAppsIconSizePx / 2, 0); + if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) { + mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); + return true; + } + } else { + // Check if the touch is outside all apps + if (ev.getX() < getPaddingLeft() || + ev.getX() > (getWidth() - getPaddingRight())) { + mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); + return true; + } + } + break; + case MotionEvent.ACTION_UP: + if (mLastTouchDownPos.x > -1) { + ViewConfiguration viewConfig = ViewConfiguration.get(getContext()); + float dx = ev.getX() - mLastTouchDownPos.x; + float dy = ev.getY() - mLastTouchDownPos.y; + float distance = (float) Math.hypot(dx, dy); + if (distance < viewConfig.getScaledTouchSlop()) { + // The background was clicked, so just go home + Launcher launcher = (Launcher) getContext(); + launcher.showWorkspace(true); + return true; + } + } + // Fall through + case MotionEvent.ACTION_CANCEL: + mLastTouchDownPos.set(-1, -1); + break; + } + return false; + } + /** * Update the padding of the Apps view and children. To ensure that the RecyclerView has the * full width to handle touches right to the edge of the screen, we only apply the top and diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 043ecb075..671dcaa74 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2264,7 +2264,9 @@ public class Workspace extends SmoothPagedView float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; // We keep the search bar visible on the workspace and in AllApps now - float finalSearchBarAlpha = (stateIsNormal || stateIsNormalHidden) ? 1f : 0f; + boolean showSearchBar = stateIsNormal || + (mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden); + float finalSearchBarAlpha = showSearchBar ? 1f : 0f; float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? getOverviewModeTranslationY() : 0; -- cgit v1.2.3 From 2826a401edb12e53cd19e76af3551c34fef05755 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 17 Apr 2015 16:18:53 -0700 Subject: Fixing build. --- src/com/android/launcher3/Launcher.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3d8cf31c7..2bfb29ef6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4415,6 +4415,17 @@ public class Launcher extends Activity return null; } + /** + * Returns whether the launcher callbacks overrides search in all apps. + * @return + */ + @Thunk boolean isAllAppsSearchOverridden() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.overrideAllAppsSearch(); + } + return false; + } + private boolean shouldRunFirstRunActivity() { return !ActivityManager.isRunningInTestHarness() && !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false); -- cgit v1.2.3 From 75ae8d3bf9992440314a55f0ed3ff3285e6cd8fa Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Sun, 19 Apr 2015 16:10:34 -0700 Subject: Notify data-set changed when new items are added/removed. - Temporarily use the full data-set changed call, when possible, we'll moved to the SortedList in the support lib which will take care of most of this for us. Bug: 20363676 --- .../android/launcher3/AlphabeticalAppsList.java | 36 ++-------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 8d1db632e..2ee5a62ed 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -239,17 +239,9 @@ public class AlphabeticalAppsList { for (AppInfo info : apps) { int removeIndex = findAppByComponent(mApps, info); if (removeIndex != -1) { - int sectionedIndex = mSectionedFilteredApps.indexOf(info); - int numAppsInSection = numAppsInSection(info); mApps.remove(removeIndex); onAppsUpdated(); - if (numAppsInSection == 1) { - // Remove the section and the icon - mAdapter.notifyItemRemoved(sectionedIndex - 1); - mAdapter.notifyItemRemoved(sectionedIndex - 1); - } else { - mAdapter.notifyItemRemoved(sectionedIndex); - } + mAdapter.notifyDataSetChanged(); } } } @@ -278,32 +270,8 @@ public class AlphabeticalAppsList { if (index < 0) { mApps.add(-(index + 1), info); onAppsUpdated(); - - int sectionedIndex = mSectionedFilteredApps.indexOf(info); - int numAppsInSection = numAppsInSection(info); - if (numAppsInSection == 1) { - // New section added along with icon - mAdapter.notifyItemInserted(sectionedIndex - 1); - mAdapter.notifyItemInserted(sectionedIndex - 1); - } else { - mAdapter.notifyItemInserted(sectionedIndex); - } - } - } - - /** - * Returns the number of apps in the section that the given info is in. - */ - private int numAppsInSection(AppInfo info) { - int appIndex = mFilteredApps.indexOf(info); - int appCount = 0; - for (SectionInfo section : mSections) { - if (appCount + section.numAppsInSection > appIndex) { - return section.numAppsInSection; - } - appCount += section.numAppsInSection; + mAdapter.notifyDataSetChanged(); } - return 1; } /** -- cgit v1.2.3 From 4cea4c830283e017fc36197cfe9d7ce713aec49d Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 17 Apr 2015 19:02:30 -0700 Subject: WidgetTray UI tweak - set fixed image view width - fade in when widget bitmap populates - widget dimension is next to the widget name text. - elevation of the widgets content and the reveal view is the same b/19897708 Change-Id: Ia656144412e7d63a491ce67ff15fb58c05d9a9d9 --- res/layout/widget_cell.xml | 12 +++++------- res/layout/widgets_view.xml | 10 +++++----- res/values-sw600dp/dimens.xml | 4 ++-- res/values-v17/styles.xml | 2 +- res/values/dimens.xml | 5 +++-- res/values/styles.xml | 2 +- .../launcher3/LauncherStateTransitionAnimation.java | 20 ++++++++++++++++---- src/com/android/launcher3/widget/WidgetCell.java | 8 +++----- .../launcher3/widget/WidgetsContainerView.java | 2 +- 9 files changed, 37 insertions(+), 28 deletions(-) diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index a5b25aadb..f53b74ef8 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -16,16 +16,16 @@ @@ -73,12 +74,9 @@ diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 8e7ed161a..0800f59aa 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -25,16 +25,16 @@ android:paddingBottom="@dimen/widget_container_inset" android:descendantFocusability="afterDescendants"> - - + android:layout_gravity="center" + android:visibility="invisible" + android:focusable="false" /> 8dp - @dimen/widget_preview_padding_left - @dimen/widget_preview_padding_right + @dimen/widget_preview_label_horizontal_padding + @dimen/widget_preview_label_horizontal_padding 400dp diff --git a/res/values-v17/styles.xml b/res/values-v17/styles.xml index 229375f85..3589e80ac 100644 --- a/res/values-v17/styles.xml +++ b/res/values-v17/styles.xml @@ -1,6 +1,6 @@ diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 101b7558a..cad60fbae 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -81,15 +81,16 @@ 8dp 140dp - 16dp - 16dp 8dp 8dp 8dp + 8dp 52dp 8dp + + 156dp 160dp diff --git a/res/values/styles.xml b/res/values/styles.xml index 94efebc06..16d4cbeed 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -92,7 +92,7 @@ diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 78272a8ec..8ba5c60f3 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -182,7 +182,13 @@ public class LauncherStateTransitionAnimation { */ public void startAnimationToWidgets(final boolean animated) { final WidgetsContainerView toView = mLauncher.getWidgetsView(); + final Resources res = mLauncher.getResources(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); + } @Override public float getMaterialRevealViewFinalAlpha(View revealView) { return 0.3f; @@ -192,8 +198,9 @@ public class LauncherStateTransitionAnimation { return revealView.getMeasuredHeight() / 2; } }; - startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), animated, true /* hideSearchBar */, cb); + startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, + toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */, + cb); } /** @@ -386,7 +393,6 @@ public class LauncherStateTransitionAnimation { mStateAnimation.start(); } }; - toView.bringToFront(); toView.setVisibility(View.VISIBLE); toView.post(startAnimRunnable); @@ -481,8 +487,14 @@ public class LauncherStateTransitionAnimation { private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, final Workspace.State toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { - WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); + final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); + final Resources res = mLauncher.getResources(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); + } @Override public float getMaterialRevealViewFinalYDrift(View revealView) { return revealView.getMeasuredHeight() / 2; diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index d10c3049e..1ae75c3cc 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -50,10 +50,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final String TAG = "WidgetCell"; private static final boolean DEBUG = false; - // Temporary preset width and height of the image to keep them aligned. - //private static final int PRESET_PREVIEW_HEIGHT = 480; - //private static final int PRESET_PREVIEW_WIDTH = 480; - + private static final int FADE_IN_DURATION_MS = 70; private int mPresetPreviewSize; private static WidgetCell sShortpressTarget = null; @@ -210,7 +207,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mOriginalImagePadding.right, mOriginalImagePadding.bottom); } - image.setAlpha(1f); + image.setAlpha(0f); + image.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS); image.mAllowRequestLayout = true; image.requestLayout(); } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 5aa80a9e2..292a5de20 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -126,7 +126,7 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie // public View getContentView() { - return findViewById(R.id.widgets_list_view); + return findViewById(R.id.widgets_content); } public View getRevealView() { -- cgit v1.2.3 From d5a03eb0b3015e8d307299ea2c8771b3b204a6ea Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 20 Apr 2015 17:09:37 -0700 Subject: Ensuring that we skip item decoration drawing when there are no apps. Bug: 20431579 Change-Id: I870c43ff8a94e2cb2c68869f2a31001d028ad58a --- src/com/android/launcher3/AppsGridAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index c8ce397f2..954c59ffc 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -72,6 +72,10 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { List items = mApps.getAdapterItems(); + if (items.isEmpty()) { + return; + } + for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); -- cgit v1.2.3 From 7e2a3608b6fa51c884e051625e10d13597cb8796 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 20 Apr 2015 18:19:25 -0700 Subject: Preventing concurrent modification exception during widget restore Change-Id: If896c5ea7136d132f30be8127b651449bde17447 --- src/com/android/launcher3/LauncherAppWidgetHost.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index a28fd255a..583f85ad0 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -84,8 +84,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost { mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher, true /* refresh */)); - for (Runnable callback : mProviderChangeListeners) { - callback.run(); + if (!mProviderChangeListeners.isEmpty()) { + for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) { + callback.run(); + } } } -- cgit v1.2.3 From 3af56b605217e4ff1c6ed79a70fa49eacaf5b02f Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 20 Apr 2015 20:40:03 -0700 Subject: Remove duplicate calls to getSortedWidgetsAndShortcuts b/19904873 Change-Id: I2e56bc0c7524caa2ef3c5fb00f35cb032cd1860b --- .../android/launcher3/LauncherAppWidgetHost.java | 4 +-- src/com/android/launcher3/LauncherModel.java | 36 ++++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index a28fd255a..55596e5bf 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -81,9 +81,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { protected void onProvidersChanged() { // Once we get the message that widget packages are updated, we need to rebind items // in AppsCustomize accordingly. - mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher, - true /* refresh */)); - + mLauncher.getModel().loadAndBindWidgetsAndShortcuts(mLauncher, mLauncher); for (Runnable callback : mProviderChangeListeners) { callback.run(); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index b19496147..2e5d4e9b3 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -39,6 +39,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Rect; import android.net.Uri; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -3320,19 +3321,9 @@ public class LauncherModel extends BroadcastReceiver } }); } - - final ArrayList widgetsAndShortcuts = - getSortedWidgetsAndShortcuts(context, true /* refresh */); - mHandler.post(new Runnable() { - @Override - public void run() { - Callbacks cb = getCallback(); - if (callbacks == cb && cb != null) { - callbacks.bindPackagesUpdated(widgetsAndShortcuts); - } - } - }); - + if (Build.VERSION.SDK_INT < 17) { + loadAndBindWidgetsAndShortcuts(context, callbacks); + } // Write all the logs to disk mHandler.post(new Runnable() { public void run() { @@ -3381,6 +3372,25 @@ public class LauncherModel extends BroadcastReceiver } } + public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks) { + runOnWorkerThread(new Runnable(){ + @Override + public void run() { + final ArrayList list = + getSortedWidgetsAndShortcuts(context, true /* refresh */); + mHandler.post(new Runnable() { + @Override + public void run() { + Callbacks cb = getCallback(); + if (callbacks == cb && cb != null) { + callbacks.bindPackagesUpdated(list); + } + } + }); + } + }); + } + // Returns a list of ResolveInfos/AppWidgetInfos in sorted order public static ArrayList getSortedWidgetsAndShortcuts(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); -- cgit v1.2.3 From 7f834d2b3084f012b935e6e95f038f2eb4a0acfa Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 21 Apr 2015 10:10:23 -0700 Subject: Using a different userManager class as getUserForSerialNumber gives a valid userHandle for deleted user. > Also chaching the user handles during loader to avoid multiple calls for UserManager Bug: 19389411 Change-Id: I95af3b41b2c9d2ea41d9a75b1020656f57f5e4c9 --- src/com/android/launcher3/LauncherModel.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f7df6bc1a..353de130d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1865,6 +1865,11 @@ public class LauncherModel extends BroadcastReceiver final int optionsIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.OPTIONS); + final LongSparseArray allUsers = new LongSparseArray<>(); + for (UserHandleCompat user : mUserManager.getUserProfiles()) { + allUsers.put(mUserManager.getSerialNumberForUser(user), user); + } + ShortcutInfo info; String intentDescription; LauncherAppWidgetInfo appWidgetInfo; @@ -1886,7 +1891,7 @@ public class LauncherModel extends BroadcastReceiver id = c.getLong(idIndex); intentDescription = c.getString(intentIndex); serialNumber = c.getInt(profileIdIndex); - user = mUserManager.getUserForSerialNumber(serialNumber); + user = allUsers.get(serialNumber); int promiseType = c.getInt(restoredIndex); int disabledState = 0; boolean itemReplaced = false; @@ -2149,23 +2154,28 @@ public class LauncherModel extends BroadcastReceiver LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; int appWidgetId = c.getInt(appWidgetIdIndex); - serialNumber= c.getLong(profileIdIndex); + serialNumber = c.getLong(profileIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); id = c.getLong(idIndex); + user = allUsers.get(serialNumber); + if (user == null) { + itemsToRemove.add(id); + continue; + } + final ComponentName component = ComponentName.unflattenFromString(savedProvider); final int restoreStatus = c.getInt(restoredIndex); final boolean isIdValid = (restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0; - final boolean wasProviderReady = (restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0; final LauncherAppWidgetProviderInfo provider = LauncherModel.getProviderInfo(context, ComponentName.unflattenFromString(savedProvider), - mUserManager.getUserForSerialNumber(serialNumber)); + user); final boolean isProviderReady = isValidProvider(provider); if (!isSafeMode && !customWidget && -- cgit v1.2.3 From dc61c4d41468a8beced2863de1b863b340cf49ba Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 20 Apr 2015 18:26:57 -0700 Subject: Refactoring to single path for overview mode. Change-Id: I590a813c6f031342d75c3a6c3e9c9afda1808f2e --- res/values-sw720dp/config.xml | 4 - res/values/config.xml | 5 +- src/com/android/launcher3/Launcher.java | 42 +- src/com/android/launcher3/LauncherClings.java | 2 +- .../LauncherStateTransitionAnimation.java | 43 +- src/com/android/launcher3/PagedView.java | 4 +- src/com/android/launcher3/SearchDropTargetBar.java | 123 ++--- src/com/android/launcher3/Workspace.java | 532 ++------------------- .../WorkspaceStateTransitionAnimation.java | 519 ++++++++++++++++++++ 9 files changed, 653 insertions(+), 621 deletions(-) create mode 100644 src/com/android/launcher3/WorkspaceStateTransitionAnimation.java diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml index 3d2ca8318..af6751e60 100644 --- a/res/values-sw720dp/config.xml +++ b/res/values-sw720dp/config.xml @@ -6,10 +6,6 @@ 90 - - - false - false diff --git a/res/values/config.xml b/res/values/config.xml index 1acace6f9..47394a1f8 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -27,7 +27,7 @@ 55 - 300 + 100 250 @@ -55,9 +55,6 @@ 150 - - false - 0 diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2bfb29ef6..7364a9f20 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -214,7 +214,6 @@ public class Launcher extends Activity /** The different states that Launcher can be in. */ enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }; @Thunk State mState = State.WORKSPACE; - @Thunk AnimatorSet mStateAnimation; @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -528,7 +527,8 @@ public class Launcher extends Activity @Override public void dismissAllApps() { - showWorkspace(true); + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, null, + false /* notifyLauncherCallbacks */); } }); return true; @@ -785,7 +785,7 @@ public class Launcher extends Activity return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(false); + showWorkspace(false); } return; } @@ -1220,7 +1220,6 @@ public class Launcher extends Activity return false; } - public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks, String description) { mWorkspace.addToCustomContentPage(customContent, callbacks, description); @@ -2480,7 +2479,7 @@ public class Launcher extends Activity } else if (isWidgetsViewVisible()) { showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(true); + showWorkspace(true); } else if (mWorkspace.getOpenFolder() != null) { Folder openFolder = mWorkspace.getOpenFolder(); if (openFolder.isEditingName()) { @@ -2523,14 +2522,14 @@ public class Launcher extends Activity if (v instanceof Workspace) { if (mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(true); + showWorkspace(true); } return; } if (v instanceof CellLayout) { if (mWorkspace.isInOverviewMode()) { - mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v), true); + showWorkspace(mWorkspace.indexOfChild(v), true); } } @@ -3178,7 +3177,9 @@ public class Launcher extends Activity if (v instanceof Workspace) { if (!mWorkspace.isInOverviewMode()) { - if (mWorkspace.enterOverviewMode()) { + + if (!mWorkspace.isTouchActive()) { + showOverviewMode(true); mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); return true; @@ -3211,7 +3212,7 @@ public class Launcher extends Activity if (mWorkspace.isInOverviewMode()) { mWorkspace.startReordering(v); } else { - mWorkspace.enterOverviewMode(); + showOverviewMode(true); } } else { final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank( @@ -3302,17 +3303,28 @@ public class Launcher extends Activity } protected void showWorkspace(boolean animated) { - showWorkspace(animated, null); + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null, + true); + } + + protected void showWorkspace(boolean animated, Runnable onCompleteRunnable) { + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, + onCompleteRunnable, true); + } + + protected void showWorkspace(int snapToPage, boolean animated) { + showWorkspace(snapToPage, animated, null, true); } - void showWorkspace(boolean animated, Runnable onCompleteRunnable) { + void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable, + boolean notifyLauncherCallbacks) { boolean changed = mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL; if (changed) { boolean wasInSpringLoadedMode = (mState != State.WORKSPACE); mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL, - animated, onCompleteRunnable); + snapToPage, animated, onCompleteRunnable); // Show the search bar (only animate if we were showing the drop target bar in spring // loaded mode) @@ -3345,7 +3357,8 @@ public class Launcher extends Activity void showOverviewMode(boolean animated) { mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW, - animated, null /* onCompleteRunnable */); + WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, + null /* onCompleteRunnable */); mState = State.WORKSPACE; onWorkspaceShown(animated); } @@ -3419,7 +3432,8 @@ public class Launcher extends Activity } mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED, - true /* animated */, null /* onCompleteRunnable */); + WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */, + null /* onCompleteRunnable */); mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; } diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index 2ce8b1c59..a2c56a91c 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -126,7 +126,7 @@ class LauncherClings implements OnClickListener { @Override public boolean onLongClick(View v) { - mLauncher.getWorkspace().enterOverviewMode(); + mLauncher.showOverviewMode(true); dismissLongPressCling(); return true; } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 8ba5c60f3..f91cfa07b 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -23,7 +23,6 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.content.res.Resources; -import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewAnimationUtils; @@ -174,7 +173,8 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), animated, false /* hideSearchBar */, cb); + toView.getRevealView(), animated, + !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb); } /** @@ -207,8 +207,8 @@ public class LauncherStateTransitionAnimation { * Starts and animation to the workspace from the current overlay view. */ public void startAnimationToWorkspace(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { if (toWorkspaceState != Workspace.State.NORMAL && toWorkspaceState != Workspace.State.SPRING_LOADED && toWorkspaceState != Workspace.State.OVERVIEW) { @@ -216,11 +216,11 @@ public class LauncherStateTransitionAnimation { } if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { - startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, animated, - onCompleteRunnable); + startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage, + animated, onCompleteRunnable); } else { - startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, animated, - onCompleteRunnable); + startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage, + animated, onCompleteRunnable); } } @@ -249,8 +249,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( - toWorkspaceState, animated, layerViews); + Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation( + toWorkspaceState, -1, animated, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); @@ -424,8 +424,8 @@ public class LauncherStateTransitionAnimation { * Starts and animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { AppsContainerView appsView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { int[] mAllAppsToPanelDelta; @@ -477,16 +477,17 @@ public class LauncherStateTransitionAnimation { }; } }; - startAnimationToWorkspaceFromOverlay(toWorkspaceState, appsView, appsView.getContentView(), - appsView.getRevealView(), animated, onCompleteRunnable, cb); + startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView, + appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable, + cb); } /** * Starts and animation to the workspace from the widgets view. */ private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); final Resources res = mLauncher.getResources(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @@ -514,7 +515,7 @@ public class LauncherStateTransitionAnimation { }; } }; - startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, + startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView, widgetsView.getContentView(), widgetsView.getRevealView(), animated, onCompleteRunnable, cb); } @@ -523,8 +524,8 @@ public class LauncherStateTransitionAnimation { * Creates and starts a new animation to the workspace. */ private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, - final View fromView, final View contentView, final View revealView, - final boolean animated, final Runnable onCompleteRunnable, + final int toWorkspacePage, final View fromView, final View contentView, + final View revealView, final boolean animated, final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); @@ -545,8 +546,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( - toWorkspaceState, animated, layerViews); + Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation( + toWorkspaceState, toWorkspacePage, animated, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 88295c084..a4593ecb4 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1696,11 +1696,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return OVERSCROLL_DAMP_FACTOR * f; } - protected void enableFreeScroll() { + public void enableFreeScroll() { setEnableFreeScroll(true); } - protected void disableFreeScroll() { + public void disableFreeScroll() { setEnableFreeScroll(false); } diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index a8dcd0f06..af8bc7580 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -33,18 +33,16 @@ import android.widget.FrameLayout; */ public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener { - private static final int sTransitionInDuration = 200; - private static final int sTransitionOutDuration = 175; + private static final int TRANSITION_DURATION = 200; - private ObjectAnimator mDropTargetBarAnim; - private ValueAnimator mQSBSearchBarAnim; + private ObjectAnimator mShowDropTargetBarAnim; + private ValueAnimator mHideSearchBarAnim; private static final AccelerateInterpolator sAccelerateInterpolator = new AccelerateInterpolator(); private boolean mIsSearchBarHidden; private View mQSBSearchBar; private View mDropTargetBar; - private int mBarHeight; private boolean mDeferOnDragEnd = false; // Drop targets @@ -52,8 +50,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D private ButtonDropTarget mDeleteDropTarget; private ButtonDropTarget mUninstallDropTarget; - private boolean mEnableDropDownDropTargets; - public SearchDropTargetBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -82,17 +78,12 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D public void setQsbSearchBar(View qsb) { mQSBSearchBar = qsb; if (mQSBSearchBar != null) { - if (mEnableDropDownDropTargets) { - mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "translationY", 0, - -mBarHeight); - } else { - mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f); - } - setupAnimation(mQSBSearchBarAnim, mQSBSearchBar); + mHideSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f); + setupAnimation(mHideSearchBarAnim, mQSBSearchBar); } else { // Create a no-op animation of the search bar is null - mQSBSearchBarAnim = ValueAnimator.ofFloat(0, 0); - mQSBSearchBarAnim.setDuration(sTransitionInDuration); + mHideSearchBarAnim = ValueAnimator.ofFloat(0, 0); + mHideSearchBarAnim.setDuration(TRANSITION_DURATION); } } @@ -106,7 +97,7 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D private void setupAnimation(ValueAnimator anim, final View v) { anim.setInterpolator(sAccelerateInterpolator); - anim.setDuration(sTransitionInDuration); + anim.setDuration(TRANSITION_DURATION); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -131,76 +122,74 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D mDeleteDropTarget.setSearchDropTargetBar(this); mUninstallDropTarget.setSearchDropTargetBar(this); - mEnableDropDownDropTargets = - getResources().getBoolean(R.bool.config_useDropTargetDownTransition); - // Create the various fade animations - if (mEnableDropDownDropTargets) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mBarHeight = grid.searchBarSpaceHeightPx; - mDropTargetBar.setTranslationY(-mBarHeight); - mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "translationY", - -mBarHeight, 0f); - - } else { - mDropTargetBar.setAlpha(0f); - mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f); - } - setupAnimation(mDropTargetBarAnim, mDropTargetBar); + mDropTargetBar.setAlpha(0f); + mShowDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f); + setupAnimation(mShowDropTargetBarAnim, mDropTargetBar); } + /** + * Finishes all the animations on the search and drop target bars. + */ public void finishAnimations() { prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.reverse(); + mShowDropTargetBarAnim.reverse(); prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); + mHideSearchBarAnim.reverse(); } - /* - * Shows and hides the search bar. + /** + * Shows the search bar. */ public void showSearchBar(boolean animated) { - boolean needToCancelOngoingAnimation = mQSBSearchBarAnim.isRunning() && !animated; - if (!mIsSearchBarHidden && !needToCancelOngoingAnimation) return; + if (!mIsSearchBarHidden) return; if (animated) { prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); + mHideSearchBarAnim.reverse(); } else { - mQSBSearchBarAnim.cancel(); - if (mQSBSearchBar != null && mEnableDropDownDropTargets) { - mQSBSearchBar.setTranslationY(0); - } else if (mQSBSearchBar != null) { + mHideSearchBarAnim.cancel(); + if (mQSBSearchBar != null) { mQSBSearchBar.setAlpha(1f); } } mIsSearchBarHidden = false; } + + /** + * Hides the search bar. We only use this for clings. + */ public void hideSearchBar(boolean animated) { - boolean needToCancelOngoingAnimation = mQSBSearchBarAnim.isRunning() && !animated; - if (mIsSearchBarHidden && !needToCancelOngoingAnimation) return; + if (mIsSearchBarHidden) return; if (animated) { prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.start(); + mHideSearchBarAnim.start(); } else { - mQSBSearchBarAnim.cancel(); - if (mQSBSearchBar != null && mEnableDropDownDropTargets) { - mQSBSearchBar.setTranslationY(-mBarHeight); - } else if (mQSBSearchBar != null) { + mHideSearchBarAnim.cancel(); + if (mQSBSearchBar != null) { mQSBSearchBar.setAlpha(0f); } } mIsSearchBarHidden = true; } - /* - * Gets various transition durations. + /** + * Shows the drop target bar. */ - public int getTransitionInDuration() { - return sTransitionInDuration; + public void showDeleteTarget() { + // Animate out the QSB search bar, and animate in the drop target bar + prepareStartAnimation(mDropTargetBar); + mShowDropTargetBarAnim.start(); + hideSearchBar(true); } - public int getTransitionOutDuration() { - return sTransitionOutDuration; + + /** + * Hides the drop target bar. + */ + public void hideDeleteTarget() { + // Restore the QSB search bar, and animate out the drop target bar + prepareStartAnimation(mDropTargetBar); + mShowDropTargetBarAnim.reverse(); + showSearchBar(true); } /* @@ -211,26 +200,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D showDeleteTarget(); } - public void showDeleteTarget() { - // Animate out the QSB search bar, and animate in the drop target bar - prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.start(); - if (!mIsSearchBarHidden) { - prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.start(); - } - } - - public void hideDeleteTarget() { - // Restore the QSB search bar, and animate out the drop target bar - prepareStartAnimation(mDropTargetBar); - mDropTargetBarAnim.reverse(); - if (!mIsSearchBarHidden) { - prepareStartAnimation(mQSBSearchBar); - mQSBSearchBarAnim.reverse(); - } - } - public void deferOnDragEnd() { mDeferOnDragEnd = true; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6b03e3100..9e680fb82 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -17,15 +17,10 @@ package com.android.launcher3; import android.animation.Animator; -import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; import android.app.WallpaperManager; import android.appwidget.AppWidgetHostView; @@ -99,12 +94,9 @@ public class Workspace extends SmoothPagedView protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; protected static final int FADE_EMPTY_SCREEN_DURATION = 150; - private static final int BACKGROUND_FADE_OUT_DURATION = 350; private static final int ADJACENT_SCREEN_DROP_DURATION = 300; private static final int FLING_THRESHOLD_VELOCITY = 500; - private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; - static final boolean MAP_NO_RECURSE = false; static final boolean MAP_RECURSE = true; @@ -113,10 +105,6 @@ public class Workspace extends SmoothPagedView private ObjectAnimator mChildrenOutlineFadeOutAnimation; private float mChildrenOutlineAlpha = 0; - // These properties refer to the background protection gradient used for AllApps and Customize - private ValueAnimator mBackgroundFadeInAnimation; - private ValueAnimator mBackgroundFadeOutAnimation; - private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; private long mTouchDownTime = -1; private long mCustomContentShowTime = -1; @@ -129,7 +117,6 @@ public class Workspace extends SmoothPagedView private int mDefaultPage; private ShortcutAndWidgetContainer mDragSourceInternal; - @Thunk static boolean sAccessibilityEnabled; // The screen id used for the empty screen always present to the right. final static long EXTRA_EMPTY_SCREEN_ID = -201; @@ -272,14 +259,7 @@ public class Workspace extends SmoothPagedView private float mSavedTranslationX; private float mCurrentScale; - private float mNewScale; - @Thunk float[] mOldBackgroundAlphas; - private float[] mOldAlphas; - @Thunk float[] mNewBackgroundAlphas; - private float[] mNewAlphas; - private int mLastChildCount = -1; private float mTransitionProgress; - @Thunk Animator mStateAnimator = null; float mOverScrollEffect = 0f; @@ -294,6 +274,9 @@ public class Workspace extends SmoothPagedView boolean mShouldSendPageSettled; int mLastOverlaySroll = 0; + // Handles workspace state transitions + private WorkspaceStateTransitionAnimation mStateTransitionAnimation; + private final Runnable mBindPages = new Runnable() { @Override public void run() { @@ -329,6 +312,7 @@ public class Workspace extends SmoothPagedView setDataIsReady(); mLauncher = (Launcher) context; + mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); final Resources res = getResources(); mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid(). getDeviceProfile().shouldFadeAdjacentWorkspaceScreens(); @@ -1583,37 +1567,6 @@ public class Workspace extends SmoothPagedView return mChildrenOutlineAlpha; } - private void animateBackgroundGradient(float finalAlpha, boolean animated) { - final DragLayer dragLayer = mLauncher.getDragLayer(); - - if (mBackgroundFadeInAnimation != null) { - mBackgroundFadeInAnimation.cancel(); - mBackgroundFadeInAnimation = null; - } - if (mBackgroundFadeOutAnimation != null) { - mBackgroundFadeOutAnimation.cancel(); - mBackgroundFadeOutAnimation = null; - } - float startAlpha = dragLayer.getBackgroundAlpha(); - if (finalAlpha != startAlpha) { - if (animated) { - mBackgroundFadeOutAnimation = - LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha); - mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() { - public void onAnimationUpdate(ValueAnimator animation) { - dragLayer.setBackgroundAlpha( - ((Float)animation.getAnimatedValue()).floatValue()); - } - }); - mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); - mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); - mBackgroundFadeOutAnimation.start(); - } else { - dragLayer.setBackgroundAlpha(finalAlpha); - } - } - } - float backgroundAlphaInterpolator(float r) { float pivotA = 0.1f; float pivotB = 0.4f; @@ -1737,7 +1690,7 @@ public class Workspace extends SmoothPagedView OnClickListener listener = new OnClickListener() { @Override public void onClick(View arg0) { - enterOverviewMode(); + mLauncher.showOverviewMode(true); } }; return listener; @@ -1797,9 +1750,6 @@ public class Workspace extends SmoothPagedView getPageIndicator().setOnClickListener(listener); } } - AccessibilityManager am = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - sAccessibilityEnabled = am.isEnabled(); // Update wallpaper dimensions if they were changed since last onResume // (we also always set the wallpaper dimensions in the constructor) @@ -1969,64 +1919,6 @@ public class Workspace extends SmoothPagedView position[0], position[1], 0, null); } - /* - * This interpolator emulates the rate at which the perceived scale of an object changes - * as its distance from a camera increases. When this interpolator is applied to a scale - * animation on a view, it evokes the sense that the object is shrinking due to moving away - * from the camera. - */ - static class ZInterpolator implements TimeInterpolator { - private float focalLength; - - public ZInterpolator(float foc) { - focalLength = foc; - } - - public float getInterpolation(float input) { - return (1.0f - focalLength / (focalLength + input)) / - (1.0f - focalLength / (focalLength + 1.0f)); - } - } - - /* - * The exact reverse of ZInterpolator. - */ - static class InverseZInterpolator implements TimeInterpolator { - private ZInterpolator zInterpolator; - public InverseZInterpolator(float foc) { - zInterpolator = new ZInterpolator(foc); - } - public float getInterpolation(float input) { - return 1 - zInterpolator.getInterpolation(1 - input); - } - } - - /* - * ZInterpolator compounded with an ease-out. - */ - static class ZoomOutInterpolator implements TimeInterpolator { - private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f); - private final ZInterpolator zInterpolator = new ZInterpolator(0.13f); - - public float getInterpolation(float input) { - return decelerate.getInterpolation(zInterpolator.getInterpolation(input)); - } - } - - /* - * InvereZInterpolator compounded with an ease-out. - */ - static class ZoomInInterpolator implements TimeInterpolator { - private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); - private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); - - public float getInterpolation(float input) { - return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); - } - } - - private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); - /* * * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we @@ -2090,21 +1982,6 @@ public class Workspace extends SmoothPagedView dragLayer.clearAllResizeFrames(); } - private void initAnimationArrays() { - final int childCount = getChildCount(); - if (mLastChildCount == childCount) return; - - mOldBackgroundAlphas = new float[childCount]; - mOldAlphas = new float[childCount]; - mNewBackgroundAlphas = new float[childCount]; - mNewAlphas = new float[childCount]; - } - - Animator getChangeStateAnimation(final State state, boolean animated, - HashMap layerViews) { - return getChangeStateAnimation(state, animated, 0, -1, layerViews); - } - @Override protected void getFreeScrollPageRange(int[] range) { getOverviewModePages(range); @@ -2151,41 +2028,6 @@ public class Workspace extends SmoothPagedView return mState == State.OVERVIEW; } - public boolean enterOverviewMode() { - if (mTouchState != TOUCH_STATE_REST) { - return false; - } - enableOverviewMode(true, -1, true); - return true; - } - - public void exitOverviewMode(boolean animated) { - exitOverviewMode(-1, animated); - } - - public void exitOverviewMode(int snapPage, boolean animated) { - enableOverviewMode(false, snapPage, animated); - } - - private void enableOverviewMode(boolean enable, int snapPage, boolean animated) { - State finalState = Workspace.State.OVERVIEW; - if (!enable) { - finalState = Workspace.State.NORMAL; - } - - Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage); - if (workspaceAnim != null) { - onTransitionPrepare(); - workspaceAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator arg0) { - onTransitionEnd(); - } - }); - workspaceAnim.start(); - } - } - int getOverviewModeTranslationY() { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -2208,10 +2050,22 @@ public class Workspace extends SmoothPagedView } } - private void setState(State state) { - mState = state; + /** + * Sets the current workspace {@link State}, returning an animation transitioning the workspace + * to that new state. + */ + public Animator setStateWithAnimation(State toState, int toPage, boolean animated, + HashMap layerViews) { + // Create the animation to the new state + Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(getState(), + toState, toPage, animated, layerViews); + + // Update the current state + mState = toState; updateInteractionForState(); updateAccessibilityFlags(); + + return workspaceAnim; } State getState() { @@ -2225,321 +2079,15 @@ public class Workspace extends SmoothPagedView setImportantForAccessibility(accessible); } - private static final int HIDE_WORKSPACE_DURATION = 100; - - Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) { - return getChangeStateAnimation(state, animated, delay, snapPage, null); - } - - Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage, - HashMap layerViews) { - if (mState == state) { - return null; - } - - // Initialize animation arrays for the first time if necessary - initAnimationArrays(); - - AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null; - - // We only want a single instance of a workspace animation to be running at once, so - // we cancel any incomplete transition. - if (mStateAnimator != null) { - mStateAnimator.cancel(); - } - mStateAnimator = anim; - - final State oldState = mState; - final boolean oldStateIsNormal = (oldState == State.NORMAL); - final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED); - final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN); - final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN); - final boolean oldStateIsOverview = (oldState == State.OVERVIEW); - setState(state); - final boolean stateIsNormal = (state == State.NORMAL); - final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED); - final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN); - final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN); - final boolean stateIsOverview = (state == State.OVERVIEW); - float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; - float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; - float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; - // We keep the search bar visible on the workspace and in AllApps now - boolean showSearchBar = stateIsNormal || - (mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden); - float finalSearchBarAlpha = showSearchBar ? 1f : 0f; - float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? - getOverviewModeTranslationY() : 0; - - boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); - boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); - boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); - boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); - boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); - - mNewScale = 1.0f; - - if (oldStateIsOverview) { - disableFreeScroll(); - } else if (stateIsOverview) { - enableFreeScroll(); - } - - if (state != State.NORMAL) { - if (stateIsSpringLoaded) { - mNewScale = mSpringLoadedShrinkFactor; - } else if (stateIsOverview || stateIsOverviewHidden) { - mNewScale = mOverviewModeShrinkFactor; - } - } - - final int duration; - if (workspaceToAllApps || overviewToAllApps) { - duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime); - } else if (workspaceToOverview || overviewToWorkspace) { - duration = getResources().getInteger(R.integer.config_overviewTransitionTime); - } else { - duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime); - } - - if (snapPage == -1) { - snapPage = getPageNearestToCenterOfScreen(); - } - snapToPage(snapPage, duration, mZoomInInterpolator); - - for (int i = 0; i < getChildCount(); i++) { - final CellLayout cl = (CellLayout) getChildAt(i); - boolean isCurrentPage = (i == snapPage); - float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); - float finalAlpha; - if (stateIsNormalHidden || stateIsOverviewHidden) { - finalAlpha = 0f; - } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) { - finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f; - } else { - finalAlpha = 1f; - } - - // If we are animating to/from the small state, then hide the side pages and fade the - // current page in - if (!mIsSwitchingState) { - if (workspaceToAllApps || allAppsToWorkspace) { - if (allAppsToWorkspace && isCurrentPage) { - initialAlpha = 0f; - } else if (!isCurrentPage) { - initialAlpha = finalAlpha = 0f; - } - cl.setShortcutAndWidgetAlpha(initialAlpha); - } - } - - mOldAlphas[i] = initialAlpha; - mNewAlphas[i] = finalAlpha; - if (animated) { - mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); - mNewBackgroundAlphas[i] = finalBackgroundAlpha; - } else { - cl.setBackgroundAlpha(finalBackgroundAlpha); - cl.setShortcutAndWidgetAlpha(finalAlpha); - } - } - - final View searchBar = mLauncher.getOrCreateQsbBar(); - final View overviewPanel = mLauncher.getOverviewPanel(); - final View hotseat = mLauncher.getHotseat(); - final View pageIndicator = getPageIndicator(); - if (animated) { - LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this); - scale.scaleX(mNewScale) - .scaleY(mNewScale) - .translationY(finalWorkspaceTranslationY) - .setDuration(duration) - .setInterpolator(mZoomInInterpolator); - anim.play(scale); - for (int index = 0; index < getChildCount(); index++) { - final int i = index; - final CellLayout cl = (CellLayout) getChildAt(i); - float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); - if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { - cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); - cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); - } else { - if (layerViews != null) { - layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); - } - if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { - LauncherViewPropertyAnimator alphaAnim = - new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); - alphaAnim.alpha(mNewAlphas[i]) - .setDuration(duration) - .setInterpolator(mZoomInInterpolator); - anim.play(alphaAnim); - } - if (mOldBackgroundAlphas[i] != 0 || - mNewBackgroundAlphas[i] != 0) { - ValueAnimator bgAnim = - LauncherAnimUtils.ofFloat(cl, 0f, 1f); - bgAnim.setInterpolator(mZoomInInterpolator); - bgAnim.setDuration(duration); - bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { - public void onAnimationUpdate(float a, float b) { - cl.setBackgroundAlpha( - a * mOldBackgroundAlphas[i] + - b * mNewBackgroundAlphas[i]); - } - }); - anim.play(bgAnim); - } - } - } - Animator pageIndicatorAlpha = null; - if (pageIndicator != null) { - pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) - .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); - pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator)); - } else { - // create a dummy animation so we don't need to do null checks later - pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0); - } - - LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) - .alpha(finalHotseatAndPageIndicatorAlpha); - hotseatAlpha.addListener(new AlphaUpdateListener(hotseat)); - - LauncherViewPropertyAnimator overviewPanelAlpha = - new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha); - overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel)); - - // For animation optimations, we may need to provide the Launcher transition - // with a set of views on which to force build layers in certain scenarios. - hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null); - overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (layerViews != null) { - // If layerViews is not null, we add these views, and indicate that - // the caller can manage layer state. - layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - } else { - // Otherwise let the animator handle layer management. - hotseatAlpha.withLayer(); - overviewPanelAlpha.withLayer(); - } - - if (workspaceToOverview) { - pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2)); - hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); - overviewPanelAlpha.setInterpolator(null); - } else if (overviewToWorkspace) { - pageIndicatorAlpha.setInterpolator(null); - hotseatAlpha.setInterpolator(null); - overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); - } - - overviewPanelAlpha.setDuration(duration); - pageIndicatorAlpha.setDuration(duration); - hotseatAlpha.setDuration(duration); - - if (searchBar != null) { - LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) - .alpha(finalSearchBarAlpha); - searchBarAlpha.addListener(new AlphaUpdateListener(searchBar)); - searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (layerViews != null) { - // If layerViews is not null, we add these views, and indicate that - // the caller can manage layer state. - layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - } else { - // Otherwise let the animator handle layer management. - searchBarAlpha.withLayer(); - } - searchBarAlpha.setDuration(duration); - anim.play(searchBarAlpha); - } - - anim.play(overviewPanelAlpha); - anim.play(hotseatAlpha); - anim.play(pageIndicatorAlpha); - anim.setStartDelay(delay); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mStateAnimator = null; - } - }); - } else { - overviewPanel.setAlpha(finalOverviewPanelAlpha); - AlphaUpdateListener.updateVisibility(overviewPanel); - hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha); - AlphaUpdateListener.updateVisibility(hotseat); - if (pageIndicator != null) { - pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); - AlphaUpdateListener.updateVisibility(pageIndicator); - } - if (searchBar != null) { - searchBar.setAlpha(finalSearchBarAlpha); - AlphaUpdateListener.updateVisibility(searchBar); - } - updateCustomContentVisibility(); - setScaleX(mNewScale); - setScaleY(mNewScale); - setTranslationY(finalWorkspaceTranslationY); - } - - if (stateIsNormal) { - animateBackgroundGradient(0f, animated); - } else { - animateBackgroundGradient(getResources().getInteger( - R.integer.config_workspaceScrimAlpha) / 100f, animated); - } - return anim; - } - - static class AlphaUpdateListener implements AnimatorUpdateListener, AnimatorListener { - View view; - public AlphaUpdateListener(View v) { - view = v; - } - - @Override - public void onAnimationUpdate(ValueAnimator arg0) { - updateVisibility(view); - } - - public static void updateVisibility(View view) { - // We want to avoid the extra layout pass by setting the views to GONE unless - // accessibility is on, in which case not setting them to GONE causes a glitch. - int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE; - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { - view.setVisibility(invisibleState); - } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD - && view.getVisibility() != VISIBLE) { - view.setVisibility(VISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator arg0) { - } - - @Override - public void onAnimationEnd(Animator arg0) { - updateVisibility(view); - } - - @Override - public void onAnimationRepeat(Animator arg0) { - } - - @Override - public void onAnimationStart(Animator arg0) { - // We want the views to be visible for animation, so fade-in/out is visible - view.setVisibility(VISIBLE); - } - } - @Override public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - onTransitionPrepare(); + mIsSwitchingState = true; + + // Invalidate here to ensure that the pages are rendered during the state change transition. + invalidate(); + + updateChildrenLayersEnabled(false); + hideCustomContentIfNecessary(); } @Override @@ -2553,17 +2101,9 @@ public class Workspace extends SmoothPagedView @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - onTransitionEnd(); - } - - private void onTransitionPrepare() { - mIsSwitchingState = true; - - // Invalidate here to ensure that the pages are rendered during the state change transition. - invalidate(); - + mIsSwitchingState = false; updateChildrenLayersEnabled(false); - hideCustomContentIfNecessary(); + showCustomContentIfNecessary(); } void updateCustomContentVisibility() { @@ -2589,12 +2129,6 @@ public class Workspace extends SmoothPagedView } } - @Thunk void onTransitionEnd() { - mIsSwitchingState = false; - updateChildrenLayersEnabled(false); - showCustomContentIfNecessary(); - } - @Override public View getContent() { return this; @@ -3106,7 +2640,7 @@ public class Workspace extends SmoothPagedView } } - int snapScreen = -1; + int snapScreen = WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE; boolean resizeOnDrop = false; if (d.dragSource != this) { final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], @@ -3267,7 +2801,9 @@ public class Workspace extends SmoothPagedView animateWidgetDrop(info, parent, d.dragView, onCompleteRunnable, animationType, cell, false); } else { - int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; + int duration = snapScreen < 0 ? + WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE : + ADJACENT_SCREEN_DROP_DURATION; mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, onCompleteRunnable, this); } @@ -4165,8 +3701,8 @@ public class Workspace extends SmoothPagedView public void setFinalTransitionTransform(CellLayout layout) { if (isSwitchingState()) { mCurrentScale = getScaleX(); - setScaleX(mNewScale); - setScaleY(mNewScale); + setScaleX(mStateTransitionAnimation.getFinalScale()); + setScaleY(mStateTransitionAnimation.getFinalScale()); } } public void resetTransitionTransform(CellLayout layout) { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java new file mode 100644 index 000000000..a0cedeb63 --- /dev/null +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -0,0 +1,519 @@ +/* + * 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.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.util.Thunk; + +import java.util.HashMap; + +/** + * A convenience class to update a view's visibility state after an alpha animation. + */ +class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener { + private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; + + private View mView; + private boolean mAccessibilityEnabled; + + public AlphaUpdateListener(View v, boolean accessibilityEnabled) { + mView = v; + mAccessibilityEnabled = accessibilityEnabled; + } + + @Override + public void onAnimationUpdate(ValueAnimator arg0) { + updateVisibility(mView, mAccessibilityEnabled); + } + + public static void updateVisibility(View view, boolean accessibilityEnabled) { + // We want to avoid the extra layout pass by setting the views to GONE unless + // accessibility is on, in which case not setting them to GONE causes a glitch. + int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE; + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { + view.setVisibility(invisibleState); + } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD + && view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationEnd(Animator arg0) { + updateVisibility(mView, mAccessibilityEnabled); + } + + @Override + public void onAnimationStart(Animator arg0) { + // We want the views to be visible for animation, so fade-in/out is visible + mView.setVisibility(View.VISIBLE); + } +} + +/** + * This interpolator emulates the rate at which the perceived scale of an object changes + * as its distance from a camera increases. When this interpolator is applied to a scale + * animation on a view, it evokes the sense that the object is shrinking due to moving away + * from the camera. + */ +class ZInterpolator implements TimeInterpolator { + private float focalLength; + + public ZInterpolator(float foc) { + focalLength = foc; + } + + public float getInterpolation(float input) { + return (1.0f - focalLength / (focalLength + input)) / + (1.0f - focalLength / (focalLength + 1.0f)); + } +} + +/** + * The exact reverse of ZInterpolator. + */ +class InverseZInterpolator implements TimeInterpolator { + private ZInterpolator zInterpolator; + public InverseZInterpolator(float foc) { + zInterpolator = new ZInterpolator(foc); + } + public float getInterpolation(float input) { + return 1 - zInterpolator.getInterpolation(1 - input); + } +} + +/** + * InverseZInterpolator compounded with an ease-out. + */ +class ZoomInInterpolator implements TimeInterpolator { + private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); + private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); + + public float getInterpolation(float input) { + return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); + } +} + +/** + * Manages the animations between each of the workspace states. + */ +public class WorkspaceStateTransitionAnimation { + + public static final String TAG = "WorkspaceStateTransitionAnimation"; + + public static final int SCROLL_TO_CURRENT_PAGE = -1; + @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350; + + final @Thunk Launcher mLauncher; + final @Thunk Workspace mWorkspace; + + @Thunk AnimatorSet mStateAnimator; + @Thunk float[] mOldBackgroundAlphas; + @Thunk float[] mOldAlphas; + @Thunk float[] mNewBackgroundAlphas; + @Thunk float[] mNewAlphas; + @Thunk int mLastChildCount = -1; + + @Thunk float mCurrentScale; + @Thunk float mNewScale; + + @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); + + // These properties refer to the background protection gradient used for AllApps and Customize + @Thunk ValueAnimator mBackgroundFadeInAnimation; + @Thunk ValueAnimator mBackgroundFadeOutAnimation; + + @Thunk float mSpringLoadedShrinkFactor; + @Thunk float mOverviewModeShrinkFactor; + @Thunk float mWorkspaceScrimAlpha; + @Thunk int mAllAppsTransitionTime; + @Thunk int mOverviewTransitionTime; + @Thunk int mOverlayTransitionTime; + @Thunk boolean mWorkspaceFadeInAdjacentScreens; + + public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) { + mLauncher = launcher; + mWorkspace = workspace; + + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + Resources res = launcher.getResources(); + mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime); + mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); + mOverlayTransitionTime = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime); + mSpringLoadedShrinkFactor = + res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; + mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; + mOverviewModeShrinkFactor = grid.getOverviewModeScale(); + mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); + } + + public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, + int toPage, boolean animated, + HashMap layerViews) { + getAnimation(fromState, toState, toPage, animated, layerViews); + return mStateAnimator; + } + + public float getFinalScale() { + return mNewScale; + } + + /** + * Starts a transition animation for the workspace. + */ + private void getAnimation(final Workspace.State fromState, final Workspace.State toState, + int toPage, final boolean animated, + final HashMap layerViews) { + AccessibilityManager am = (AccessibilityManager) + mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); + boolean accessibilityEnabled = am.isEnabled(); + + // Reinitialize animation arrays for the current workspace state + reinitializeAnimationArrays(); + + // Cancel existing workspace animations and create a new animator set if requested + cancelAnimation(); + if (animated) { + mStateAnimator = LauncherAnimUtils.createAnimatorSet(); + } + + // Update the workspace state + final boolean oldStateIsNormal = (fromState == Workspace.State.NORMAL); + final boolean oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED); + final boolean oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN); + final boolean oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN); + final boolean oldStateIsOverview = (fromState == Workspace.State.OVERVIEW); + + final boolean stateIsNormal = (toState == Workspace.State.NORMAL); + final boolean stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED); + final boolean stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN); + final boolean stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN); + final boolean stateIsOverview = (toState == Workspace.State.OVERVIEW); + + final boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); + final boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); + final boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); + final boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); + final boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); + + float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; + float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; + float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; + // We keep the search bar visible on the workspace and in AllApps now + boolean showSearchBar = stateIsNormal || + (mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden); + float finalSearchBarAlpha = showSearchBar ? 1f : 0f; + float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? + mWorkspace.getOverviewModeTranslationY() : 0; + + final int childCount = mWorkspace.getChildCount(); + final int customPageCount = mWorkspace.numCustomPages(); + + mNewScale = 1.0f; + + if (oldStateIsOverview) { + mWorkspace.disableFreeScroll(); + } else if (stateIsOverview) { + mWorkspace.enableFreeScroll(); + } + + if (!stateIsNormal) { + if (stateIsSpringLoaded) { + mNewScale = mSpringLoadedShrinkFactor; + } else if (stateIsOverview || stateIsOverviewHidden) { + mNewScale = mOverviewModeShrinkFactor; + } + } + + final int duration; + if (workspaceToAllApps || overviewToAllApps) { + duration = mAllAppsTransitionTime; + } else if (workspaceToOverview || overviewToWorkspace) { + duration = mOverviewTransitionTime; + } else { + duration = mOverlayTransitionTime; + } + + if (toPage == SCROLL_TO_CURRENT_PAGE) { + toPage = mWorkspace.getPageNearestToCenterOfScreen(); + } + mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator); + + for (int i = 0; i < childCount; i++) { + final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); + boolean isCurrentPage = (i == toPage); + float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); + float finalAlpha; + if (stateIsNormalHidden || stateIsOverviewHidden) { + finalAlpha = 0f; + } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) { + finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; + } else { + finalAlpha = 1f; + } + + // If we are animating to/from the small state, then hide the side pages and fade the + // current page in + if (!mWorkspace.isSwitchingState()) { + if (workspaceToAllApps || allAppsToWorkspace) { + if (allAppsToWorkspace && isCurrentPage) { + initialAlpha = 0f; + } else if (!isCurrentPage) { + initialAlpha = finalAlpha = 0f; + } + cl.setShortcutAndWidgetAlpha(initialAlpha); + } + } + + mOldAlphas[i] = initialAlpha; + mNewAlphas[i] = finalAlpha; + if (animated) { + mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); + mNewBackgroundAlphas[i] = finalBackgroundAlpha; + } else { + cl.setBackgroundAlpha(finalBackgroundAlpha); + cl.setShortcutAndWidgetAlpha(finalAlpha); + } + } + + final View searchBar = mLauncher.getOrCreateQsbBar(); + final View overviewPanel = mLauncher.getOverviewPanel(); + final View hotseat = mLauncher.getHotseat(); + final View pageIndicator = mWorkspace.getPageIndicator(); + if (animated) { + LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mWorkspace); + scale.scaleX(mNewScale) + .scaleY(mNewScale) + .translationY(finalWorkspaceTranslationY) + .setDuration(duration) + .setInterpolator(mZoomInInterpolator); + mStateAnimator.play(scale); + for (int index = 0; index < childCount; index++) { + final int i = index; + final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); + float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); + if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { + cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); + cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); + } else { + if (layerViews != null) { + layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); + } + if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { + LauncherViewPropertyAnimator alphaAnim = + new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); + alphaAnim.alpha(mNewAlphas[i]) + .setDuration(duration) + .setInterpolator(mZoomInInterpolator); + mStateAnimator.play(alphaAnim); + } + if (mOldBackgroundAlphas[i] != 0 || + mNewBackgroundAlphas[i] != 0) { + ValueAnimator bgAnim = + LauncherAnimUtils.ofFloat(cl, 0f, 1f); + bgAnim.setInterpolator(mZoomInInterpolator); + bgAnim.setDuration(duration); + bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { + public void onAnimationUpdate(float a, float b) { + cl.setBackgroundAlpha( + a * mOldBackgroundAlphas[i] + + b * mNewBackgroundAlphas[i]); + } + }); + mStateAnimator.play(bgAnim); + } + } + } + Animator pageIndicatorAlpha = null; + if (pageIndicator != null) { + pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) + .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); + pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator, + accessibilityEnabled)); + } else { + // create a dummy animation so we don't need to do null checks later + pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0); + } + + LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat) + .alpha(finalHotseatAndPageIndicatorAlpha); + hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled)); + + LauncherViewPropertyAnimator overviewPanelAlpha = + new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha); + overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel, + accessibilityEnabled)); + + // For animation optimations, we may need to provide the Launcher transition + // with a set of views on which to force build layers in certain scenarios. + hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null); + overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (layerViews != null) { + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + hotseatAlpha.withLayer(); + overviewPanelAlpha.withLayer(); + } + + if (workspaceToOverview) { + pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2)); + hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); + overviewPanelAlpha.setInterpolator(null); + } else if (overviewToWorkspace) { + pageIndicatorAlpha.setInterpolator(null); + hotseatAlpha.setInterpolator(null); + overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); + } + + overviewPanelAlpha.setDuration(duration); + pageIndicatorAlpha.setDuration(duration); + hotseatAlpha.setDuration(duration); + + // TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the + // bar has no idea that it is hidden, and this has no idea what state the bar is + // actually in. + if (searchBar != null) { + LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) + .alpha(finalSearchBarAlpha); + searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, accessibilityEnabled)); + searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (layerViews != null) { + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + searchBarAlpha.withLayer(); + } + searchBarAlpha.setDuration(duration); + mStateAnimator.play(searchBarAlpha); + } + + mStateAnimator.play(overviewPanelAlpha); + mStateAnimator.play(hotseatAlpha); + mStateAnimator.play(pageIndicatorAlpha); + mStateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStateAnimator = null; + } + }); + } else { + overviewPanel.setAlpha(finalOverviewPanelAlpha); + AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled); + hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha); + AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled); + if (pageIndicator != null) { + pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); + AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled); + } + if (searchBar != null) { + searchBar.setAlpha(finalSearchBarAlpha); + AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); + } + mWorkspace.updateCustomContentVisibility(); + mWorkspace.setScaleX(mNewScale); + mWorkspace.setScaleY(mNewScale); + mWorkspace.setTranslationY(finalWorkspaceTranslationY); + } + + if (stateIsNormal) { + animateBackgroundGradient(0f, animated); + } else { + animateBackgroundGradient(mWorkspaceScrimAlpha, animated); + } + } + + /** + * Reinitializes the arrays that we need for the animations on each page. + */ + private void reinitializeAnimationArrays() { + final int childCount = mWorkspace.getChildCount(); + if (mLastChildCount == childCount) return; + + mOldBackgroundAlphas = new float[childCount]; + mOldAlphas = new float[childCount]; + mNewBackgroundAlphas = new float[childCount]; + mNewAlphas = new float[childCount]; + } + + /** + * Animates the background scrim. + * TODO(winsonc): Is there a better place for this? + * + * @param finalAlpha the final alpha for the background scrim + * @param animated whether or not to set the background alpha immediately + */ + private void animateBackgroundGradient(float finalAlpha, boolean animated) { + // Cancel any running background animations + cancelAnimator(mBackgroundFadeInAnimation); + cancelAnimator(mBackgroundFadeOutAnimation); + + final DragLayer dragLayer = mLauncher.getDragLayer(); + final float startAlpha = dragLayer.getBackgroundAlpha(); + if (finalAlpha != startAlpha) { + if (animated) { + mBackgroundFadeOutAnimation = + LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha); + mBackgroundFadeOutAnimation.addUpdateListener( + new ValueAnimator.AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + dragLayer.setBackgroundAlpha( + ((Float)animation.getAnimatedValue()).floatValue()); + } + }); + mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); + mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); + mBackgroundFadeOutAnimation.start(); + } else { + dragLayer.setBackgroundAlpha(finalAlpha); + } + } + } + + /** + * Cancels the current animation. + */ + private void cancelAnimation() { + cancelAnimator(mStateAnimator); + mStateAnimator = null; + } + + /** + * Cancels the specified animation. + */ + private void cancelAnimator(Animator animator) { + if (animator != null) { + animator.setDuration(0); + animator.cancel(); + } + } +} \ No newline at end of file -- cgit v1.2.3 From d4af1484447129d489203130e0d654f81a10437d Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 20 Apr 2015 20:40:03 -0700 Subject: Remove duplicate calls to getSortedWidgetsAndShortcuts Note: this is a manual CP of the CL that was submitted to ub-launcher3-almonte b/19904873 Change-Id: I2e56bc0c7524caa2ef3c5fb00f35cb032cd1860b --- .../android/launcher3/LauncherAppWidgetHost.java | 6 +--- src/com/android/launcher3/LauncherModel.java | 36 ++++++++++++++-------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 583f85ad0..e32e0d9fe 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -79,11 +79,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } protected void onProvidersChanged() { - // Once we get the message that widget packages are updated, we need to rebind items - // in AppsCustomize accordingly. - mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher, - true /* refresh */)); - + mLauncher.getModel().loadAndBindWidgetsAndShortcuts(mLauncher, mLauncher); if (!mProviderChangeListeners.isEmpty()) { for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) { callback.run(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f7df6bc1a..424f649fb 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -38,6 +38,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Rect; import android.net.Uri; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -3219,19 +3220,9 @@ public class LauncherModel extends BroadcastReceiver } }); } - - final ArrayList widgetsAndShortcuts = - getSortedWidgetsAndShortcuts(context, true /* refresh */); - mHandler.post(new Runnable() { - @Override - public void run() { - Callbacks cb = getCallback(); - if (callbacks == cb && cb != null) { - callbacks.bindPackagesUpdated(widgetsAndShortcuts); - } - } - }); - + if (Build.VERSION.SDK_INT < 17) { + loadAndBindWidgetsAndShortcuts(context, callbacks); + } // Write all the logs to disk mHandler.post(new Runnable() { public void run() { @@ -3280,6 +3271,25 @@ public class LauncherModel extends BroadcastReceiver } } + public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks) { + runOnWorkerThread(new Runnable(){ + @Override + public void run() { + final ArrayList list = + getSortedWidgetsAndShortcuts(context, true /* refresh */); + mHandler.post(new Runnable() { + @Override + public void run() { + Callbacks cb = getCallback(); + if (callbacks == cb && cb != null) { + callbacks.bindPackagesUpdated(list); + } + } + }); + } + }); + } + // Returns a list of ResolveInfos/AppWidgetInfos in sorted order public static ArrayList getSortedWidgetsAndShortcuts(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); -- cgit v1.2.3 From 756adbc3e41ee1edb53c580b8c679f343924fab5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 16 Apr 2015 15:20:51 -0700 Subject: Fixing missing updates in package install sessions > Ensure icon cache never returns null icon > Enabling install shortuct receiver only after workspace has finished binding > Making all the model changes for package installs on worker thread and only posting the updaes on the UI > Making shortcut exists check on the loaded items and not on the DB > Explicitely using worker thread for PckageInstallListener > Removing backward compatibility support from PackageInstallerCompat Change-Id: I9592771b9670c1c1c84c8208cae8dafa7b393e65 --- src/com/android/launcher3/IconCache.java | 16 +- .../android/launcher3/InstallShortcutReceiver.java | 8 +- src/com/android/launcher3/Launcher.java | 36 ++-- src/com/android/launcher3/LauncherAppState.java | 11 -- src/com/android/launcher3/LauncherFiles.java | 3 +- src/com/android/launcher3/LauncherModel.java | 196 ++++++++++++++++----- src/com/android/launcher3/ShortcutInfo.java | 1 + src/com/android/launcher3/Workspace.java | 69 +------- .../launcher3/compat/PackageInstallerCompat.java | 17 +- .../compat/PackageInstallerCompatV16.java | 151 +--------------- .../launcher3/compat/PackageInstallerCompatVL.java | 144 +++------------ 11 files changed, 225 insertions(+), 427 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 48b38f182..fd4571482 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -101,7 +101,7 @@ public class IconCache { mIconDpi = activityManager.getLauncherLargeIconDensity(); mIconDb = new IconDB(context); - mWorkerHandler = new Handler(LauncherModel.sWorkerThread.getLooper()); + mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); } private Drawable getFullResDefaultActivityIcon() { @@ -388,16 +388,20 @@ public class IconCache { return new IconLoadRequest(request, mWorkerHandler); } + private Bitmap getNonNullIcon(CacheEntry entry, UserHandleCompat user) { + return entry.icon == null ? getDefaultIcon(user) : entry.icon; + } + /** * Fill in "application" with the icon and label for "info." */ public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, boolean useLowResIcon) { - CacheEntry entry = cacheLocked(application.componentName, info, - info == null ? application.user : info.getUser(), + UserHandleCompat user = info == null ? application.user : info.getUser(); + CacheEntry entry = cacheLocked(application.componentName, info, user, false, useLowResIcon); application.title = entry.title; - application.iconBitmap = entry.icon; + application.iconBitmap = getNonNullIcon(entry, user); application.contentDescription = entry.contentDescription; application.usingLowResIcon = entry.isLowResIcon; } @@ -445,7 +449,7 @@ public class IconCache { ShortcutInfo shortcutInfo, ComponentName component, LauncherActivityInfoCompat info, UserHandleCompat user, boolean usePkgIcon, boolean useLowResIcon) { CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon); - shortcutInfo.setIcon(entry.icon); + shortcutInfo.setIcon(getNonNullIcon(entry, user)); shortcutInfo.title = entry.title; shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); shortcutInfo.usingLowResIcon = entry.isLowResIcon; @@ -458,7 +462,7 @@ public class IconCache { String packageName, UserHandleCompat user, boolean useLowResIcon, PackageItemInfo infoOut) { CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon); - infoOut.iconBitmap = entry.icon; + infoOut.iconBitmap = getNonNullIcon(entry, user); infoOut.title = entry.title; infoOut.usingLowResIcon = entry.isLowResIcon; infoOut.contentDescription = entry.contentDescription; diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 0c69154aa..27dda6404 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -199,12 +199,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } } - final boolean exists = LauncherModel.shortcutExists(context, pendingInfo.label, - intent, pendingInfo.user); - if (!exists) { - // Generate a shortcut info to add into the model - addShortcuts.add(pendingInfo.getShortcutInfo()); - } + // Generate a shortcut info to add into the model + addShortcuts.add(pendingInfo.getShortcutInfo()); } // Add the new apps to the model and bind them diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7364a9f20..20844777f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -98,8 +98,6 @@ import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; -import com.android.launcher3.compat.PackageInstallerCompat; -import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.Thunk; @@ -1060,9 +1058,6 @@ public class Launcher extends Activity getWorkspace().reinflateWidgetsIfNecessary(); reinflateQSBIfNecessary(); - // Process any items that were added while Launcher was away. - InstallShortcutReceiver.disableAndFlushInstallQueue(this); - if (DEBUG_RESUME_TIME) { Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime)); } @@ -1078,7 +1073,10 @@ public class Launcher extends Activity mWorkspace.updateInteractionForState(); mWorkspace.onResume(); - PackageInstallerCompat.getInstance(this).onResume(); + if (!isWorkspaceLoading()) { + // Process any items that were added while Launcher was away. + InstallShortcutReceiver.disableAndFlushInstallQueue(this); + } if (mLauncherCallbacks != null) { mLauncherCallbacks.onResume(); @@ -1089,7 +1087,6 @@ public class Launcher extends Activity protected void onPause() { // Ensure that items added to Launcher are queued until Launcher returns InstallShortcutReceiver.enableInstallQueue(); - PackageInstallerCompat.getInstance(this).onPause(); super.onPause(); mPaused = true; @@ -4084,7 +4081,7 @@ public class Launcher extends Activity sPendingAddItem = null; } - PackageInstallerCompat.getInstance(this).onFinishBind(); + InstallShortcutReceiver.disableAndFlushInstallQueue(this); if (mLauncherCallbacks != null) { mLauncherCallbacks.finishBindingItems(false); @@ -4236,22 +4233,17 @@ public class Launcher extends Activity * Implementation of the method from LauncherModel.Callbacks. */ @Override - public void updatePackageState(ArrayList installInfo) { - if (mWorkspace != null) { - mWorkspace.updatePackageState(installInfo); + public void bindRestoreItemsChange(final HashSet updates) { + Runnable r = new Runnable() { + public void run() { + bindRestoreItemsChange(updates); + } + }; + if (waitUntilResume(r)) { + return; } - } - /** - * Update the label and icon of all the icons in a package - * - * Implementation of the method from LauncherModel.Callbacks. - */ - @Override - public void updatePackageBadge(String packageName) { - if (mWorkspace != null) { - mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle()); - } + mWorkspace.updateRestoreItems(updates); } /** diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 6e77d0628..7f31e4999 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -256,15 +256,4 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { public static boolean isDogfoodBuild() { return getInstance().mBuildInfo.isDogfoodBuild(); } - - public void setPackageState(ArrayList installInfo) { - mModel.setPackageState(installInfo); - } - - /** - * Updates the icons and label of all icons for the provided package name. - */ - public void updatePackageBadge(String packageName) { - mModel.updatePackageBadge(packageName); - } } diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 9dd8dc50c..03ec4bf7a 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -42,5 +42,6 @@ public class LauncherFiles { // TODO: Delete these files on upgrade public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( "launches.log", - "stats.log")); + "stats.log", + "com.android.launcher3.compat.PackageInstallerCompatV16.queue")); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index e5561e219..5a65cab82 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -42,6 +42,7 @@ import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.Looper; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; @@ -199,8 +200,7 @@ public class LauncherModel extends BroadcastReceiver public void bindShortcutsChanged(ArrayList updated, ArrayList removed, UserHandleCompat user); public void bindWidgetsRestored(ArrayList widgets); - public void updatePackageState(ArrayList installInfo); - public void updatePackageBadge(String packageName); + public void bindRestoreItemsChange(HashSet updates); public void bindComponentsRemoved(ArrayList packageNames, ArrayList appInfos, UserHandleCompat user, int reason); public void bindPackagesUpdated(ArrayList widgetsAndShortcuts); @@ -282,30 +282,110 @@ public class LauncherModel extends BroadcastReceiver return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ; } - public void setPackageState(final ArrayList installInfo) { - // Process the updated package state - Runnable r = new Runnable() { + public void setPackageState(final PackageInstallInfo installInfo) { + Runnable updateRunnable = new Runnable() { + + @Override public void run() { - Callbacks callbacks = getCallback(); - if (callbacks != null) { - callbacks.updatePackageState(installInfo); + synchronized (sBgLock) { + final HashSet updates = new HashSet<>(); + + if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) { + // Ignore install success events as they are handled by Package add events. + return; + } + + for (ItemInfo info : sBgItemsIdMap.values()) { + if (info instanceof ShortcutInfo) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (si.isPromise() && (cn != null) + && installInfo.packageName.equals(cn.getPackageName())) { + si.setInstallProgress(installInfo.progress); + + if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) { + // Mark this info as broken. + si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; + } + updates.add(si); + } + } + } + + for (LauncherAppWidgetInfo widget : sBgAppWidgets) { + if (widget.providerName.getPackageName().equals(installInfo.packageName)) { + widget.installProgress = installInfo.progress; + updates.add(widget); + } + } + + if (!updates.isEmpty()) { + // Push changes to the callback. + Runnable r = new Runnable() { + public void run() { + Callbacks callbacks = getCallback(); + if (callbacks != null) { + callbacks.bindRestoreItemsChange(updates); + } + } + }; + mHandler.post(r); + } } } }; - mHandler.post(r); + runOnWorkerThread(updateRunnable); } - public void updatePackageBadge(final String packageName) { - // Process the updated package badge - Runnable r = new Runnable() { + /** + * Updates the icons and label of all pending icons for the provided package name. + */ + public void updateSessionDisplayInfo(final String packageName) { + Runnable updateRunnable = new Runnable() { + + @Override public void run() { - Callbacks callbacks = getCallback(); - if (callbacks != null) { - callbacks.updatePackageBadge(packageName); + synchronized (sBgLock) { + final ArrayList updates = new ArrayList<>(); + final UserHandleCompat user = UserHandleCompat.myUserHandle(); + + for (ItemInfo info : sBgItemsIdMap.values()) { + if (info instanceof ShortcutInfo) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (si.isPromise() && (cn != null) + && packageName.equals(cn.getPackageName())) { + if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { + // For auto install apps update the icon as well as label. + mIconCache.getTitleAndIcon(si, + si.promisedIntent, user, + si.shouldUseLowResIcon()); + } else { + // Only update the icon for restored apps. + si.updateIcon(mIconCache); + } + updates.add(si); + } + } + } + + if (!updates.isEmpty()) { + // Push changes to the callback. + Runnable r = new Runnable() { + public void run() { + Callbacks callbacks = getCallback(); + if (callbacks != null) { + callbacks.bindShortcutsChanged(updates, + new ArrayList(), user); + } + } + }; + mHandler.post(r); + } } } }; - mHandler.post(r); + runOnWorkerThread(updateRunnable); } public void addAppsToAllApps(final Context ctx, final ArrayList allAppsApps) { @@ -537,8 +617,7 @@ public class LauncherModel extends BroadcastReceiver for (ItemInfo item : workspaceApps) { if (!allowDuplicate && item instanceof ShortcutInfo) { // Short-circuit this logic if the icon exists somewhere on the workspace - if (shortcutExists(context, item.title.toString(), - item.getIntent(), item.user)) { + if (shortcutExists(context, item.getIntent(), item.user)) { continue; } } @@ -904,41 +983,42 @@ public class LauncherModel extends BroadcastReceiver } /** - * Returns true if the shortcuts already exists in the database. - * we identify a shortcut by its title and intent. + * Returns true if the shortcuts already exists on the workspace. This must be called after + * the workspace has been loaded. We identify a shortcut by its intent. + * TODO: Throw exception is above condition is not met. */ - static boolean shortcutExists(Context context, String title, Intent intent, - UserHandleCompat user) { - final ContentResolver cr = context.getContentResolver(); + @Thunk static boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) { final Intent intentWithPkg, intentWithoutPkg; - + final String packageName; if (intent.getComponent() != null) { // If component is not null, an intent with null package will produce // the same result and should also be a match. + packageName = intent.getComponent().getPackageName(); if (intent.getPackage() != null) { intentWithPkg = intent; intentWithoutPkg = new Intent(intent).setPackage(null); } else { - intentWithPkg = new Intent(intent).setPackage( - intent.getComponent().getPackageName()); + intentWithPkg = new Intent(intent).setPackage(packageName); intentWithoutPkg = intent; } } else { intentWithPkg = intent; intentWithoutPkg = intent; + packageName = intent.getPackage(); } - String userSerial = Long.toString(UserManagerCompat.getInstance(context) - .getSerialNumberForUser(user)); - Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { "title", "intent", "profileId" }, - "title=? and (intent=? or intent=?) and profileId=?", - new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial }, - null); - try { - return c.moveToFirst(); - } finally { - c.close(); + + synchronized (sBgLock) { + for (ItemInfo item : sBgItemsIdMap.values()) { + if (item instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo) item; + if (intentWithPkg.equals(info.getIntent()) + || intentWithoutPkg.equals(info.getIntent())) { + return true; + } + } + } } + return false; } /** @@ -1366,6 +1446,8 @@ public class LauncherModel extends BroadcastReceiver } public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) { + // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems + InstallShortcutReceiver.enableInstallQueue(); synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); @@ -1812,7 +1894,7 @@ public class LauncherModel extends BroadcastReceiver synchronized (sBgLock) { clearSBgDataStructures(); - final HashSet installingPkgs = PackageInstallerCompat + final HashMap installingPkgs = PackageInstallerCompat .getInstance(mContext).updateAndGetActiveSessionCache(); final ArrayList itemsToRemove = new ArrayList(); @@ -1951,7 +2033,7 @@ public class LauncherModel extends BroadcastReceiver if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) { // Restore has started once. - } else if (installingPkgs.contains(cn.getPackageName())) { + } else if (installingPkgs.containsKey(cn.getPackageName())) { // App restore has started. Update the flag promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED; ContentValues values = new ContentValues(); @@ -2093,6 +2175,18 @@ public class LauncherModel extends BroadcastReceiver break; } + if (restored) { + ComponentName cn = info.getTargetComponent(); + if (cn != null) { + Integer progress = installingPkgs.get(cn.getPackageName()); + if (progress != null) { + info.setInstallProgress(progress); + } else { + info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; + } + } + } + switch (container) { case LauncherSettings.Favorites.CONTAINER_DESKTOP: case LauncherSettings.Favorites.CONTAINER_HOTSEAT: @@ -2220,10 +2314,11 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); appWidgetInfo.restoreStatus = restoreStatus; + Integer installProgress = installingPkgs.get(component.getPackageName()); if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) { // Restore has started once. - } else if (installingPkgs.contains(component.getPackageName())) { + } else if (installProgress != null) { // App restore has started. Update the flag appWidgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; @@ -2233,6 +2328,9 @@ public class LauncherModel extends BroadcastReceiver itemsToRemove.add(id); continue; } + + appWidgetInfo.installProgress = + installProgress == null ? 0 : installProgress; } appWidgetInfo.id = id; @@ -3112,15 +3210,13 @@ public class LauncherModel extends BroadcastReceiver } // Restore the shortcut. - si.intent = si.promisedIntent; - si.promisedIntent = null; - si.status &= ~ShortcutInfo.FLAG_RESTORED_ICON - & ~ShortcutInfo.FLAG_AUTOINTALL_ICON - & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; if (appInfo != null) { si.flags = appInfo.flags; } + si.intent = si.promisedIntent; + si.promisedIntent = null; + si.status = ShortcutInfo.DEFAULT; infoUpdated = true; si.updateIcon(mIconCache); } @@ -3353,12 +3449,10 @@ public class LauncherModel extends BroadcastReceiver if (!TextUtils.isEmpty(title)) { info.title = title; } - info.status = ShortcutInfo.FLAG_RESTORED_ICON; } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) { if (TextUtils.isEmpty(info.title)) { info.title = (cursor != null) ? cursor.getString(titleIndex) : ""; } - info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON; } else { throw new InvalidParameterException("Invalid restoreType " + promiseType); } @@ -3367,6 +3461,7 @@ public class LauncherModel extends BroadcastReceiver info.title.toString(), info.user); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; info.promisedIntent = intent; + info.status = promiseType; return info; } @@ -3669,4 +3764,11 @@ public class LauncherModel extends BroadcastReceiver return sBgFolders.get(folderId); } } + + /** + * @return the looper for the worker thread which can be used to start background tasks. + */ + public static Looper getWorkerLooper() { + return sWorkerThread.getLooper(); + } } diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 5bef845bb..6354fcd28 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -214,6 +214,7 @@ public class ShortcutInfo extends ItemInfo { String uri = promisedIntent != null ? promisedIntent.toUri(0) : (intent != null ? intent.toUri(0) : null); values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri); + values.put(LauncherSettings.Favorites.RESTORED, status); if (customIcon) { values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 9e680fb82..abb8489fd 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -62,8 +62,6 @@ import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; -import com.android.launcher3.compat.PackageInstallerCompat; -import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; @@ -4358,32 +4356,17 @@ public class Workspace extends SmoothPagedView removeItemsByPackageName(packages, user); } - public void updatePackageBadge(final String packageName, final UserHandleCompat user) { + public void updateRestoreItems(final HashSet updates) { mapOverItems(MAP_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { - if (info instanceof ShortcutInfo && v instanceof BubbleTextView) { - ShortcutInfo shortcutInfo = (ShortcutInfo) info; - ComponentName cn = shortcutInfo.getTargetComponent(); - if (user.equals(shortcutInfo.user) && cn != null - && shortcutInfo.isPromise() - && packageName.equals(cn.getPackageName())) { - if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { - // For auto install apps update the icon as well as label. - mIconCache.getTitleAndIcon(shortcutInfo, - shortcutInfo.promisedIntent, user, - shortcutInfo.shouldUseLowResIcon()); - } else { - // Only update the icon for restored apps. - shortcutInfo.updateIcon(mIconCache); - } - BubbleTextView shortcut = (BubbleTextView) v; - shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false); - - if (parent != null) { - parent.invalidate(); - } - } + if (info instanceof ShortcutInfo && v instanceof BubbleTextView + && updates.contains(info)) { + ((BubbleTextView) v).applyState(false); + } else if (v instanceof PendingAppWidgetHostView + && info instanceof LauncherAppWidgetInfo + && updates.contains(info)) { + ((PendingAppWidgetHostView) v).applyState(); } // process all the shortcuts return false; @@ -4391,42 +4374,6 @@ public class Workspace extends SmoothPagedView }); } - public void updatePackageState(ArrayList installInfos) { - for (final PackageInstallInfo installInfo : installInfos) { - if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) { - continue; - } - - mapOverItems(MAP_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v, View parent) { - if (info instanceof ShortcutInfo && v instanceof BubbleTextView) { - ShortcutInfo si = (ShortcutInfo) info; - ComponentName cn = si.getTargetComponent(); - if (si.isPromise() && (cn != null) - && installInfo.packageName.equals(cn.getPackageName())) { - si.setInstallProgress(installInfo.progress); - if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) { - // Mark this info as broken. - si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; - } - ((BubbleTextView)v).applyState(false); - } - } else if (v instanceof PendingAppWidgetHostView - && info instanceof LauncherAppWidgetInfo - && ((LauncherAppWidgetInfo) info).providerName.getPackageName() - .equals(installInfo.packageName)) { - ((LauncherAppWidgetInfo) info).installProgress = installInfo.progress; - ((PendingAppWidgetHostView) v).applyState(); - } - - // process all the shortcuts - return false; - } - }); - } - } - void widgetsRestored(ArrayList changedInfo) { if (!changedInfo.isEmpty()) { DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo, diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java index 0eb8754e8..c49908328 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompat.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java @@ -20,7 +20,7 @@ import android.content.Context; import com.android.launcher3.Utilities; -import java.util.HashSet; +import java.util.HashMap; public abstract class PackageInstallerCompat { @@ -37,25 +37,20 @@ public abstract class PackageInstallerCompat { if (Utilities.isLmpOrAbove()) { sInstance = new PackageInstallerCompatVL(context); } else { - sInstance = new PackageInstallerCompatV16(context) { }; + sInstance = new PackageInstallerCompatV16(); } } return sInstance; } } - public abstract HashSet updateAndGetActiveSessionCache(); - - public abstract void onPause(); - - public abstract void onResume(); - - public abstract void onFinishBind(); + /** + * @return a map of active installs to their progress + */ + public abstract HashMap updateAndGetActiveSessionCache(); public abstract void onStop(); - public abstract void recordPackageUpdate(String packageName, int state, int progress); - public static final class PackageInstallInfo { public final String packageName; diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java index 1910d22ae..654e34968 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java @@ -16,160 +16,17 @@ package com.android.launcher3.compat; -import android.content.Context; -import android.content.SharedPreferences; -import android.text.TextUtils; -import android.util.Log; - -import com.android.launcher3.LauncherAppState; - -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONStringer; -import org.json.JSONTokener; - -import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; public class PackageInstallerCompatV16 extends PackageInstallerCompat { - private static final String TAG = "PackageInstallerCompatV16"; - private static final boolean DEBUG = false; - - private static final String KEY_PROGRESS = "progress"; - private static final String KEY_STATE = "state"; - - private static final String PREFS = - "com.android.launcher3.compat.PackageInstallerCompatV16.queue"; - - protected final SharedPreferences mPrefs; - - boolean mUseQueue; - boolean mFinishedBind; - boolean mReplayPending; - - PackageInstallerCompatV16(Context context) { - mPrefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE); - } - - @Override - public void onPause() { - mUseQueue = true; - if (DEBUG) Log.d(TAG, "updates paused"); - } - - @Override - public void onResume() { - mUseQueue = false; - if (mFinishedBind) { - replayUpdates(); - } - } - - @Override - public void onFinishBind() { - mFinishedBind = true; - if (!mUseQueue) { - replayUpdates(); - } - } + PackageInstallerCompatV16() { } @Override public void onStop() { } - private void replayUpdates() { - if (DEBUG) Log.d(TAG, "updates resumed"); - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app == null) { - mReplayPending = true; // try again later - if (DEBUG) Log.d(TAG, "app is null, delaying send"); - return; - } - mReplayPending = false; - ArrayList updates = new ArrayList(); - for (String packageName: mPrefs.getAll().keySet()) { - final String json = mPrefs.getString(packageName, null); - if (!TextUtils.isEmpty(json)) { - updates.add(infoFromJson(packageName, json)); - } - } - if (!updates.isEmpty()) { - sendUpdate(app, updates); - } - } - - /** - * This should be called by the implementations to register a package update. - */ - @Override - public synchronized void recordPackageUpdate(String packageName, int state, int progress) { - SharedPreferences.Editor editor = mPrefs.edit(); - PackageInstallInfo installInfo = new PackageInstallInfo(packageName); - installInfo.progress = progress; - installInfo.state = state; - if (state == STATUS_INSTALLED) { - // no longer necessary to track this package - editor.remove(packageName); - if (DEBUG) Log.d(TAG, "no longer tracking " + packageName); - } else { - editor.putString(packageName, infoToJson(installInfo)); - if (DEBUG) - Log.d(TAG, "saved state: " + infoToJson(installInfo) - + " for package: " + packageName); - - } - editor.commit(); - - if (!mUseQueue) { - if (mReplayPending) { - replayUpdates(); - } else if (state != STATUS_INSTALLED) { - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - ArrayList update = new ArrayList(); - update.add(installInfo); - sendUpdate(app, update); - } - } - } - - private void sendUpdate(LauncherAppState app, ArrayList updates) { - if (app == null) { - mReplayPending = true; // try again later - if (DEBUG) Log.d(TAG, "app is null, delaying send"); - } else { - app.setPackageState(updates); - } - } - - private static PackageInstallInfo infoFromJson(String packageName, String json) { - PackageInstallInfo info = new PackageInstallInfo(packageName); - try { - JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); - info.state = object.getInt(KEY_STATE); - info.progress = object.getInt(KEY_PROGRESS); - } catch (JSONException e) { - Log.e(TAG, "failed to deserialize app state update", e); - } - return info; - } - - private static String infoToJson(PackageInstallInfo info) { - String value = null; - try { - JSONStringer json = new JSONStringer() - .object() - .key(KEY_STATE).value(info.state) - .key(KEY_PROGRESS).value(info.progress) - .endObject(); - value = json.toString(); - } catch (JSONException e) { - Log.e(TAG, "failed to serialize app state update", e); - } - return value; - } - @Override - public HashSet updateAndGetActiveSessionCache() { - return new HashSet(); + public HashMap updateAndGetActiveSessionCache() { + return new HashMap<>(); } } diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index d6d4b8287..395d5f9e2 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -21,63 +21,41 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionCallback; import android.content.pm.PackageInstaller.SessionInfo; import android.os.Handler; -import android.util.Log; import android.util.SparseArray; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; import com.android.launcher3.util.Thunk; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; -public class PackageInstallerCompatVL extends PackageInstallerCompat implements Runnable { +public class PackageInstallerCompatVL extends PackageInstallerCompat { - private static final String TAG = "PackageInstallerCompatVL"; - private static final boolean DEBUG = false; - - // All updates to these sets must happen on the {@link #mWorker} thread. - @Thunk final SparseArray mPendingReplays = new SparseArray(); - @Thunk final HashSet mPendingBadgeUpdates = new HashSet(); + @Thunk final SparseArray mActiveSessions = new SparseArray<>(); @Thunk final PackageInstaller mInstaller; private final IconCache mCache; private final Handler mWorker; - private boolean mResumed; - private boolean mBound; - PackageInstallerCompatVL(Context context) { mInstaller = context.getPackageManager().getPackageInstaller(); LauncherAppState.setApplicationContext(context.getApplicationContext()); mCache = LauncherAppState.getInstance().getIconCache(); - mWorker = new Handler(); - - mResumed = false; - mBound = false; + mWorker = new Handler(LauncherModel.getWorkerLooper()); mInstaller.registerSessionCallback(mCallback, mWorker); - - // On start, send updates for all active sessions - mWorker.post(new Runnable() { - - @Override - public void run() { - for (SessionInfo info : mInstaller.getAllSessions()) { - mPendingReplays.append(info.getSessionId(), info); - } - } - }); } @Override - public HashSet updateAndGetActiveSessionCache() { - HashSet activePackages = new HashSet(); + public HashMap updateAndGetActiveSessionCache() { + HashMap activePackages = new HashMap<>(); UserHandleCompat user = UserHandleCompat.myUserHandle(); for (SessionInfo info : mInstaller.getAllSessions()) { addSessionInfoToCahce(info, user); if (info.getAppPackageName() != null) { - activePackages.add(info.getAppPackageName()); + activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100)); + mActiveSessions.put(info.getSessionId(), info.getAppPackageName()); } } return activePackages; @@ -96,74 +74,10 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements mInstaller.unregisterSessionCallback(mCallback); } - @Override - public void onFinishBind() { - mBound = true; - mWorker.post(this); - } - - @Override - public void onPause() { - mResumed = false; - } - - @Override - public void onResume() { - mResumed = true; - mWorker.post(this); - } - - @Override - public void recordPackageUpdate(String packageName, int state, int progress) { - // No op - } - - @Override - public void run() { - // Called on mWorker thread. - replayUpdates(null); - } - - @Thunk void replayUpdates(PackageInstallInfo newInfo) { - if (DEBUG) Log.d(TAG, "updates resumed"); - if (!mResumed || !mBound) { - // Not yet ready - return; - } - if ((mPendingReplays.size() == 0) && (newInfo == null)) { - // Nothing to update - return; - } - + @Thunk void sendUpdate(PackageInstallInfo info) { LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app == null) { - // Try again later - if (DEBUG) Log.d(TAG, "app is null, delaying send"); - return; - } - - ArrayList updates = new ArrayList(); - if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) { - updates.add(newInfo); - } - for (int i = mPendingReplays.size() - 1; i >= 0; i--) { - SessionInfo session = mPendingReplays.valueAt(i); - if (session.getAppPackageName() != null) { - updates.add(new PackageInstallInfo(session.getAppPackageName(), - STATUS_INSTALLING, - (int) (session.getProgress() * 100))); - } - } - mPendingReplays.clear(); - if (!updates.isEmpty()) { - app.setPackageState(updates); - } - - if (!mPendingBadgeUpdates.isEmpty()) { - for (String pkg : mPendingBadgeUpdates) { - app.updatePackageBadge(pkg); - } - mPendingBadgeUpdates.clear(); + if (app != null) { + app.getModel().setPackageState(info); } } @@ -171,19 +85,18 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements @Override public void onCreated(int sessionId) { - pushSessionBadgeToLauncher(sessionId); + pushSessionDisplayToLauncher(sessionId); } @Override public void onFinished(int sessionId, boolean success) { - mPendingReplays.remove(sessionId); - SessionInfo session = mInstaller.getSessionInfo(sessionId); - if ((session != null) && (session.getAppPackageName() != null)) { - mPendingBadgeUpdates.remove(session.getAppPackageName()); - // Replay all updates with a one time update for this installed package. No - // need to store this record for future updates, as the app list will get - // refreshed on resume. - replayUpdates(new PackageInstallInfo(session.getAppPackageName(), + // For a finished session, we can't get the session info. So use the + // packageName from our local cache. + String packageName = mActiveSessions.get(sessionId); + mActiveSessions.remove(sessionId); + + if (packageName != null) { + sendUpdate(new PackageInstallInfo(packageName, success ? STATUS_INSTALLED : STATUS_FAILED, 0)); } } @@ -192,8 +105,9 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements public void onProgressChanged(int sessionId, float progress) { SessionInfo session = mInstaller.getSessionInfo(sessionId); if (session != null) { - mPendingReplays.put(sessionId, session); - replayUpdates(null); + sendUpdate(new PackageInstallInfo(session.getAppPackageName(), + STATUS_INSTALLING, + (int) (session.getProgress() * 100))); } } @@ -202,18 +116,18 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat implements @Override public void onBadgingChanged(int sessionId) { - pushSessionBadgeToLauncher(sessionId); + pushSessionDisplayToLauncher(sessionId); } - private void pushSessionBadgeToLauncher(int sessionId) { + private void pushSessionDisplayToLauncher(int sessionId) { SessionInfo session = mInstaller.getSessionInfo(sessionId); if (session != null) { addSessionInfoToCahce(session, UserHandleCompat.myUserHandle()); - if (session.getAppPackageName() != null) { - mPendingBadgeUpdates.add(session.getAppPackageName()); + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + + if (app != null) { + app.getModel().updateSessionDisplayInfo(session.getAppPackageName()); } - mPendingReplays.put(sessionId, session); - replayUpdates(null); } } }; -- cgit v1.2.3 From 475784a4ebcf3cc4816aba7faaacac7a5f826298 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 22 Apr 2015 13:59:20 -0700 Subject: Widgettray UI tweak that introduces bounding box around the widgets. Note: waiting on more polished UX spec right now. But until then, these padding and dimensions are what is used to get the bounding box around the widgets. Change-Id: I7a6a5fa04bbdb135b39291cb671b9a719fcdc4fc --- res/layout/widget_cell.xml | 7 ++++--- res/layout/widgets_list_row_view.xml | 6 +----- res/values/dimens.xml | 6 +++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index f53b74ef8..50294c035 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -19,9 +19,11 @@ android:layout_width="@dimen/widget_preview_container_width" android:layout_height="wrap_content" android:layout_weight="1" - android:paddingTop="@dimen/widget_preview_padding_top" + android:layout_marginTop="@dimen/widget_preview_padding_top" + android:layout_marginLeft="8dp" + android:layout_marginBottom="8dp" android:orientation="vertical" - android:background="@drawable/focusable_view_bg" + android:background="@color/bubble_dark_background" android:focusable="true"> @@ -49,7 +45,6 @@ android:id="@+id/section" android:layout_width="match_parent" android:layout_height="@dimen/widget_section_height" - android:paddingTop="8dp" android:paddingLeft="16dp" android:paddingRight="16dp" android:singleLine="true" @@ -80,6 +75,7 @@ android:id="@+id/widgets_scroll_container" android:layout_width="match_parent" android:layout_height="@dimen/widget_cell_height" + android:paddingLeft="40dp" android:scrollbars="none" > 8dp - 140dp + 120dp 8dp 8dp 8dp @@ -90,8 +90,8 @@ 8dp - 156dp - 160dp + 136dp + 150dp 0dp -- cgit v1.2.3 From d9cc780fade7a078d127c7e5b8209d414a46f0a5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 23 Apr 2015 14:47:19 -0700 Subject: Making QSB alingment match with that of recents Change-Id: I6be59c721cbd163641013ca6c5e1776cf2162a0d --- res/layout/search_drop_target_bar.xml | 3 +-- src/com/android/launcher3/DeviceProfile.java | 14 +++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml index 9b0da1d4e..e383d74c0 100644 --- a/res/layout/search_drop_target_bar.xml +++ b/res/layout/search_drop_target_bar.xml @@ -17,8 +17,7 @@ + android:focusable="false" > diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 22fb6a049..40998a52a 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -734,7 +734,6 @@ public class DeviceProfile { public void layout(Launcher launcher) { FrameLayout.LayoutParams lp; - Resources res = launcher.getResources(); boolean hasVerticalBarLayout = isVerticalBarLayout(); // Layout the search bar space @@ -742,17 +741,22 @@ public class DeviceProfile { lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); if (hasVerticalBarLayout) { // Vertical search bar space - lp.gravity = Gravity.TOP | Gravity.LEFT; + lp.gravity = Gravity.LEFT; lp.width = searchBarSpaceHeightPx; - lp.height = LayoutParams.WRAP_CONTENT; LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar); targets.setOrientation(LinearLayout.VERTICAL); + FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams(); + targetsLp.gravity = Gravity.TOP; + targetsLp.height = LayoutParams.WRAP_CONTENT; + } else { // Horizontal search bar space - lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; - lp.width = searchBarSpaceWidthPx; + lp.gravity = Gravity.TOP; lp.height = searchBarSpaceHeightPx; + + LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar); + targets.getLayoutParams().width = searchBarSpaceWidthPx; } searchBar.setLayoutParams(lp); -- cgit v1.2.3 From cabc4e5159ffa8ac6600418a204d8720fd41c913 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 23 Apr 2015 14:48:51 -0700 Subject: Rearranging drop target relative positions > It will now show either [Remove, Uninstall] or [App Info, Uninstall] Bug: 20529800 Change-Id: Ifda6f24cca5c38e84c68560cbeb5f29bade75b6a --- res/layout/search_drop_target_bar.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml index 9b0da1d4e..fe18edc84 100644 --- a/res/layout/search_drop_target_bar.xml +++ b/res/layout/search_drop_target_bar.xml @@ -45,26 +45,26 @@ style="@style/DropTargetButtonContainer" android:layout_weight="1" > - + - + android:drawableStart="@drawable/info_target_selector" + android:text="@string/info_target_label" /> - + - + android:drawableStart="@drawable/uninstall_target_selector" + android:text="@string/delete_target_uninstall_label" /> -- cgit v1.2.3 From b99ff3e83270e113f6182e337c4f7b0223bad92b Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 23 Apr 2015 15:17:50 -0700 Subject: Add drop animation / Toast to widgettray - show instruction toast on clicking the widget - Add animation when widget drops on the workspace. Added WidgetHostViewLoader to handle short press and assign widget host view to enable animation b/19897708 Change-Id: Iec36d72cb21bf09343d0beeb31a09bf8b0cb5e0d --- res/layout/widget_cell.xml | 4 +- .../android/launcher3/AppWidgetResizeFrame.java | 2 +- src/com/android/launcher3/Launcher.java | 3 +- src/com/android/launcher3/widget/WidgetCell.java | 212 ++++++--------------- .../launcher3/widget/WidgetHostViewLoader.java | 197 +++++++++++++++++++ .../launcher3/widget/WidgetsContainerView.java | 67 ++++--- .../launcher3/widget/WidgetsListAdapter.java | 5 - 7 files changed, 296 insertions(+), 194 deletions(-) create mode 100644 src/com/android/launcher3/widget/WidgetHostViewLoader.java diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index 50294c035..64ddea1ae 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -47,7 +47,7 @@ android:fadingEdge="horizontal" android:textColor="#FFFFFFFF" - android:textSize="12sp" + android:textSize="16sp" android:textAlignment="viewStart" android:fontFamily="sans-serif-condensed" android:shadowRadius="2.0" @@ -64,7 +64,7 @@ android:layout_weight="0" android:gravity="start" android:textColor="#FFFFFFFF" - android:textSize="12sp" + android:textSize="16sp" android:textAlignment="viewStart" android:fontFamily="sans-serif-condensed" android:shadowRadius="2.0" diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 240250750..3c698c014 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -349,7 +349,7 @@ public class AppWidgetResizeFrame extends FrameLayout { mTmpRect.right, mTmpRect.bottom); } - static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) { + public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) { if (rect == null) { rect = new Rect(); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7364a9f20..c708b3c8e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -104,6 +104,7 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; import java.io.DataInputStream; @@ -3953,7 +3954,7 @@ public class Launcher extends Activity pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; Bundle options = null; - // AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo); + WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); int newWidgetId = mAppWidgetHost.allocateAppWidgetId(); boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 1ae75c3cc..0bc7333ec 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -24,8 +24,6 @@ import android.graphics.Bitmap; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; -import android.util.TypedValue; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.widget.ImageView; @@ -43,7 +41,7 @@ import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; import com.android.launcher3.compat.AppWidgetManagerCompat; /** - * The linear layout used strictly for the widget tray. + * Represents the individual cell of the widget inside the widget tray. */ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { @@ -53,14 +51,12 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final int FADE_IN_DURATION_MS = 70; private int mPresetPreviewSize; - private static WidgetCell sShortpressTarget = null; - + private ImageView mWidgetImage; + private TextView mWidgetName; + private TextView mWidgetDims; private final Rect mOriginalImagePadding = new Rect(); private String mDimensionsFormatString; - private CheckForShortPress mPendingCheckForShortPress = null; - private ShortPressListener mShortPressListener = null; - private boolean mShortPressTriggered = false; private boolean mIsAppWidget; private Object mInfo; @@ -92,57 +88,27 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { protected void onFinishInflate() { super.onFinishInflate(); - final ImageView image = (ImageView) findViewById(R.id.widget_preview); - mOriginalImagePadding.left = image.getPaddingLeft(); - mOriginalImagePadding.top = image.getPaddingTop(); - mOriginalImagePadding.right = image.getPaddingRight(); - mOriginalImagePadding.bottom = image.getPaddingBottom(); + mWidgetImage = (ImageView) findViewById(R.id.widget_preview); + mOriginalImagePadding.left = mWidgetImage.getPaddingLeft(); + mOriginalImagePadding.top = mWidgetImage.getPaddingTop(); + mOriginalImagePadding.right = mWidgetImage.getPaddingRight(); + mOriginalImagePadding.bottom = mWidgetImage.getPaddingBottom(); // Ensure we are using the right text size - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - TextView name = (TextView) findViewById(R.id.widget_name); - if (name != null) { - name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); - } - TextView dims = (TextView) findViewById(R.id.widget_dims); - if (dims != null) { - dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); - } - } - - @Override - protected void onDetachedFromWindow() { - if (DEBUG) { - Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString())); - } - super.onDetachedFromWindow(); - deletePreview(false); + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + mWidgetName = ((TextView) findViewById(R.id.widget_name)); + mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } public void reset() { - ImageView image = (ImageView) findViewById(R.id.widget_preview); - final TextView name = (TextView) findViewById(R.id.widget_name); - final TextView dims = (TextView) findViewById(R.id.widget_dims); - image.setImageDrawable(null); - name.setText(null); - dims.setText(null); - } - - public void deletePreview(boolean recycleImage) { - if (recycleImage) { - final ImageView image = (ImageView) findViewById(R.id.widget_preview); - if (image != null) { - image.setImageDrawable(null); - } - } - - if (mActiveRequest != null) { - mActiveRequest.cancel(recycleImage); - mActiveRequest = null; - } + mWidgetImage.setImageDrawable(null); + mWidgetName.setText(null); + mWidgetDims.setText(null); } + /** + * Apply the widget provider info to the view. + */ public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, int maxWidth, WidgetPreviewLoader loader) { LauncherAppState app = LauncherAppState.getInstance(); @@ -150,37 +116,41 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mIsAppWidget = true; mInfo = info; - final ImageView image = (ImageView) findViewById(R.id.widget_preview); if (maxWidth > -1) { - image.setMaxWidth(maxWidth); - } - final TextView name = (TextView) findViewById(R.id.widget_name); - name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); - final TextView dims = (TextView) findViewById(R.id.widget_dims); - if (dims != null) { - int hSpan = Math.min(info.spanX, (int) grid.numColumns); - int vSpan = Math.min(info.spanY, (int) grid.numRows); - dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); + mWidgetImage.setMaxWidth(maxWidth); } + // TODO(hyunyoungs): setup a cache for these labels. + mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); + int hSpan = Math.min(info.spanX, (int) grid.numColumns); + int vSpan = Math.min(info.spanY, (int) grid.numRows); + mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); mWidgetPreviewLoader = loader; } + /** + * Apply the resolve info to the view. + */ public void applyFromResolveInfo( PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) { mIsAppWidget = false; mInfo = info; CharSequence label = info.loadLabel(pm); - final TextView name = (TextView) findViewById(R.id.widget_name); - name.setText(label); - final TextView dims = (TextView) findViewById(R.id.widget_dims); - if (dims != null) { - dims.setText(String.format(mDimensionsFormatString, 1, 1)); - } + mWidgetName.setText(label); + mWidgetDims.setText(String.format(mDimensionsFormatString, 1, 1)); mWidgetPreviewLoader = loader; } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + deletePreview(false); + + if (DEBUG) { + Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString())); + } + } + public int[] getPreviewSize() { - final ImageView i = (ImageView) findViewById(R.id.widget_preview); int[] maxSize = new int[2]; maxSize[0] = mPresetPreviewSize; maxSize[1] = mPresetPreviewSize; @@ -189,110 +159,28 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { public void applyPreview(Bitmap bitmap) { FastBitmapDrawable preview = new FastBitmapDrawable(bitmap); - final WidgetImageView image = - (WidgetImageView) findViewById(R.id.widget_preview); if (DEBUG) { Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s", getTagToString(), preview)); } if (preview != null) { - image.mAllowRequestLayout = false; - image.setImageDrawable(preview); + mWidgetImage.setImageDrawable(preview); if (mIsAppWidget) { // center horizontally int[] imageSize = getPreviewSize(); int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2; - image.setPadding(mOriginalImagePadding.left + centerAmount, + mWidgetImage.setPadding(mOriginalImagePadding.left + centerAmount, mOriginalImagePadding.top, mOriginalImagePadding.right, mOriginalImagePadding.bottom); } - image.setAlpha(0f); - image.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS); - image.mAllowRequestLayout = true; - image.requestLayout(); - } - } - - void setShortPressListener(ShortPressListener listener) { - mShortPressListener = listener; - } - - interface ShortPressListener { - void onShortPress(View v); - void cleanUpShortPress(View v); - } - - class CheckForShortPress implements Runnable { - public void run() { - if (sShortpressTarget != null) return; - if (mShortPressListener != null) { - mShortPressListener.onShortPress(WidgetCell.this); - sShortpressTarget = WidgetCell.this; - } - mShortPressTriggered = true; + mWidgetImage.setAlpha(0f); + mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS); + // TODO(hyunyoungs): figure out why this has to be called explicitly. + mWidgetImage.requestLayout(); } } - private void checkForShortPress() { - if (sShortpressTarget != null) return; - if (mPendingCheckForShortPress == null) { - mPendingCheckForShortPress = new CheckForShortPress(); - } - postDelayed(mPendingCheckForShortPress, 120); - } - - /** - * Remove the longpress detection timer. - */ - private void removeShortPressCallback() { - if (mPendingCheckForShortPress != null) { - removeCallbacks(mPendingCheckForShortPress); - } - } - - private void cleanUpShortPress() { - removeShortPressCallback(); - if (mShortPressTriggered) { - if (mShortPressListener != null) { - mShortPressListener.cleanUpShortPress(WidgetCell.this); - } - mShortPressTriggered = false; - } - } - - static void resetShortPressTarget() { - sShortpressTarget = null; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - cleanUpShortPress(); - break; - case MotionEvent.ACTION_DOWN: - checkForShortPress(); - break; - case MotionEvent.ACTION_CANCEL: - cleanUpShortPress(); - break; - case MotionEvent.ACTION_MOVE: - break; - } - - // We eat up the touch events here, since the PagedView (which uses the same swiping - // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when - // the user is scrolling between pages. This means that if the pages themselves don't - // handle touch events, it gets forwarded up to PagedView itself, and it's own - // onTouchEvent() handling will prevent further intercept touch events from being called - // (it's the same view in that case). This is not ideal, but to prevent more changes, - // we just always mark the touch event as handled. - return true; - } - public void ensurePreview() { if (mActiveRequest != null) { return; @@ -331,6 +219,16 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return Math.min(size[0], info.spanX * cellWidth); } + + private void deletePreview(boolean recycleImage) { + mWidgetImage.setImageDrawable(null); + + if (mActiveRequest != null) { + mActiveRequest.cancel(recycleImage); + mActiveRequest = null; + } + } + /** * Helper method to get the string info of the tag. */ diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java new file mode 100644 index 000000000..d65455053 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -0,0 +1,197 @@ +package com.android.launcher3.widget; + +import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.View; + +import com.android.launcher3.AppWidgetResizeFrame; +import com.android.launcher3.DragLayer; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.compat.AppWidgetManagerCompat; + +public class WidgetHostViewLoader { + + private static final boolean DEBUG = false; + private static final String TAG = "WidgetHostViewLoader"; + + /* constants used for widget loading state. */ + private static final int WIDGET_NO_CLEANUP_REQUIRED = -1; + private static final int WIDGET_PRELOAD_PENDING = 0; + private static final int WIDGET_BOUND = 1; + private static final int WIDGET_INFLATED = 2; + + int mState = WIDGET_NO_CLEANUP_REQUIRED; + + /* Runnables to handle inflation and binding. */ + private Runnable mInflateWidgetRunnable = null; + private Runnable mBindWidgetRunnable = null; + + /* Id of the widget being handled. */ + int mWidgetLoadingId = -1; + PendingAddWidgetInfo mCreateWidgetInfo = null; + + // TODO: technically, this class should not have to know the existence of the launcher. + private Launcher mLauncher; + private Handler mHandler; + + public WidgetHostViewLoader(Launcher launcher) { + mLauncher = launcher; + mHandler = new Handler(); + } + + /** + * Start loading the widget. + */ + public void load(View v) { + if (mCreateWidgetInfo != null) { + // Just in case the cleanup process wasn't properly executed. + finish(false); + } + boolean status = false; + if (v.getTag() instanceof PendingAddWidgetInfo) { + mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag()); + status = preloadWidget(v, mCreateWidgetInfo); + } + if (DEBUG) { + Log.d(TAG, String.format("load started on [state=%d, status=%s]", mState, status)); + } + } + + + /** + * Clean up according to what the last known state was. + * @param widgetIdUsed {@code true} if the widgetId was consumed which can happen only + * when view is fully inflated + */ + public void finish(boolean widgetIdUsed) { + if (DEBUG) { + Log.d(TAG, String.format("cancel on state [%d] widgetId=[%d]", + mState, mWidgetLoadingId)); + } + + // If the widget was not added, we may need to do further cleanup. + PendingAddWidgetInfo info = mCreateWidgetInfo; + mCreateWidgetInfo = null; + + if (mState == WIDGET_PRELOAD_PENDING) { + // We never did any preloading, so just remove pending callbacks to do so + mHandler.removeCallbacks(mBindWidgetRunnable); + mHandler.removeCallbacks(mInflateWidgetRunnable); + } else if (mState == WIDGET_BOUND) { + // Delete the widget id which was allocated + if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { + mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + } + + // We never got around to inflating the widget, so remove the callback to do so. + mHandler.removeCallbacks(mInflateWidgetRunnable); + } else if (mState == WIDGET_INFLATED && !widgetIdUsed) { + // Delete the widget id which was allocated + if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { + mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + } + + // The widget was inflated and added to the DragLayer -- remove it. + AppWidgetHostView widget = info.boundWidget; + mLauncher.getDragLayer().removeView(widget); + } + setState(WIDGET_NO_CLEANUP_REQUIRED); + mWidgetLoadingId = -1; + } + + private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) { + final LauncherAppWidgetProviderInfo pInfo = info.info; + + final Bundle options = pInfo.isCustomWidget ? null : + getDefaultOptionsForWidget(mLauncher, info); + + // If there is a configuration activity, do not follow thru bound and inflate. + if (pInfo.configure != null) { + info.bindOptions = options; + return false; + } + setState(WIDGET_PRELOAD_PENDING); + mBindWidgetRunnable = new Runnable() { + @Override + public void run() { + if (pInfo.isCustomWidget) { + setState(WIDGET_BOUND); + return; + } + + mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); + if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( + mWidgetLoadingId, pInfo, options)) { + setState(WIDGET_BOUND); + } + } + }; + mHandler.post(mBindWidgetRunnable); + + mInflateWidgetRunnable = new Runnable() { + @Override + public void run() { + if (mState != WIDGET_BOUND) { + return; + } + AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( + (Context) mLauncher, mWidgetLoadingId, pInfo); + info.boundWidget = hostView; + setState(WIDGET_INFLATED); + hostView.setVisibility(View.INVISIBLE); + int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false); + + // We want the first widget layout to be the correct size. This will be important + // for width size reporting to the AppWidgetManager. + DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], + unScaledSize[1]); + lp.x = lp.y = 0; + lp.customPosition = true; + hostView.setLayoutParams(lp); + mLauncher.getDragLayer().addView(hostView); + v.setTag(info); + } + }; + mHandler.post(mInflateWidgetRunnable); + return true; + } + + public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { + Bundle options = null; + Rect rect = new Rect(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect); + Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher, + info.componentName, null); + + float density = launcher.getResources().getDisplayMetrics().density; + int xPaddingDips = (int) ((padding.left + padding.right) / density); + int yPaddingDips = (int) ((padding.top + padding.bottom) / density); + + options = new Bundle(); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, + rect.left - xPaddingDips); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, + rect.top - yPaddingDips); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, + rect.right - xPaddingDips); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, + rect.bottom - yPaddingDips); + } + return options; + } + + private void setState(int state) { + if (DEBUG) { + Log.d(TAG, String.format(" state [%d -> %d]", mState, state)); + } + mState = state; + } +} diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 292a5de20..27a3ea13d 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -30,6 +30,7 @@ import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.Toast; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; @@ -54,15 +55,17 @@ import java.util.ArrayList; /** * The widgets list view container. */ -public class WidgetsContainerView extends FrameLayout implements Insettable, View.OnTouchListener, - View.OnLongClickListener, DragSource{ +public class WidgetsContainerView extends FrameLayout implements Insettable, + View.OnLongClickListener, View.OnClickListener, DragSource{ - private static final String TAG = "WidgetContainerView"; + private static final String TAG = "WidgetsContainerView"; private static final boolean DEBUG = false; /* {@link RecyclerView} will keep following # of views in cache, before recycling. */ private static final int WIDGET_CACHE_SIZE = 2; + private static final int SPRING_MODE_DELAY_MS = 150; + /* Global instances that are used inside this container. */ private Launcher mLauncher; private DragController mDragController; @@ -75,12 +78,13 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie private RecyclerView mView; private WidgetsListAdapter mAdapter; - /* Dragging related. */ - private boolean mDraggingWidget = false; // TODO(hyunyoungs): seems not needed? check! - private Point mLastTouchDownPos = new Point(); + /* Touch handling related member variables. */ + private Toast mWidgetInstructionToast; /* Rendering related. */ private WidgetPreviewLoader mWidgetPreviewLoader; + private WidgetHostViewLoader mWidgetHostViewLoader; + private Rect mPadding = new Rect(); public WidgetsContainerView(Context context) { @@ -95,8 +99,8 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie super(context, attrs, defStyleAttr); mLauncher = (Launcher) context; mDragController = mLauncher.getDragController(); - - mAdapter = new WidgetsListAdapter(context, this, mLauncher, this, mLauncher); + mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher); + mAdapter = new WidgetsListAdapter(context, this, this, mLauncher); mWidgets = new WidgetsModel(context, mAdapter); mAdapter.setWidgetsModel(mWidgets); mIconCache = (LauncherAppState.getInstance()).getIconCache(); @@ -146,12 +150,27 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie // Touch related handling. // + @Override + public void onClick(View v) { + // When we have exited widget tray or are in transition, disregard clicks + if (!mLauncher.isWidgetsViewVisible() + || mLauncher.getWorkspace().isSwitchingState() + || !(v instanceof WidgetCell)) return; + + // Let the user know that they have to long press to add a widget + if (mWidgetInstructionToast != null) { + mWidgetInstructionToast.cancel(); + } + mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add, + Toast.LENGTH_SHORT); + mWidgetInstructionToast.show(); + } + @Override public boolean onLongClick(View v) { if (DEBUG) { Log.d(TAG, String.format("onLonglick [v=%s]", v)); } - // Return early if this is not initiated from a touch if (!v.isInTouchMode()) return false; // When we have exited all apps or are in transition, disregard long clicks @@ -161,7 +180,11 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie Log.d(TAG, String.format("onLonglick dragging enabled?.", v)); if (!mLauncher.isDraggingEnabled()) return false; - return beginDragging(v); + boolean status = beginDragging(v); + if (status) { + mWidgetHostViewLoader.load(v); + } + return status; } private boolean beginDragging(View v) { @@ -174,7 +197,7 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie } // We delay entering spring-loaded mode slightly to make sure the UI - // thready is free of any work. + // thread is free of any work. postDelayed(new Runnable() { @Override public void run() { @@ -184,13 +207,12 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie mLauncher.enterSpringLoadedDragMode(); } } - }, 150); + }, SPRING_MODE_DELAY_MS); return true; } private boolean beginDraggingWidget(WidgetCell v) { - mDraggingWidget = true; // Get the widget preview as the drag representation ImageView image = (ImageView) v.findViewById(R.id.widget_preview); PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); @@ -198,7 +220,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and // we abort the drag. if (image.getDrawable() == null) { - mDraggingWidget = false; return false; } @@ -259,19 +280,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie return true; } - /* - * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent) - */ - @Override - public boolean onTouch(View v, MotionEvent ev) { - Log.d(TAG, String.format("onTouch [MotionEvent=%s]", ev)); - if (ev.getAction() == MotionEvent.ACTION_DOWN || - ev.getAction() == MotionEvent.ACTION_MOVE) { - mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY()); - } - return false; - } - // // Drag related handling methods that implement {@link DragSource} interface. // @@ -340,6 +348,10 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie } d.deferDragViewCleanupPostAnimation = false; } + //TODO(hyunyoungs): if drop fails, this call cleans up correctly. + // However, in rare corner case where drop succeeds but doesn't end up using the widget + // id created by the loader, this finish will leave dangling widget id. + mWidgetHostViewLoader.finish(success); } // @@ -368,5 +380,4 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, Vie } return mWidgetPreviewLoader; } - } \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index afeb2d385..f6ab21eb4 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -56,20 +56,16 @@ public class WidgetsListAdapter extends Adapter { private WidgetsModel mWidgetsModel; private WidgetPreviewLoader mWidgetPreviewLoader; - private View.OnTouchListener mTouchListener; private View.OnClickListener mIconClickListener; private View.OnLongClickListener mIconLongClickListener; - public WidgetsListAdapter(Context context, - View.OnTouchListener touchListener, View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener, Launcher launcher) { mLayoutInflater = LayoutInflater.from(context); mContext = context; - mTouchListener = touchListener; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; @@ -109,7 +105,6 @@ public class WidgetsListAdapter extends Adapter { // set up touch. widget.setOnClickListener(mIconClickListener); widget.setOnLongClickListener(mIconLongClickListener); - widget.setOnTouchListener(mTouchListener); row.addView(widget); } } else if (diff < 0) { -- cgit v1.2.3 From d33860f2cc027b91dd09cc38ddd0e37878ba6c69 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 23 Apr 2015 16:02:20 -0700 Subject: Clearing all pending runnables when Launcher is destroyed. Change-Id: I00596c11116b5579c1f013b268b6c0b5239f0aa7 --- src/com/android/launcher3/DeferredHandler.java | 47 +++++------------------- src/com/android/launcher3/Launcher.java | 6 ---- src/com/android/launcher3/LauncherModel.java | 49 ++++++++++---------------- 3 files changed, 28 insertions(+), 74 deletions(-) diff --git a/src/com/android/launcher3/DeferredHandler.java b/src/com/android/launcher3/DeferredHandler.java index eb7c26a28..a43ab6723 100644 --- a/src/com/android/launcher3/DeferredHandler.java +++ b/src/com/android/launcher3/DeferredHandler.java @@ -20,12 +20,10 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; -import android.util.Pair; import com.android.launcher3.util.Thunk; import java.util.LinkedList; -import java.util.ListIterator; /** * Queue of things to run on a looper thread. Items posted with {@link #post} will not @@ -35,20 +33,18 @@ import java.util.ListIterator; * This class is fifo. */ public class DeferredHandler { - @Thunk LinkedList> mQueue = new LinkedList>(); + @Thunk LinkedList mQueue = new LinkedList<>(); private MessageQueue mMessageQueue = Looper.myQueue(); private Impl mHandler = new Impl(); @Thunk class Impl extends Handler implements MessageQueue.IdleHandler { public void handleMessage(Message msg) { - Pair p; Runnable r; synchronized (mQueue) { if (mQueue.size() == 0) { return; } - p = mQueue.removeFirst(); - r = p.first; + r = mQueue.removeFirst(); } r.run(); synchronized (mQueue) { @@ -79,11 +75,8 @@ public class DeferredHandler { /** Schedule runnable to run after everything that's on the queue right now. */ public void post(Runnable runnable) { - post(runnable, 0); - } - public void post(Runnable runnable, int type) { synchronized (mQueue) { - mQueue.add(new Pair(runnable, type)); + mQueue.add(runnable); if (mQueue.size() == 1) { scheduleNextLocked(); } @@ -92,31 +85,10 @@ public class DeferredHandler { /** Schedule runnable to run when the queue goes idle. */ public void postIdle(final Runnable runnable) { - postIdle(runnable, 0); - } - public void postIdle(final Runnable runnable, int type) { - post(new IdleRunnable(runnable), type); - } - - public void cancelRunnable(Runnable runnable) { - synchronized (mQueue) { - while (mQueue.remove(runnable)) { } - } - } - public void cancelAllRunnablesOfType(int type) { - synchronized (mQueue) { - ListIterator> iter = mQueue.listIterator(); - Pair p; - while (iter.hasNext()) { - p = iter.next(); - if (p.second == type) { - iter.remove(); - } - } - } + post(new IdleRunnable(runnable)); } - public void cancel() { + public void cancelAll() { synchronized (mQueue) { mQueue.clear(); } @@ -124,20 +96,19 @@ public class DeferredHandler { /** Runs all queued Runnables from the calling thread. */ public void flush() { - LinkedList> queue = new LinkedList>(); + LinkedList queue = new LinkedList<>(); synchronized (mQueue) { queue.addAll(mQueue); mQueue.clear(); } - for (Pair p : queue) { - p.first.run(); + for (Runnable r : queue) { + r.run(); } } void scheduleNextLocked() { if (mQueue.size() > 0) { - Pair p = mQueue.getFirst(); - Runnable peek = p.first; + Runnable peek = mQueue.getFirst(); if (peek instanceof IdleRunnable) { mMessageQueue.addIdleHandler(mHandler); } else { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 20844777f..7220e0258 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2047,12 +2047,6 @@ public class Launcher extends Activity TextKeyListener.getInstance().release(); - // Disconnect any of the callbacks and drawables associated with ItemInfos on the workspace - // to prevent leaking Launcher activities on orientation change. - if (mModel != null) { - mModel.unbindItemInfosAndClearQueuedBindRunnables(); - } - getContentResolver().unregisterContentObserver(mWidgetObserver); unregisterReceiver(mCloseSystemDialogsReceiver); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 5a65cab82..a5703a262 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -28,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -109,11 +108,6 @@ public class LauncherModel extends BroadcastReceiver @Thunk LoaderTask mLoaderTask; @Thunk boolean mIsLoaderTaskRunning; - // Specific runnable types that are run on the main thread deferred handler, this allows us to - // clear all queued binding runnables when the Launcher activity is destroyed. - private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0; - private static final int MAIN_THREAD_BINDING_RUNNABLE = 1; - private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings"; @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); @@ -256,9 +250,6 @@ public class LauncherModel extends BroadcastReceiver /** Runs the specified runnable immediately if called from the main thread, otherwise it is * posted on the main thread handler. */ @Thunk void runOnMainThread(Runnable r) { - runOnMainThread(r, 0); - } - @Thunk void runOnMainThread(Runnable r, int type) { if (sWorkerThread.getThreadId() == Process.myTid()) { // If we are on the worker thread, post onto the main handler mHandler.post(r); @@ -679,7 +670,7 @@ public class LauncherModel extends BroadcastReceiver runOnWorkerThread(r); } - public void unbindItemInfosAndClearQueuedBindRunnables() { + private void unbindItemInfosAndClearQueuedBindRunnables() { if (sWorkerThread.getThreadId() == Process.myTid()) { throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " + "main thread"); @@ -689,8 +680,9 @@ public class LauncherModel extends BroadcastReceiver synchronized (mDeferredBindRunnables) { mDeferredBindRunnables.clear(); } - // Remove any queued bind runnables - mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE); + + // Remove any queued UI runnables + mHandler.cancelAll(); // Unbind all the workspace items unbindWorkspaceItemsOnMainThread(); } @@ -699,19 +691,15 @@ public class LauncherModel extends BroadcastReceiver void unbindWorkspaceItemsOnMainThread() { // Ensure that we don't use the same workspace items data structure on the main thread // by making a copy of workspace items first. - final ArrayList tmpWorkspaceItems = new ArrayList(); - final ArrayList tmpAppWidgets = new ArrayList(); + final ArrayList tmpItems = new ArrayList(); synchronized (sBgLock) { - tmpWorkspaceItems.addAll(sBgWorkspaceItems); - tmpAppWidgets.addAll(sBgAppWidgets); + tmpItems.addAll(sBgWorkspaceItems); + tmpItems.addAll(sBgAppWidgets); } Runnable r = new Runnable() { @Override public void run() { - for (ItemInfo item : tmpWorkspaceItems) { - item.unbind(); - } - for (ItemInfo item : tmpAppWidgets) { + for (ItemInfo item : tmpItems) { item.unbind(); } } @@ -1297,6 +1285,9 @@ public class LauncherModel extends BroadcastReceiver */ public void initialize(Callbacks callbacks) { synchronized (mLock) { + // Disconnect any of the callbacks and drawables associated with ItemInfos on the + // workspace to prevent leaking Launcher activities on orientation change. + unbindItemInfosAndClearQueuedBindRunnables(); mCallbacks = new WeakReference(callbacks); } } @@ -1486,7 +1477,7 @@ public class LauncherModel extends BroadcastReceiver mDeferredBindRunnables.clear(); } for (final Runnable r : deferredBindRunnables) { - mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE); + mHandler.post(r); } } } @@ -2619,7 +2610,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); } private void bindWorkspaceItems(final Callbacks oldCallbacks, @@ -2650,7 +2641,7 @@ public class LauncherModel extends BroadcastReceiver deferredBindRunnables.add(r); } } else { - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); } } @@ -2669,7 +2660,7 @@ public class LauncherModel extends BroadcastReceiver deferredBindRunnables.add(r); } } else { - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); } } @@ -2688,7 +2679,7 @@ public class LauncherModel extends BroadcastReceiver if (postOnMainThread) { deferredBindRunnables.add(r); } else { - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); } } } @@ -2768,7 +2759,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); bindWorkspaceScreens(oldCallbacks, orderedScreenIds); @@ -2784,7 +2775,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); } // Load all the remaining pages (if we are loading synchronously, we want to defer this @@ -2817,7 +2808,7 @@ public class LauncherModel extends BroadcastReceiver mDeferredBindRunnables.add(r); } } else { - runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); + runOnMainThread(r); } } @@ -2888,8 +2879,6 @@ public class LauncherModel extends BroadcastReceiver // Clear the list of apps mBgAllAppsList.clear(); - SharedPreferences prefs = mContext.getSharedPreferences( - LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); for (UserHandleCompat user : profiles) { // Query for the set of apps final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; -- cgit v1.2.3 From b8c663c492d32963b6ee33750fc985f037c58f10 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 23 Apr 2015 11:43:48 -0700 Subject: Moving item to its old position in folder if DnD is cancelled Change-Id: I6362ef23b346409e8517a03e63dee974294a3a18 --- src/com/android/launcher3/Folder.java | 11 ++++++++++- src/com/android/launcher3/FolderPagedView.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index c35ce944f..dff47c256 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -745,9 +745,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList replaceFolderWithFinalItem(); } } else { - rearrangeChildren(); // The drag failed, we need to return the item to the folder + ShortcutInfo info = (ShortcutInfo) d.dragInfo; + View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info) + ? mCurrentDragView : mContent.createNewView(info); + ArrayList views = getItemsInReadingOrder(); + views.add(info.rank, icon); + mContent.arrangeChildren(views, views.size()); + mItemsInvalidated = true; + + mSuppressOnAdd = true; mFolderIcon.onDrop(d); + mSuppressOnAdd = false; } if (target != this) { diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 617489271..3f08f43c2 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -363,7 +363,7 @@ public class FolderPagedView extends PagedView { } @SuppressLint("InflateParams") - private View createNewView(ShortcutInfo item) { + public View createNewView(ShortcutInfo item) { final BubbleTextView textView = (BubbleTextView) mInflater.inflate( R.layout.folder_application, null, false); textView.applyFromShortcutInfo(item, mIconCache, false); -- cgit v1.2.3 From 1a70cef9884270f2f0a760f079a10fdfb1544c98 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 22 Apr 2015 11:29:51 -0700 Subject: Accessibility fixes > Enabling top bar buttons in accessibility drag-drop > Unifying logic to show delete/uninstall/app-info targets > Announcing cell loction as 1-index instead of 0-index Change-Id: Ibc7801f77e938b2646f0655462cbe9b7f781818b --- res/values/strings.xml | 4 +- src/com/android/launcher3/ButtonDropTarget.java | 19 +++++++- src/com/android/launcher3/CellLayout.java | 2 +- src/com/android/launcher3/DeleteDropTarget.java | 14 ++++-- src/com/android/launcher3/DragController.java | 5 +- src/com/android/launcher3/InfoDropTarget.java | 11 +++-- .../launcher3/LauncherAccessibilityDelegate.java | 53 +++++++++++++--------- src/com/android/launcher3/SearchDropTargetBar.java | 15 ++++++ src/com/android/launcher3/UninstallDropTarget.java | 17 +++++-- src/com/android/launcher3/Workspace.java | 1 + 10 files changed, 99 insertions(+), 42 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 52306e30e..bfe7e36f2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -309,13 +309,13 @@ s --> - Add To Workspace + Add to workspace Item added to workspace - Item removed from workspace + Item removed Move Item diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 5b399087a..fb49df5df 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -26,18 +26,19 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.util.AttributeSet; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import android.widget.TextView; -import com.android.launcher3.R; import com.android.launcher3.util.Thunk; /** * Implements a DropTarget. */ -public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener { +public abstract class ButtonDropTarget extends TextView + implements DropTarget, DragController.DragListener, OnClickListener { private static int DRAG_VIEW_DROP_DURATION = 285; @@ -256,4 +257,18 @@ public abstract class ButtonDropTarget extends TextView implements DropTarget, D public void getLocationInDragLayer(int[] loc) { mLauncher.getDragLayer().getLocationInDragLayer(this, loc); } + + public void enableAccessibleDrag(boolean enable) { + setOnClickListener(enable ? this : null); + } + + protected String getAccessibilityDropConfirmation() { + return null; + } + + @Override + public void onClick(View v) { + LauncherAppState.getInstance().getAccessibilityDelegate() + .handleAccessibleDrop(this, null, getAccessibilityDropConfirmation()); + } } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index f4afb954d..f08f25fb2 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -557,7 +557,7 @@ public class CellLayout extends ViewGroup { Resources res = getContext().getResources(); View child = getChildAt(x, y); if (child == null || child == dragInfo.item) { - return res.getString(R.string.move_to_empty_cell, x, y); + return res.getString(R.string.move_to_empty_cell, x + 1, y + 1); } else { ItemInfo info = (ItemInfo) child.getTag(); if (info instanceof AppInfo || info instanceof ShortcutInfo) { diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index aa3e66c09..e741b9787 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -29,7 +29,6 @@ import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; -import com.android.launcher3.R; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetsContainerView; @@ -59,13 +58,15 @@ public class DeleteDropTarget extends ButtonDropTarget { setDrawable(R.drawable.remove_target_selector); } - public static boolean willAcceptDrop(DragSource source, Object info) { - return (info instanceof ItemInfo) && source.supportsDeleteDropTarget(); + public static boolean supportsDrop(Object info) { + return (info instanceof ShortcutInfo) + || (info instanceof LauncherAppWidgetInfo) + || (info instanceof FolderInfo); } @Override protected boolean supportsDrop(DragSource source, Object info) { - return willAcceptDrop(source, info); + return source.supportsDeleteDropTarget() && supportsDrop(info); } @Override @@ -304,4 +305,9 @@ public class DeleteDropTarget extends ButtonDropTarget { dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); } + + @Override + protected String getAccessibilityDropConfirmation() { + return getResources().getString(R.string.item_removed); + } } diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index b24608cb1..196886895 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -462,8 +462,7 @@ public class DragController { mLastTouchUpTime = System.currentTimeMillis(); if (mDragging) { PointF vec = isFlingingToDelete(mDragObject.dragSource); - if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource, - mDragObject.dragInfo)) { + if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) { vec = null; } if (vec != null) { @@ -617,7 +616,7 @@ public class DragController { if (mDragging) { PointF vec = isFlingingToDelete(mDragObject.dragSource); - if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource, mDragObject.dragInfo)) { + if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) { vec = null; } if (vec != null) { diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index e48640c93..f1ff48da3 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -21,7 +21,6 @@ import android.content.Context; import android.provider.Settings; import android.util.AttributeSet; -import com.android.launcher3.R; import com.android.launcher3.compat.UserHandleCompat; public class InfoDropTarget extends ButtonDropTarget { @@ -66,9 +65,13 @@ public class InfoDropTarget extends ButtonDropTarget { @Override protected boolean supportsDrop(DragSource source, Object info) { - return source.supportsAppInfoDropTarget() && - Settings.Global.getInt(getContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1; + return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info); + } + + public static boolean supportsDrop(Context context, Object info) { + return (Settings.Global.getInt(context.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) && + (info instanceof AppInfo || info instanceof PendingAddItemInfo); } @Override diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index 8ba02ea5f..a60e16024 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -1,16 +1,13 @@ package com.android.launcher3; import android.annotation.TargetApi; -import android.content.res.Resources; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; -import android.support.v4.view.accessibility.AccessibilityEventCompat; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.text.TextUtils; import android.util.SparseArray; import android.view.View; import android.view.View.AccessibilityDelegate; -import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -68,18 +65,21 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { if (!(host.getTag() instanceof ItemInfo)) return; ItemInfo item = (ItemInfo) host.getTag(); + if (DeleteDropTarget.supportsDrop(item)) { + info.addAction(mActions.get(REMOVE)); + } + if (UninstallDropTarget.supportsDrop(host.getContext(), item)) { + info.addAction(mActions.get(UNINSTALL)); + } + if (InfoDropTarget.supportsDrop(host.getContext(), item)) { + info.addAction(mActions.get(INFO)); + } + if ((item instanceof ShortcutInfo) || (item instanceof LauncherAppWidgetInfo) || (item instanceof FolderInfo)) { - // Workspace shortcut / widget - info.addAction(mActions.get(REMOVE)); info.addAction(mActions.get(MOVE)); - } else if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { - // App or Widget from customization tray - if (item instanceof AppInfo) { - info.addAction(mActions.get(UNINSTALL)); - } - info.addAction(mActions.get(INFO)); + } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { info.addAction(mActions.get(ADD_TO_WORKSPACE)); } } @@ -94,10 +94,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { } public boolean performAction(View host, ItemInfo item, int action) { - Resources res = mLauncher.getResources(); if (action == REMOVE) { if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) { - announceConfirmation(R.string.item_removed_from_workspace); + announceConfirmation(R.string.item_removed); return true; } return false; @@ -105,9 +104,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); return true; } else if (action == UNINSTALL) { - AppInfo info = (AppInfo) item; - mLauncher.startApplicationUninstallActivity(info.componentName, info.flags, info.user); - return true; + return UninstallDropTarget.startUninstallActivity(mLauncher, item); } else if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { @@ -158,19 +155,31 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { return mDragInfo; } - public void handleAccessibleDrop(CellLayout targetContainer, Rect dropLocation, + /** + * @param clickedTarget the actual view that was clicked + * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used + * as the actual drop location otherwise the views center is used. + */ + public void handleAccessibleDrop(View clickedTarget, Rect dropLocation, String confirmation) { if (!isInAccessibleDrag()) return; int[] loc = new int[2]; - loc[0] = dropLocation.centerX(); - loc[1] = dropLocation.centerY(); + if (dropLocation == null) { + loc[0] = clickedTarget.getWidth() / 2; + loc[1] = clickedTarget.getHeight() / 2; + } else { + loc[0] = dropLocation.centerX(); + loc[1] = dropLocation.centerY(); + } - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(targetContainer, loc); + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc); mLauncher.getDragController().completeAccessibleDrag(loc); endAccessibleDrag(); - announceConfirmation(confirmation); + if (!TextUtils.isEmpty(confirmation)) { + announceConfirmation(confirmation); + } } public void beginAccessibleDrag(View item, ItemInfo info) { diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index af8bc7580..44a76b73b 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -180,6 +180,9 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D prepareStartAnimation(mDropTargetBar); mShowDropTargetBarAnim.start(); hideSearchBar(true); + if (mQSBSearchBar != null) { + mQSBSearchBar.setVisibility(View.GONE); + } } /** @@ -190,6 +193,9 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D prepareStartAnimation(mDropTargetBar); mShowDropTargetBarAnim.reverse(); showSearchBar(true); + if (mQSBSearchBar != null) { + mQSBSearchBar.setVisibility(View.VISIBLE); + } } /* @@ -228,4 +234,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D return null; } } + + public void enableAccessibleDrag(boolean enable) { + if (mQSBSearchBar != null) { + mQSBSearchBar.setVisibility(enable ? View.GONE : View.VISIBLE); + } + mInfoDropTarget.enableAccessibleDrag(enable); + mDeleteDropTarget.enableAccessibleDrag(enable); + mUninstallDropTarget.enableAccessibleDrag(enable); + } } diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 4a7fffeb2..4c52d7e0f 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -33,9 +33,12 @@ public class UninstallDropTarget extends ButtonDropTarget { @Override protected boolean supportsDrop(DragSource source, Object info) { + return supportsDrop(getContext(), info); + } + + public static boolean supportsDrop(Context context, Object info) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - UserManager userManager = (UserManager) - getContext().getSystemService(Context.USER_SERVICE); + UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); Bundle restrictions = userManager.getUserRestrictions(); if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) { @@ -78,8 +81,7 @@ public class UninstallDropTarget extends ButtonDropTarget { void completeDrop(final DragObject d) { final Pair componentInfo = getAppInfoFlags(d.dragInfo); final UserHandleCompat user = ((ItemInfo) d.dragInfo).user; - if (mLauncher.startApplicationUninstallActivity( - componentInfo.first, componentInfo.second, user)) { + if (startUninstallActivity(mLauncher, d.dragInfo)) { final Runnable checkIfUninstallWasSuccess = new Runnable() { @Override @@ -96,6 +98,13 @@ public class UninstallDropTarget extends ButtonDropTarget { } } + public static boolean startUninstallActivity(Launcher launcher, Object info) { + final Pair componentInfo = getAppInfoFlags(info); + final UserHandleCompat user = ((ItemInfo) info).user; + return launcher.startApplicationUninstallActivity( + componentInfo.first, componentInfo.second, user); + } + @Thunk void sendUninstallResult(DragSource target, boolean result) { if (target instanceof UninstallSource) { ((UninstallSource) target).onUninstallActivityReturned(result); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index abb8489fd..0ae4ffa22 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1615,6 +1615,7 @@ public class Workspace extends SmoothPagedView // Reset our click listener setOnClickListener(mLauncher); } + mLauncher.getSearchBar().enableAccessibleDrag(enable); mLauncher.getHotseat().getLayout().enableAccessibleDrag(enable); } -- cgit v1.2.3 From e2df0620c13b9dc7e63224153b98fdbb48546f9b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 24 Apr 2015 11:27:00 -0700 Subject: Adding LongArrayMap as a replacement for HashMap Change-Id: I4710e6db69abcdbd897a8401fc8b980c09e2ffef --- src/com/android/launcher3/Launcher.java | 8 +-- src/com/android/launcher3/LauncherModel.java | 74 +++++++++++++----------- src/com/android/launcher3/Workspace.java | 19 +++--- src/com/android/launcher3/util/LongArrayMap.java | 65 +++++++++++++++++++++ 4 files changed, 118 insertions(+), 48 deletions(-) create mode 100644 src/com/android/launcher3/util/LongArrayMap.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7220e0258..b936c1fd5 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -100,6 +100,7 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetsContainerView; @@ -302,7 +303,7 @@ public class Launcher extends Activity @Thunk static LocaleConfiguration sLocaleConfiguration = null; - private static HashMap sFolders = new HashMap(); + private static LongArrayMap sFolders = new LongArrayMap<>(); private View.OnTouchListener mHapticFeedbackTouchListener; @@ -3886,7 +3887,7 @@ public class Launcher extends Activity /** * Implementation of the method from LauncherModel.Callbacks. */ - public void bindFolders(final HashMap folders) { + public void bindFolders(final LongArrayMap folders) { Runnable r = new Runnable() { public void run() { bindFolders(folders); @@ -3895,8 +3896,7 @@ public class Launcher extends Activity if (waitUntilResume(r)) { return; } - sFolders.clear(); - sFolders.putAll(folders); + sFolders = folders.clone(); } /** diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index a5703a262..d5dce51df 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -60,6 +60,7 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.Thunk; @@ -141,7 +142,7 @@ public class LauncherModel extends BroadcastReceiver // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by // LauncherModel to their ids - static final HashMap sBgItemsIdMap = new HashMap(); + static final LongArrayMap sBgItemsIdMap = new LongArrayMap<>(); // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts // created by LauncherModel that are directly on the home screen (however, no widgets or @@ -153,7 +154,7 @@ public class LauncherModel extends BroadcastReceiver new ArrayList(); // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders() - static final HashMap sBgFolders = new HashMap(); + static final LongArrayMap sBgFolders = new LongArrayMap<>(); // sBgWorkspaceScreens is the ordered set of workspace screens. static final ArrayList sBgWorkspaceScreens = new ArrayList(); @@ -182,7 +183,7 @@ public class LauncherModel extends BroadcastReceiver boolean forceAnimateIcons); public void bindScreens(ArrayList orderedScreenIds); public void bindAddScreens(ArrayList orderedScreenIds); - public void bindFolders(HashMap folders); + public void bindFolders(LongArrayMap folders); public void finishBindingItems(); public void bindAppWidget(LauncherAppWidgetInfo info); public void bindAllApplications(ArrayList apps); @@ -286,7 +287,7 @@ public class LauncherModel extends BroadcastReceiver return; } - for (ItemInfo info : sBgItemsIdMap.values()) { + for (ItemInfo info : sBgItemsIdMap) { if (info instanceof ShortcutInfo) { ShortcutInfo si = (ShortcutInfo) info; ComponentName cn = si.getTargetComponent(); @@ -340,7 +341,7 @@ public class LauncherModel extends BroadcastReceiver final ArrayList updates = new ArrayList<>(); final UserHandleCompat user = UserHandleCompat.myUserHandle(); - for (ItemInfo info : sBgItemsIdMap.values()) { + for (ItemInfo info : sBgItemsIdMap) { if (info instanceof ShortcutInfo) { ShortcutInfo si = (ShortcutInfo) info; ComponentName cn = si.getTargetComponent(); @@ -996,7 +997,7 @@ public class LauncherModel extends BroadcastReceiver } synchronized (sBgLock) { - for (ItemInfo item : sBgItemsIdMap.values()) { + for (ItemInfo item : sBgItemsIdMap) { if (item instanceof ShortcutInfo) { ShortcutInfo info = (ShortcutInfo) item; if (intentWithPkg.equals(info.getIntent()) @@ -1012,7 +1013,7 @@ public class LauncherModel extends BroadcastReceiver /** * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList. */ - FolderInfo getFolderById(Context context, HashMap folderList, long id) { + FolderInfo getFolderById(Context context, LongArrayMap folderList, long id) { final ContentResolver cr = context.getContentResolver(); Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null, "_id=? and (itemType=? or itemType=?)", @@ -1132,7 +1133,7 @@ public class LauncherModel extends BroadcastReceiver return cn.getPackageName().equals(pn) && info.user.equals(user); } }; - return filterItemInfos(sBgItemsIdMap.values(), filter); + return filterItemInfos(sBgItemsIdMap, filter); } /** @@ -1172,7 +1173,7 @@ public class LauncherModel extends BroadcastReceiver switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: sBgFolders.remove(item.id); - for (ItemInfo info: sBgItemsIdMap.values()) { + for (ItemInfo info: sBgItemsIdMap) { if (info.container == item.id) { // We are deleting a folder which still contains items that // think they are contained by that folder. @@ -1749,7 +1750,7 @@ public class LauncherModel extends BroadcastReceiver } // check & update map of what's occupied; used to discard overlapping/invalid items - private boolean checkItemPlacement(HashMap occupied, ItemInfo item) { + private boolean checkItemPlacement(LongArrayMap occupied, ItemInfo item) { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); final int countX = (int) grid.numColumns; @@ -1897,7 +1898,7 @@ public class LauncherModel extends BroadcastReceiver // +1 for the hotseat (it can be larger than the workspace) // Load workspace in reverse order to ensure that latest items are loaded first (and // before any earlier duplicates) - final HashMap occupied = new HashMap(); + final LongArrayMap occupied = new LongArrayMap<>(); try { final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); @@ -2436,7 +2437,7 @@ public class LauncherModel extends BroadcastReceiver // Remove any empty screens ArrayList unusedScreens = new ArrayList(sBgWorkspaceScreens); - for (ItemInfo item: sBgItemsIdMap.values()) { + for (ItemInfo item: sBgItemsIdMap) { long screenId = item.screenId; if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && unusedScreens.contains(screenId)) { @@ -2461,14 +2462,13 @@ public class LauncherModel extends BroadcastReceiver for (int y = 0; y < countY; y++) { String line = ""; - Iterator iter = occupied.keySet().iterator(); - while (iter.hasNext()) { - long screenId = iter.next(); + for (int i = 0; i < nScreens; i++) { + long screenId = occupied.keyAt(i); if (screenId > 0) { line += " | "; } + ItemInfo[][] screen = occupied.valueAt(i); for (int x = 0; x < countX; x++) { - ItemInfo[][] screen = occupied.get(screenId); if (x < screen.length && y < screen[x].length) { line += (screen[x][y] != null) ? "#" : "."; } else { @@ -2559,14 +2559,17 @@ public class LauncherModel extends BroadcastReceiver /** Filters the set of folders which are on the specified screen. */ private void filterCurrentFolders(long currentScreenId, - HashMap itemsIdMap, - HashMap folders, - HashMap currentScreenFolders, - HashMap otherScreenFolders) { + LongArrayMap itemsIdMap, + LongArrayMap folders, + LongArrayMap currentScreenFolders, + LongArrayMap otherScreenFolders) { + + int total = folders.size(); + for (int i = 0; i < total; i++) { + long id = folders.keyAt(i); + FolderInfo folder = folders.valueAt(i); - for (long id : folders.keySet()) { ItemInfo info = itemsIdMap.get(id); - FolderInfo folder = folders.get(id); if (info == null || folder == null) continue; if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && info.screenId == currentScreenId) { @@ -2616,7 +2619,7 @@ public class LauncherModel extends BroadcastReceiver private void bindWorkspaceItems(final Callbacks oldCallbacks, final ArrayList workspaceItems, final ArrayList appWidgets, - final HashMap folders, + final LongArrayMap folders, ArrayList deferredBindRunnables) { final boolean postOnMainThread = (deferredBindRunnables != null); @@ -2704,15 +2707,18 @@ public class LauncherModel extends BroadcastReceiver ArrayList workspaceItems = new ArrayList(); ArrayList appWidgets = new ArrayList(); - HashMap folders = new HashMap(); - HashMap itemsIdMap = new HashMap(); ArrayList orderedScreenIds = new ArrayList(); + + final LongArrayMap folders; + final LongArrayMap itemsIdMap; + synchronized (sBgLock) { workspaceItems.addAll(sBgWorkspaceItems); appWidgets.addAll(sBgAppWidgets); - folders.putAll(sBgFolders); - itemsIdMap.putAll(sBgItemsIdMap); orderedScreenIds.addAll(sBgWorkspaceScreens); + + folders = sBgFolders.clone(); + itemsIdMap = sBgItemsIdMap.clone(); } final boolean isLoadingSynchronously = @@ -2738,8 +2744,8 @@ public class LauncherModel extends BroadcastReceiver new ArrayList(); ArrayList otherAppWidgets = new ArrayList(); - HashMap currentFolders = new HashMap(); - HashMap otherFolders = new HashMap(); + LongArrayMap currentFolders = new LongArrayMap<>(); + LongArrayMap otherFolders = new LongArrayMap<>(); filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); @@ -2902,7 +2908,7 @@ public class LauncherModel extends BroadcastReceiver if (!updatedPackages.isEmpty()) { final ArrayList updates = new ArrayList(); synchronized (sBgLock) { - for (ItemInfo info : sBgItemsIdMap.values()) { + for (ItemInfo info : sBgItemsIdMap) { if (info instanceof ShortcutInfo && user.equals(info.user) && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { ShortcutInfo si = (ShortcutInfo) info; @@ -3151,7 +3157,7 @@ public class LauncherModel extends BroadcastReceiver HashSet packageSet = new HashSet(Arrays.asList(packages)); synchronized (sBgLock) { - for (ItemInfo info : sBgItemsIdMap.values()) { + for (ItemInfo info : sBgItemsIdMap) { if (info instanceof ShortcutInfo && mUser.equals(info.user)) { ShortcutInfo si = (ShortcutInfo) info; boolean infoUpdated = false; @@ -3527,7 +3533,7 @@ public class LauncherModel extends BroadcastReceiver return info; } - static ArrayList filterItemInfos(Collection infos, + static ArrayList filterItemInfos(Iterable infos, ItemInfoFilter f) { HashSet filtered = new HashSet(); for (ItemInfo i : infos) { @@ -3568,7 +3574,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - return filterItemInfos(sBgItemsIdMap.values(), filter); + return filterItemInfos(sBgItemsIdMap, filter); } /** @@ -3678,7 +3684,7 @@ public class LauncherModel extends BroadcastReceiver * Return an existing FolderInfo object if we have encountered this ID previously, * or make a new one. */ - @Thunk static FolderInfo findOrMakeFolder(HashMap folders, long id) { + @Thunk static FolderInfo findOrMakeFolder(LongArrayMap folders, long id) { // See if a placeholder was created for us already FolderInfo folderInfo = folders.get(id); if (folderInfo == null) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 0ae4ffa22..2efd20739 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -63,6 +63,7 @@ import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; import com.android.launcher3.widget.PendingAddShortcutInfo; @@ -71,7 +72,6 @@ import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.concurrent.atomic.AtomicInteger; /** @@ -120,7 +120,7 @@ public class Workspace extends SmoothPagedView final static long EXTRA_EMPTY_SCREEN_ID = -201; private final static long CUSTOM_CONTENT_SCREEN_ID = -301; - @Thunk HashMap mWorkspaceScreens = new HashMap(); + @Thunk LongArrayMap mWorkspaceScreens = new LongArrayMap<>(); @Thunk ArrayList mScreenOrder = new ArrayList(); @Thunk Runnable mRemoveEmptyScreenRunnable; @@ -834,12 +834,9 @@ public class Workspace extends SmoothPagedView } public long getIdForScreen(CellLayout layout) { - Iterator iter = mWorkspaceScreens.keySet().iterator(); - while (iter.hasNext()) { - long id = iter.next(); - if (mWorkspaceScreens.get(id) == layout) { - return id; - } + int index = mWorkspaceScreens.indexOfValue(layout); + if (index != -1) { + return mWorkspaceScreens.keyAt(index); } return -1; } @@ -874,8 +871,10 @@ public class Workspace extends SmoothPagedView int currentPage = getNextPage(); ArrayList removeScreens = new ArrayList(); - for (Long id: mWorkspaceScreens.keySet()) { - CellLayout cl = mWorkspaceScreens.get(id); + int total = mWorkspaceScreens.size(); + for (int i = 0; i < total; i++) { + long id = mWorkspaceScreens.keyAt(i); + CellLayout cl = mWorkspaceScreens.valueAt(i); if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) { removeScreens.add(id); } diff --git a/src/com/android/launcher3/util/LongArrayMap.java b/src/com/android/launcher3/util/LongArrayMap.java new file mode 100644 index 000000000..e3c96cd42 --- /dev/null +++ b/src/com/android/launcher3/util/LongArrayMap.java @@ -0,0 +1,65 @@ +/* + * 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.util; + +import android.util.LongSparseArray; + +import java.util.Iterator; + +/** + * Extension of {@link LongSparseArray} with some utility methods. + */ +public class LongArrayMap extends LongSparseArray implements Iterable { + + public boolean containsKey(long key) { + return indexOfKey(key) >= 0; + } + + public boolean isEmpty() { + return size() <= 0; + } + + @Override + public LongArrayMap clone() { + return (LongArrayMap) super.clone(); + } + + @Override + public Iterator iterator() { + return new ValueIterator(); + } + + private class ValueIterator implements Iterator { + + private int mNextIndex = 0; + + @Override + public boolean hasNext() { + return mNextIndex < size(); + } + + @Override + public E next() { + return valueAt(mNextIndex ++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} -- cgit v1.2.3 From ba17ad75f001d19b4ae43e9401a41b8d18f02a6e Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 24 Apr 2015 13:59:17 -0700 Subject: Add override method to fix building against master support lib. --- src/com/android/launcher3/AppsContainerRecyclerView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 16244ee35..f8897128e 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -156,6 +156,10 @@ public class AppsContainerRecyclerView extends RecyclerView handleTouchEvent(ev); } + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // Do nothing + } + /** * Handles the touch event and determines whether to show the fast scroller (or updates it if * it is already showing). -- cgit v1.2.3 From f7a29e83f06909b378dba39c83a522375682710a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 24 Apr 2015 15:20:43 -0700 Subject: Removing some unused method params from CellLayout > Replacing various distance calculations with Math.hypot > Moving findVacantCell to Utilities Change-Id: I0cb194b603e52b3bb2b29a095bb4da2bb408ab13 --- src/com/android/launcher3/CellLayout.java | 358 +++------------------ src/com/android/launcher3/DeviceProfile.java | 3 +- src/com/android/launcher3/DragController.java | 3 +- src/com/android/launcher3/DragLayer.java | 3 +- src/com/android/launcher3/Folder.java | 2 +- src/com/android/launcher3/FolderPagedView.java | 2 +- src/com/android/launcher3/LauncherModel.java | 2 +- .../LauncherStateTransitionAnimation.java | 4 +- .../launcher3/ShortcutAndWidgetContainer.java | 8 - src/com/android/launcher3/Utilities.java | 33 ++ 10 files changed, 81 insertions(+), 337 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index f08f25fb2..85653bef7 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -48,9 +48,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; -import android.view.animation.LayoutAnimationController; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.LauncherAccessibilityDelegate.DragType; @@ -121,7 +119,6 @@ public class CellLayout extends ViewGroup { // If we're actively dragging something over this screen, mIsDragOverlapping is true private boolean mIsDragOverlapping = false; - boolean mUseActiveGlowBackground = false; // These arrays are used to implement the drag visualization on x-large screens. // They are used as circular arrays, indexed by mDragOutlineCurrent. @@ -684,10 +681,6 @@ public class CellLayout extends ViewGroup { } } - void setUseActiveGlowBackground(boolean use) { - mUseActiveGlowBackground = use; - } - void disableBackground() { mDrawBackground = false; } @@ -703,7 +696,6 @@ public class CellLayout extends ViewGroup { void setIsDragOverlapping(boolean isDragOverlapping) { if (mIsDragOverlapping != isDragOverlapping) { mIsDragOverlapping = isDragOverlapping; - setUseActiveGlowBackground(mIsDragOverlapping); invalidate(); } } @@ -722,7 +714,7 @@ public class CellLayout extends ViewGroup { if (mDrawBackground && mBackgroundAlpha > 0.0f) { Drawable bg; - if (mUseActiveGlowBackground) { + if (mIsDragOverlapping) { // In the mini case, we draw the active_glow bg *over* the active background bg = mActiveGlowBackground; } else { @@ -906,7 +898,7 @@ public class CellLayout extends ViewGroup { } public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params, - boolean markCells, boolean inLayout) { + boolean markCells) { final LayoutParams lp = params; // Hotseat icons - remove text @@ -927,7 +919,7 @@ public class CellLayout extends ViewGroup { if (lp.cellVSpan < 0) lp.cellVSpan = mCountY; child.setId(childId); - mShortcutsAndWidgets.addView(child, index, lp, inLayout); + mShortcutsAndWidgets.addView(child, index, lp); if (markCells) markCellsAsOccupiedForView(child); @@ -936,11 +928,6 @@ public class CellLayout extends ViewGroup { return false; } - public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params, - boolean markCells) { - return addViewToCellLayout(child, index, childId, params, markCells, false); - } - @Override public void removeAllViews() { clearOccupiedCells(); @@ -955,10 +942,6 @@ public class CellLayout extends ViewGroup { } } - public void removeViewWithoutMarkingCells(View view) { - mShortcutsAndWidgets.removeView(view); - } - @Override public void removeView(View view) { markCellsAsUnoccupiedForView(view); @@ -1088,9 +1071,7 @@ public class CellLayout extends ViewGroup { public float getDistanceFromCell(float x, float y, int[] cell) { cellToCenterPoint(cell[0], cell[1], mTmpPoint); - float distance = (float) Math.sqrt( Math.pow(x - mTmpPoint[0], 2) + - Math.pow(y - mTmpPoint[1], 2)); - return distance; + return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]); } int getCellWidth() { @@ -1109,28 +1090,6 @@ public class CellLayout extends ViewGroup { return mHeightGap; } - Rect getContentRect(Rect r) { - if (r == null) { - r = new Rect(); - } - int left = getPaddingLeft(); - int top = getPaddingTop(); - int right = left + getWidth() - getPaddingLeft() - getPaddingRight(); - int bottom = top + getHeight() - getPaddingTop() - getPaddingBottom(); - r.set(left, top, right, bottom); - return r; - } - - /** Return a rect that has the cellWidth/cellHeight (left, top), and - * widthGap/heightGap (right, bottom) */ - static void getMetrics(Rect metrics, int paddedMeasureWidth, - int paddedMeasureHeight, int countX, int countY) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - metrics.set(grid.calculateCellWidth(paddedMeasureWidth, countX), - grid.calculateCellHeight(paddedMeasureHeight, countY), 0, 0); - } - public void setFixedSize(int width, int height) { mFixedWidth = width; mFixedHeight = height; @@ -1246,7 +1205,6 @@ public class CellLayout extends ViewGroup { } public void setBackgroundAlphaMultiplier(float multiplier) { - if (mBackgroundAlphaMultiplier != multiplier) { mBackgroundAlphaMultiplier = multiplier; invalidate(); @@ -1360,36 +1318,6 @@ public class CellLayout extends ViewGroup { return false; } - /** - * Estimate where the top left cell of the dragged item will land if it is dropped. - * - * @param originX The X value of the top left corner of the item - * @param originY The Y value of the top left corner of the item - * @param spanX The number of horizontal cells that the item spans - * @param spanY The number of vertical cells that the item spans - * @param result The estimated drop cell X and Y. - */ - void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) { - final int countX = mCountX; - final int countY = mCountY; - - // pointToCellRounded takes the top left of a cell but will pad that with - // cellWidth/2 and cellHeight/2 when finding the matching cell - pointToCellRounded(originX, originY, result); - - // If the item isn't fully on this screen, snap to the edges - int rightOverhang = result[0] + spanX - countX; - if (rightOverhang > 0) { - result[0] -= rightOverhang; // Snap to right - } - result[0] = Math.max(0, result[0]); // Snap to left - int bottomOverhang = result[1] + spanY - countY; - if (bottomOverhang > 0) { - result[1] -= bottomOverhang; // Snap to bottom - } - result[1] = Math.max(0, result[1]); // Snap to top - } - void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY, int cellX, int cellY, int spanX, int spanY, boolean resize, Point dragOffset, Rect dragRegion) { final int oldDragCellX = mDragCell[0]; @@ -1473,9 +1401,8 @@ public class CellLayout extends ViewGroup { * @return The X, Y cell of a vacant area that can contain this object, * nearest the requested location. */ - int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, - int[] result) { - return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result); + int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) { + return findNearestVacantArea(pixelX, pixelY, spanX, spanY, spanX, spanY, result, null); } /** @@ -1495,30 +1422,10 @@ public class CellLayout extends ViewGroup { */ int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, int[] result, int[] resultSpan) { - return findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null, + return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, true, result, resultSpan); } - /** - * Find a vacant area that will fit the given bounds nearest the requested - * cell location. Uses Euclidean distance to score multiple vacant areas. - * - * @param pixelX The X location at which you want to search for a vacant area. - * @param pixelY The Y location at which you want to search for a vacant area. - * @param spanX Horizontal span of the object. - * @param spanY Vertical span of the object. - * @param ignoreOccupied If true, the result can be an occupied cell - * @param result Array in which to place the result, or null (in which case a new array will - * be allocated) - * @return The X, Y cell of a vacant area that can contain this object, - * nearest the requested location. - */ - int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView, - boolean ignoreOccupied, int[] result) { - return findNearestArea(pixelX, pixelY, spanX, spanY, - spanX, spanY, ignoreView, ignoreOccupied, result, null, mOccupied); - } - private final Stack mTempRectStack = new Stack(); private void lazyInitTempRectStack() { if (mTempRectStack.isEmpty()) { @@ -1550,12 +1457,9 @@ public class CellLayout extends ViewGroup { * @return The X, Y cell of a vacant area that can contain this object, * nearest the requested location. */ - int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, - View ignoreView, boolean ignoreOccupied, int[] result, int[] resultSpan, - boolean[][] occupied) { + private int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, + int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) { lazyInitTempRectStack(); - // mark space take by ignoreView as available (method checks if ignoreView is null) - markCellsAsUnoccupiedForView(ignoreView, occupied); // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds // to the center of the item, but we are searching based on the top-left cell, so @@ -1586,7 +1490,7 @@ public class CellLayout extends ViewGroup { // First, let's see if this thing fits anywhere for (int i = 0; i < minSpanX; i++) { for (int j = 0; j < minSpanY; j++) { - if (occupied[x + i][y + j]) { + if (mOccupied[x + i][y + j]) { continue inner; } } @@ -1603,7 +1507,7 @@ public class CellLayout extends ViewGroup { while (!(hitMaxX && hitMaxY)) { if (incX && !hitMaxX) { for (int j = 0; j < ySize; j++) { - if (x + xSize > countX -1 || occupied[x + xSize][y + j]) { + if (x + xSize > countX -1 || mOccupied[x + xSize][y + j]) { // We can't move out horizontally hitMaxX = true; } @@ -1613,7 +1517,7 @@ public class CellLayout extends ViewGroup { } } else if (!hitMaxY) { for (int i = 0; i < xSize; i++) { - if (y + ySize > countY - 1 || occupied[x + i][y + ySize]) { + if (y + ySize > countY - 1 || mOccupied[x + i][y + ySize]) { // We can't move out vertically hitMaxY = true; } @@ -1646,8 +1550,7 @@ public class CellLayout extends ViewGroup { } } validRegions.push(currentRect); - double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) - + Math.pow(cellXY[1] - pixelY, 2)); + double distance = Math.hypot(cellXY[0] - pixelX, cellXY[1] - pixelY); if ((distance <= bestDistance && !contained) || currentRect.contains(bestRect)) { @@ -1662,8 +1565,6 @@ public class CellLayout extends ViewGroup { } } } - // re-mark space taken by ignoreView as occupied - markCellsAsOccupiedForView(ignoreView, occupied); // Return -1, -1 if no suitable location found if (bestDistance == Double.MAX_VALUE) { @@ -1717,8 +1618,7 @@ public class CellLayout extends ViewGroup { } } - float distance = (float) - Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY)); + float distance = (float) Math.hypot(x - cellX, y - cellY); int[] curDirection = mTmpPoint; computeDirectionVector(x - cellX, y - cellY, curDirection); // The direction score is just the dot product of the two candidate direction @@ -2334,7 +2234,7 @@ public class CellLayout extends ViewGroup { } } - ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY, + private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) { // Copy the current state into the solution. This solution will be manipulated as necessary. @@ -2623,7 +2523,7 @@ public class CellLayout extends ViewGroup { mLauncher.getWorkspace().updateItemLocationsInDatabase(this); } - public void setUseTempCoords(boolean useTempCoords) { + private void setUseTempCoords(boolean useTempCoords) { int childCount = mShortcutsAndWidgets.getChildCount(); for (int i = 0; i < childCount; i++) { LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams(); @@ -2631,11 +2531,11 @@ public class CellLayout extends ViewGroup { } } - ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY, + private ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, View dragView, ItemConfiguration solution) { int[] result = new int[2]; int[] resultSpan = new int[2]; - findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null, result, + findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result, resultSpan); if (result[0] >= 0 && result[1] >= 0) { copyCurrentStateToSolution(solution, false); @@ -2951,45 +2851,6 @@ public class CellLayout extends ViewGroup { } - /** - * Find a vacant area that will fit the given bounds nearest the requested - * cell location. Uses Euclidean distance to score multiple vacant areas. - * - * @param pixelX The X location at which you want to search for a vacant area. - * @param pixelY The Y location at which you want to search for a vacant area. - * @param spanX Horizontal span of the object. - * @param spanY Vertical span of the object. - * @param ignoreView Considers space occupied by this view as unoccupied - * @param result Previously returned value to possibly recycle. - * @return The X, Y cell of a vacant area that can contain this object, - * nearest the requested location. - */ - int[] findNearestVacantArea( - int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) { - return findNearestArea(pixelX, pixelY, spanX, spanY, ignoreView, true, result); - } - - /** - * Find a vacant area that will fit the given bounds nearest the requested - * cell location. Uses Euclidean distance to score multiple vacant areas. - * - * @param pixelX The X location at which you want to search for a vacant area. - * @param pixelY The Y location at which you want to search for a vacant area. - * @param minSpanX The minimum horizontal span required - * @param minSpanY The minimum vertical span required - * @param spanX Horizontal span of the object. - * @param spanY Vertical span of the object. - * @param ignoreView Considers space occupied by this view as unoccupied - * @param result Previously returned value to possibly recycle. - * @return The X, Y cell of a vacant area that can contain this object, - * nearest the requested location. - */ - int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, - int spanX, int spanY, View ignoreView, int[] result, int[] resultSpan) { - return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, ignoreView, true, - result, resultSpan, mOccupied); - } - /** * Find a starting cell position that will fit the given bounds nearest the requested * cell location. Uses Euclidean distance to score multiple vacant areas. @@ -3003,9 +2864,8 @@ public class CellLayout extends ViewGroup { * @return The X, Y cell of a vacant area that can contain this object, * nearest the requested location. */ - int[] findNearestArea( - int pixelX, int pixelY, int spanX, int spanY, int[] result) { - return findNearestArea(pixelX, pixelY, spanX, spanY, null, false, result); + int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) { + return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, false, result, null); } boolean existsEmptyCell() { @@ -3026,103 +2886,32 @@ public class CellLayout extends ViewGroup { * @return True if a vacant cell of the specified dimension was found, false otherwise. */ public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { - return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied); - } - - /** - * Like above, but ignores any cells occupied by the item "ignoreView" - * - * @param cellXY The array that will contain the position of a vacant cell if such a cell - * can be found. - * @param spanX The horizontal span of the cell we want to find. - * @param spanY The vertical span of the cell we want to find. - * @param ignoreView The home screen item we should treat as not occupying any space - * @return - */ - boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) { - return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, - ignoreView, mOccupied); - } - - /** - * Like above, but if intersectX and intersectY are not -1, then this method will try to - * return coordinates for rectangles that contain the cell [intersectX, intersectY] - * - * @param spanX The horizontal span of the cell we want to find. - * @param spanY The vertical span of the cell we want to find. - * @param ignoreView The home screen item we should treat as not occupying any space - * @param intersectX The X coordinate of the cell that we should try to overlap - * @param intersectX The Y coordinate of the cell that we should try to overlap - * - * @return True if a vacant cell of the specified dimension was found, false otherwise. - */ - boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY, - int intersectX, int intersectY) { - return findCellForSpanThatIntersectsIgnoring( - cellXY, spanX, spanY, intersectX, intersectY, null, mOccupied); - } - - /** - * The superset of the above two methods - */ - boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY, - int intersectX, int intersectY, View ignoreView, boolean occupied[][]) { - // mark space take by ignoreView as available (method checks if ignoreView is null) - markCellsAsUnoccupiedForView(ignoreView, occupied); - boolean foundCell = false; - while (true) { - int startX = 0; - if (intersectX >= 0) { - startX = Math.max(startX, intersectX - (spanX - 1)); - } - int endX = mCountX - (spanX - 1); - if (intersectX >= 0) { - endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0)); - } - int startY = 0; - if (intersectY >= 0) { - startY = Math.max(startY, intersectY - (spanY - 1)); - } - int endY = mCountY - (spanY - 1); - if (intersectY >= 0) { - endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0)); - } - - for (int y = startY; y < endY && !foundCell; y++) { - inner: - for (int x = startX; x < endX; x++) { - for (int i = 0; i < spanX; i++) { - for (int j = 0; j < spanY; j++) { - if (occupied[x + i][y + j]) { - // small optimization: we can skip to after the column we just found - // an occupied cell - x += i; - continue inner; - } + final int endX = mCountX - (spanX - 1); + final int endY = mCountY - (spanY - 1); + + for (int y = 0; y < endY && !foundCell; y++) { + inner: + for (int x = 0; x < endX; x++) { + for (int i = 0; i < spanX; i++) { + for (int j = 0; j < spanY; j++) { + if (mOccupied[x + i][y + j]) { + // small optimization: we can skip to after the column we just found + // an occupied cell + x += i; + continue inner; } } - if (cellXY != null) { - cellXY[0] = x; - cellXY[1] = y; - } - foundCell = true; - break; } - } - if (intersectX == -1 && intersectY == -1) { + if (cellXY != null) { + cellXY[0] = x; + cellXY[1] = y; + } + foundCell = true; break; - } else { - // if we failed to find anything, try again but without any requirements of - // intersecting - intersectX = -1; - intersectY = -1; - continue; } } - // re-mark space taken by ignoreView as occupied - markCellsAsOccupiedForView(ignoreView, occupied); return foundCell; } @@ -3232,13 +3021,6 @@ public class CellLayout extends ViewGroup { return result; } - public int[] cellSpansToSize(int hSpans, int vSpans) { - int[] size = new int[2]; - size[0] = hSpans * mCellWidth + (hSpans - 1) * mWidthGap; - size[1] = vSpans * mCellHeight + (vSpans - 1) * mHeightGap; - return size; - } - /** * Calculate the grid spans needed to fit given item */ @@ -3262,44 +3044,6 @@ public class CellLayout extends ViewGroup { info.spanY = spans[1]; } - /** - * Find the first vacant cell, if there is one. - * - * @param vacant Holds the x and y coordinate of the vacant cell - * @param spanX Horizontal cell span. - * @param spanY Vertical cell span. - * - * @return True if a vacant cell was found - */ - public boolean getVacantCell(int[] vacant, int spanX, int spanY) { - - return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied); - } - - static boolean findVacantCell(int[] vacant, int spanX, int spanY, - int xCount, int yCount, boolean[][] occupied) { - - for (int y = 0; (y + spanY) <= yCount; y++) { - for (int x = 0; (x + spanX) <= xCount; x++) { - boolean available = !occupied[x][y]; -out: for (int i = x; i < x + spanX; i++) { - for (int j = y; j < y + spanY; j++) { - available = available && !occupied[i][j]; - if (!available) break out; - } - } - - if (available) { - vacant[0] = x; - vacant[1] = y; - return true; - } - } - } - - return false; - } - private void clearOccupiedCells() { for (int x = 0; x < mCountX; x++) { for (int y = 0; y < mCountY; y++) { @@ -3308,27 +3052,16 @@ out: for (int i = x; i < x + spanX; i++) { } } - public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) { - markCellsAsUnoccupiedForView(view); - markCellsForView(newCellX, newCellY, newSpanX, newSpanY, mOccupied, true); - } - public void markCellsAsOccupiedForView(View view) { - markCellsAsOccupiedForView(view, mOccupied); - } - public void markCellsAsOccupiedForView(View view, boolean[][] occupied) { if (view == null || view.getParent() != mShortcutsAndWidgets) return; LayoutParams lp = (LayoutParams) view.getLayoutParams(); - markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, true); + markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, mOccupied, true); } public void markCellsAsUnoccupiedForView(View view) { - markCellsAsUnoccupiedForView(view, mOccupied); - } - public void markCellsAsUnoccupiedForView(View view, boolean occupied[][]) { if (view == null || view.getParent() != mShortcutsAndWidgets) return; LayoutParams lp = (LayoutParams) view.getLayoutParams(); - markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, false); + markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, mOccupied, false); } private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied, @@ -3374,17 +3107,6 @@ out: for (int i = x; i < x + spanX; i++) { return new CellLayout.LayoutParams(p); } - public static class CellLayoutAnimationController extends LayoutAnimationController { - public CellLayoutAnimationController(Animation animation, float delay) { - super(animation, delay); - } - - @Override - protected long getDelayForView(View view) { - return (int) (Math.random() * 150); - } - } - public static class LayoutParams extends ViewGroup.MarginLayoutParams { /** * Horizontal location of the item in the grid. diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 22fb6a049..3dadd1a70 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -460,8 +460,7 @@ public class DeviceProfile { } @Thunk float dist(PointF p0, PointF p1) { - return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) + - (p1.y-p0.y)*(p1.y-p0.y)); + return (float) Math.hypot(p1.x - p0.x, p1.y - p0.y); } private float weight(PointF a, PointF b, diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 196886895..3b21c2b55 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -513,8 +513,7 @@ public class DragController { checkTouchMove(dropTarget); // Check if we are hovering over the scroll areas - mDistanceSinceScroll += - Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2)); + mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y); mLastTouch[0] = x; mLastTouch[1] = y; checkScrollState(x, y); diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index ab2e094cb..91f97fa4a 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -657,8 +657,7 @@ public class DragLayer extends InsettableFrameLayout { final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) { // Calculate the duration of the animation based on the object's distance - final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) + - Math.pow(to.top - from.top, 2)); + final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top); final Resources res = getResources(); final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index dff47c256..1fca7ce68 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -459,7 +459,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX()); int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY()); - float radius = (float) Math.sqrt(rx * rx + ry * ry); + float radius = (float) Math.hypot(rx, ry); AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); Animator reveal = LauncherAnimUtils.createCircularReveal(this, (int) getPivotX(), (int) getPivotY(), 0, radius); diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 3f08f43c2..43765a250 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -559,7 +559,7 @@ public class FolderPagedView extends PagedView { public int findNearestArea(int pixelX, int pixelY) { int pageIndex = getNextPage(); CellLayout page = getPageAt(pageIndex); - page.findNearestArea(pixelX, pixelY, 1, 1, null, false, sTempPosArray); + page.findNearestArea(pixelX, pixelY, 1, 1, sTempPosArray); if (mFolder.isLayoutRtl()) { sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d5dce51df..00afd98d0 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -434,7 +434,7 @@ public class LauncherModel extends BroadcastReceiver } } } - return CellLayout.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied); + return Utilities.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied); } /** diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index f91cfa07b..9e005f29a 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -258,7 +258,7 @@ public class LauncherStateTransitionAnimation { // Setup the reveal view animation int width = revealView.getMeasuredWidth(); int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); + float revealRadius = (float) Math.hypot(width / 2, height / 2); revealView.setVisibility(View.VISIBLE); revealView.setAlpha(0f); revealView.setTranslationY(0f); @@ -563,7 +563,7 @@ public class LauncherStateTransitionAnimation { if (fromView.getVisibility() == View.VISIBLE) { int width = revealView.getMeasuredWidth(); int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); + float revealRadius = (float) Math.hypot(width / 2, height / 2); revealView.setVisibility(View.VISIBLE); revealView.setAlpha(1f); revealView.setTranslationY(0); diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index 15b617683..56c8b39b6 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -76,14 +76,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup { return null; } - public void addView(View child, int index, LayoutParams params, boolean inLayout) { - if (!inLayout) { - addView(child, index, params); - } else { - addViewInLayout(child, index, params, false); - } - } - @Override protected void dispatchDraw(Canvas canvas) { @SuppressWarnings("all") // suppress dead code warning diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 22677c8ea..2dbf078a4 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -583,4 +583,37 @@ public final class Utilities { return lhs.rank - rhs.rank; } }; + + /** + * Find the first vacant cell, if there is one. + * + * @param vacant Holds the x and y coordinate of the vacant cell + * @param spanX Horizontal cell span. + * @param spanY Vertical cell span. + * + * @return true if a vacant cell was found + */ + public static boolean findVacantCell(int[] vacant, int spanX, int spanY, + int xCount, int yCount, boolean[][] occupied) { + + for (int y = 0; (y + spanY) <= yCount; y++) { + for (int x = 0; (x + spanX) <= xCount; x++) { + boolean available = !occupied[x][y]; + out: for (int i = x; i < x + spanX; i++) { + for (int j = y; j < y + spanY; j++) { + available = available && !occupied[i][j]; + if (!available) break out; + } + } + + if (available) { + vacant[0] = x; + vacant[1] = y; + return true; + } + } + } + + return false; + } } -- cgit v1.2.3 From 8167dc2dcffbf4f68724bc1db2f5cfc1caf6a848 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 27 Apr 2015 13:44:01 -0700 Subject: Goodbye folder sorting > Removing all traces of folder sorting Change-Id: Id77a7745564c869f5c5c1b3a560bf606fe08b107 --- res/values/strings.xml | 4 +- src/com/android/launcher3/Folder.java | 14 +- src/com/android/launcher3/FolderInfo.java | 1 + src/com/android/launcher3/FolderPagedView.java | 211 +------------------------ 4 files changed, 7 insertions(+), 223 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index bfe7e36f2..a68f53a91 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -77,7 +77,7 @@ Search Apps - Loading Apps... + Loading Apps… No Apps found matching \"%1$s\" @@ -91,8 +91,6 @@ OK Cancel - - A-Z diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 1fca7ce68..03a9019e8 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -82,12 +82,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList */ public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY; - /** - * Time in milliseconds for which an icon sticks to the target position - * in case of a sorted folder. - */ - private static final int SORTED_STICKY_REORDER_DELAY = 1500; - /** * Fraction of icon width which behave as scroll region. */ @@ -417,7 +411,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (!(getParent() instanceof DragLayer)) return; mContent.completePendingPageChanges(); - if (!(mDragInProgress && mContent.mIsSorted)) { + if (!mDragInProgress) { // Open on the first page. mContent.snapToPageImmediately(0); } @@ -533,12 +527,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mIsExternalDrag = true; mDragInProgress = true; - if (mContent.mIsSorted) { - mScrollPauseAlarm.setOnAlarmListener(null); - mScrollPauseAlarm.cancelAlarm(); - mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY); - } - // Since this folder opened by another controller, it might not get onDrop or // onDropComplete. Perform cleanup once drag-n-drop ends. mDragController.addDragListener(this); diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 80b156413..69b2ae78c 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -33,6 +33,7 @@ public class FolderInfo extends ItemInfo { /** * The folder is locked in sorted mode + * @deprecated */ public static final int FLAG_ITEMS_SORTED = 0x00000001; diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 43765a250..c68ef72b3 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -20,23 +20,16 @@ import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.util.Log; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.OvershootInterpolator; -import android.widget.Switch; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; import com.android.launcher3.PageIndicator.PageMarkerResources; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.util.Thunk; -import java.text.Collator; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -47,17 +40,10 @@ public class FolderPagedView extends PagedView { private static final boolean ALLOW_FOLDER_SCROLL = true; - // To enable this flag, user_folder.xml needs to be modified to add sort button. - private static final boolean ALLOW_ITEM_SORTING = false; - private static final int REORDER_ANIMATION_DURATION = 230; private static final int START_VIEW_REORDER_DELAY = 30; private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; - private static final int SPAN_TO_PAGE_DURATION = 350; - private static final int SORT_ANIM_HIDE_DURATION = 130; - private static final int SORT_ANIM_SHOW_DURATION = 160; - /** * Fraction of the width to scroll when showing the next page hint. */ @@ -87,13 +73,8 @@ public class FolderPagedView extends PagedView { private FocusIndicatorView mFocusIndicatorView; private PagedFolderKeyEventListener mKeyListener; - private View mSortButton; - private Switch mSortSwitch; private View mPageIndicator; - private boolean mSortOperationPending; - boolean mIsSorted; - public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); LauncherAppState app = LauncherAppState.getInstance(); @@ -121,132 +102,6 @@ public class FolderPagedView extends PagedView { mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); mKeyListener = new PagedFolderKeyEventListener(folder); mPageIndicator = folder.findViewById(R.id.folder_page_indicator); - - if (ALLOW_ITEM_SORTING) { - // Initialize {@link #mSortSwitch} and {@link #mSortButton}. - } - } - - /** - * Called when sort button is clicked. - */ - private void onSortClicked() { - if (mSortOperationPending) { - return; - } - if (mIsSorted) { - setIsSorted(false, true); - } else { - mSortOperationPending = true; - doSort(); - } - } - - private void setIsSorted(boolean isSorted, boolean saveChanges) { - mIsSorted = isSorted; - if (ALLOW_ITEM_SORTING) { - mSortSwitch.setChecked(isSorted); - mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted, - saveChanges ? mFolder.mLauncher : null); - } - } - - /** - * Sorts the contents of the folder and animates the icons on the first page to reflect - * the changes. - * Steps: - * 1. Scroll to first page - * 2. Sort all icons in one go - * 3. Re-apply the old IconInfos on the first page (so that there is no instant change) - * 4. Animate each view individually to reflect the new icon. - */ - private void doSort() { - if (!mSortOperationPending) { - return; - } - if (getNextPage() != 0) { - snapToPage(0, SPAN_TO_PAGE_DURATION, new DecelerateInterpolator()); - return; - } - - mSortOperationPending = false; - ShortcutInfo[][] oldItems = new ShortcutInfo[mGridCountX][mGridCountY]; - CellLayout currentPage = getCurrentCellLayout(); - for (int x = 0; x < mGridCountX; x++) { - for (int y = 0; y < mGridCountY; y++) { - View v = currentPage.getChildAt(x, y); - if (v != null) { - oldItems[x][y] = (ShortcutInfo) v.getTag(); - } - } - } - - ArrayList views = new ArrayList(mFolder.getItemsInReadingOrder()); - Collections.sort(views, new ViewComparator()); - arrangeChildren(views, views.size()); - - int delay = 0; - float delayAmount = START_VIEW_REORDER_DELAY; - final Interpolator hideInterpolator = new DecelerateInterpolator(2); - final Interpolator showInterpolator = new OvershootInterpolator(0.8f); - - currentPage = getCurrentCellLayout(); - for (int x = 0; x < mGridCountX; x++) { - for (int y = 0; y < mGridCountY; y++) { - final BubbleTextView v = (BubbleTextView) currentPage.getChildAt(x, y); - if (v != null) { - final ShortcutInfo info = (ShortcutInfo) v.getTag(); - final Runnable clearPending = new Runnable() { - - @Override - public void run() { - mPendingAnimations.remove(v); - v.setScaleX(1); - v.setScaleY(1); - } - }; - if (oldItems[x][y] == null) { - v.setScaleX(0); - v.setScaleY(0); - v.animate().setDuration(SORT_ANIM_SHOW_DURATION) - .setStartDelay(SORT_ANIM_HIDE_DURATION + delay) - .scaleX(1).scaleY(1).setInterpolator(showInterpolator) - .withEndAction(clearPending); - mPendingAnimations.put(v, clearPending); - } else { - // Apply the old iconInfo so that there is no sudden change. - v.applyFromShortcutInfo(oldItems[x][y], mIconCache, false); - v.animate().setStartDelay(delay).setDuration(SORT_ANIM_HIDE_DURATION) - .scaleX(0).scaleY(0) - .setInterpolator(hideInterpolator) - .withEndAction(new Runnable() { - - @Override - public void run() { - // Apply the new iconInfo as part of the animation. - v.applyFromShortcutInfo(info, mIconCache, false); - v.animate().scaleX(1).scaleY(1) - .setDuration(SORT_ANIM_SHOW_DURATION).setStartDelay(0) - .setInterpolator(showInterpolator) - .withEndAction(clearPending); - } - }); - mPendingAnimations.put(v, new Runnable() { - - @Override - public void run() { - clearPending.run(); - v.applyFromShortcutInfo(info, mIconCache, false); - } - }); - } - delay += delayAmount; - delayAmount *= VIEW_REORDER_DELAY_FACTOR; - } - } - } - - setIsSorted(true, true); } /** @@ -295,7 +150,6 @@ public class FolderPagedView extends PagedView { * @return list of items that could not be bound, probably because we hit the max size limit. */ public ArrayList bindItems(ArrayList items) { - mIsSorted = ALLOW_ITEM_SORTING && mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED); ArrayList icons = new ArrayList(); ArrayList extra = new ArrayList(); @@ -317,20 +171,6 @@ public class FolderPagedView extends PagedView { public int allocateRankForNewItem(ShortcutInfo info) { int rank = getItemCount(); ArrayList views = new ArrayList(mFolder.getItemsInReadingOrder()); - if (ALLOW_ITEM_SORTING && mIsSorted) { - View tmp = new View(getContext()); - tmp.setTag(info); - int index = Collections.binarySearch(views, tmp, new ViewComparator()); - if (index < 0) { - rank = -index - 1; - } else { - // Item with same name already exists. - // We will just insert it before that item. - rank = index; - } - - } - views.add(rank, null); arrangeChildren(views, views.size(), false); setCurrentPage(rank / mMaxItemsPerPage); @@ -447,10 +287,6 @@ public class FolderPagedView extends PagedView { int position = 0; int newX, newY, rank; - boolean isSorted = mIsSorted; - - ViewComparator comparator = new ViewComparator(); - View lastView = null; rank = 0; for (int i = 0; i < itemCount; i++) { View v = list.size() > i ? list.get(i) : null; @@ -465,10 +301,6 @@ public class FolderPagedView extends PagedView { } if (v != null) { - if (lastView != null) { - isSorted &= comparator.compare(lastView, v) <= 0; - } - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); newX = position % mGridCountX; newY = position / mGridCountX; @@ -488,7 +320,6 @@ public class FolderPagedView extends PagedView { v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); } - lastView = v; rank ++; position++; } @@ -506,23 +337,10 @@ public class FolderPagedView extends PagedView { setEnableOverscroll(getPageCount() > 1); // Update footer - if (ALLOW_ITEM_SORTING) { - setIsSorted(isSorted, saveChanges); - if (getPageCount() > 1) { - mPageIndicator.setVisibility(View.VISIBLE); - mSortButton.setVisibility(View.VISIBLE); - mFolder.mFolderName.setGravity(rtlLayout ? Gravity.RIGHT : Gravity.LEFT); - } else { - mPageIndicator.setVisibility(View.GONE); - mSortButton.setVisibility(View.GONE); - mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL); - } - } else { - int indicatorVisibility = mPageIndicator.getVisibility(); - mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE); - if (indicatorVisibility != mPageIndicator.getVisibility()) { - mFolder.updateFooterHeight(); - } + int indicatorVisibility = mPageIndicator.getVisibility(); + mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE); + if (indicatorVisibility != mPageIndicator.getVisibility()) { + mFolder.updateFooterHeight(); } } @@ -630,17 +448,6 @@ public class FolderPagedView extends PagedView { if (mFolder != null) { mFolder.updateTextViewFocus(); } - if (ALLOW_ITEM_SORTING && mSortOperationPending && getNextPage() == 0) { - post(new Runnable() { - - @Override - public void run() { - if (mSortOperationPending) { - doSort(); - } - } - }); - } } /** @@ -829,14 +636,4 @@ public class FolderPagedView extends PagedView { } } } - - private static class ViewComparator implements Comparator { - private final Collator mCollator = Collator.getInstance(); - - @Override - public int compare(View lhs, View rhs) { - return mCollator.compare( ((ShortcutInfo) lhs.getTag()).title.toString(), - ((ShortcutInfo) rhs.getTag()).title.toString()); - } - } } -- cgit v1.2.3 From f17a1c9ad99d46a65de797422fd439382114b348 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 27 Apr 2015 17:01:32 -0700 Subject: Widget tray alignment according to UX spec - Fixing an issue where widget preview is not loaded do to faulty canceling of loading async task when view is detached from the window. b/20338334 Change-Id: I1e3bd6b6cdafda8d1e0a7a0e87c8089100c341a7 --- res/layout/widget_cell.xml | 8 ++---- res/layout/widgets_list_row_view.xml | 33 ++++++++-------------- res/values/colors.xml | 1 + res/values/dimens.xml | 8 ++++-- src/com/android/launcher3/widget/WidgetCell.java | 19 +++---------- .../launcher3/widget/WidgetsListAdapter.java | 11 +++++++- 6 files changed, 36 insertions(+), 44 deletions(-) diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index 64ddea1ae..cb1c812cb 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -17,13 +17,11 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res-auto" android:layout_width="@dimen/widget_preview_container_width" - android:layout_height="wrap_content" + android:layout_height="@dimen/widget_cell_height" android:layout_weight="1" - android:layout_marginTop="@dimen/widget_preview_padding_top" - android:layout_marginLeft="8dp" - android:layout_marginBottom="8dp" + android:layout_marginRight="@dimen/widget_row_divider" android:orientation="vertical" - android:background="@color/bubble_dark_background" + android:background="@color/widgets_cell_color" android:focusable="true"> @@ -29,24 +28,23 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:focusable="true" - android:background="@drawable/focusable_view_bg" android:descendantFocusability="afterDescendants"> - - - + android:orientation="horizontal" + android:background="@color/widget_text_panel"/> diff --git a/res/values/colors.xml b/res/values/colors.xml index 3a06bd95a..58a4d4ce1 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -42,4 +42,5 @@ #009688 + #263238 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index da1108271..a57ae89d0 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -86,12 +86,16 @@ 8dp 8dp - 52dp - 8dp + 56dp + 72dp + 8dp + 16dp 136dp 150dp + 8dp + 1dp 0dp diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 0bc7333ec..7c7d982de 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -48,7 +48,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final String TAG = "WidgetCell"; private static final boolean DEBUG = false; - private static final int FADE_IN_DURATION_MS = 70; + private static final int FADE_IN_DURATION_MS = 90; private int mPresetPreviewSize; private ImageView mWidgetImage; @@ -104,6 +104,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetImage.setImageDrawable(null); mWidgetName.setText(null); mWidgetDims.setText(null); + + cancelLoader(false); } /** @@ -140,16 +142,6 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetPreviewLoader = loader; } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - deletePreview(false); - - if (DEBUG) { - Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString())); - } - } - public int[] getPreviewSize() { int[] maxSize = new int[2]; maxSize[0] = mPresetPreviewSize; @@ -219,10 +211,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return Math.min(size[0], info.spanX * cellWidth); } - - private void deletePreview(boolean recycleImage) { - mWidgetImage.setImageDrawable(null); - + private void cancelLoader(boolean recycleImage) { if (mActiveRequest != null) { mActiveRequest.cancel(recycleImage); mActiveRequest = null; diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index f6ab21eb4..a5b2aff1b 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -127,7 +127,6 @@ public class WidgetsListAdapter extends Adapter { // Bind the view in the widget horizontal tray region. for (int i=0; i < infoList.size(); i++) { WidgetCell widget = (WidgetCell) row.getChildAt(i); - widget.reset(); if (getWidgetPreviewLoader() == null) { return; } @@ -158,6 +157,16 @@ public class WidgetsListAdapter extends Adapter { return new WidgetsRowViewHolder(container); } + @Override + public void onViewRecycled(WidgetsRowViewHolder holder) { + ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); + + for (int i = 0; i < row.getChildCount(); i++) { + WidgetCell widget = (WidgetCell) row.getChildAt(i); + widget.reset(); + } + } + @Override public long getItemId(int pos) { return pos; -- cgit v1.2.3 From 559d90d0dafbac1d97a1e6f18062309831a25d51 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 28 Apr 2015 15:06:45 -0700 Subject: WidgetPreviewLoader concurrency issue / Caching improvement 1) Concurrency issue: unused bitmap was not properly synchronized which caused concurrency issue. Hence, leading current widget tray implementation to not use it. (a.k.a. cancel(false)). Issue fixed and now using the unused bitmap pool. 2) Caching improvement: LoadedBitmap cache was a legacy support system for the old widget tray implementation. On our latest implementation, cache and recycled view is completely being managed by the recycler view. Hence removed. Change-Id: I843e6a286b676f283172f2c1ef5cbeed0a9fb17f --- src/com/android/launcher3/WidgetPreviewLoader.java | 65 +++++++--------------- src/com/android/launcher3/widget/WidgetCell.java | 22 ++++---- .../launcher3/widget/WidgetsContainerView.java | 5 -- .../launcher3/widget/WidgetsListAdapter.java | 12 +++- .../launcher3/widget/WidgetsRowViewHolder.java | 2 +- 5 files changed, 41 insertions(+), 65 deletions(-) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 5c3ed9272..412cbcd6d 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -34,7 +34,6 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetCell; -import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -51,8 +50,13 @@ public class WidgetPreviewLoader { private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; private final HashMap mPackageVersions = new HashMap<>(); - private final HashMap> mLoadedPreviews = new HashMap<>(); - private Set mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap()); + + /** + * Weak reference objects, do not prevent their referents from being made finalizable, + * finalized, and then reclaimed. + */ + private Set mUnusedBitmaps = + Collections.newSetFromMap(new WeakHashMap()); private final Context mContext; private final IconCache mIconCache; @@ -84,18 +88,9 @@ public class WidgetPreviewLoader { String size = previewWidth + "x" + previewHeight; WidgetCacheKey key = getObjectKey(o, size); - // Check if we have the preview loaded or not. - synchronized (mLoadedPreviews) { - WeakReference ref = mLoadedPreviews.get(key); - if (ref != null && ref.get() != null) { - immediateResult[0] = ref.get(); - return new PreviewLoadRequest(null, key); - } - } - PreviewLoadTask task = new PreviewLoadTask(key, o, previewWidth, previewHeight, caller); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - return new PreviewLoadRequest(task, key); + return new PreviewLoadRequest(task); } /** @@ -192,22 +187,6 @@ public class WidgetPreviewLoader { mPackageVersions.remove(packageName); } - synchronized (mLoadedPreviews) { - Set keysToRemove = new HashSet<>(); - for (WidgetCacheKey key : mLoadedPreviews.keySet()) { - if (key.componentName.getPackageName().equals(packageName) && key.user.equals(user)) { - keysToRemove.add(key); - } - } - - for (WidgetCacheKey key : keysToRemove) { - WeakReference req = mLoadedPreviews.remove(key); - if (req != null && req.get() != null) { - mUnusedBitmaps.add(req.get()); - } - } - } - try { mDb.getWritableDatabase().delete(CacheDb.TABLE_NAME, CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?", @@ -549,26 +528,25 @@ public class WidgetPreviewLoader { public class PreviewLoadRequest { private final PreviewLoadTask mTask; - private final WidgetCacheKey mKey; - public PreviewLoadRequest(PreviewLoadTask task, WidgetCacheKey key) { + public PreviewLoadRequest(PreviewLoadTask task) { mTask = task; - mKey = key; } - public void cancel(boolean recycleImage) { + public void cleanup() { if (mTask != null) { mTask.cancel(true); } - if (recycleImage) { - synchronized(mLoadedPreviews) { - WeakReference result = mLoadedPreviews.remove(mKey); - if (result != null && result.get() != null) { - mUnusedBitmaps.add(result.get()); - } - } + if (mTask.mBitmap == null) { + return; + } + + // The preview is no longer bound to any view, move it to {@link WeakReference} list. + synchronized(mUnusedBitmaps) { + mUnusedBitmaps.add(mTask.mBitmap); } + mTask.mBitmap = null; } } @@ -579,6 +557,7 @@ public class WidgetPreviewLoader { private final int mPreviewHeight; private final int mPreviewWidth; private final WidgetCell mCaller; + private Bitmap mBitmap; PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, int previewHeight, WidgetCell caller) { @@ -597,7 +576,6 @@ public class WidgetPreviewLoader { protected Bitmap doInBackground(Void... params) { Bitmap unusedBitmap = null; - // TODO(hyunyoungs): Figure out why this path causes concurrency issue. synchronized (mUnusedBitmaps) { // Check if we can use a bitmap for (Bitmap candidate : mUnusedBitmaps) { @@ -638,10 +616,7 @@ public class WidgetPreviewLoader { @Override protected void onPostExecute(Bitmap result) { - synchronized(mLoadedPreviews) { - mLoadedPreviews.put(mKey, new WeakReference(result)); - } - + mBitmap = result; mCaller.applyPreview(result); } } diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 7c7d982de..2df170eff 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -94,18 +94,25 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mOriginalImagePadding.right = mWidgetImage.getPaddingRight(); mOriginalImagePadding.bottom = mWidgetImage.getPaddingBottom(); - // Ensure we are using the right text size - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); mWidgetName = ((TextView) findViewById(R.id.widget_name)); mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } - public void reset() { + /** + * Called to clear the view and free attached resources. (e.g., {@link Bitmap} + */ + public void clear() { + if (DEBUG) { + Log.d(TAG, "reset called on:" + mWidgetName.getText()); + } mWidgetImage.setImageDrawable(null); mWidgetName.setText(null); mWidgetDims.setText(null); - cancelLoader(false); + if (mActiveRequest != null) { + mActiveRequest.cleanup(); + mActiveRequest = null; + } } /** @@ -211,13 +218,6 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return Math.min(size[0], info.spanX * cellWidth); } - private void cancelLoader(boolean recycleImage) { - if (mActiveRequest != null) { - mActiveRequest.cancel(recycleImage); - mActiveRequest = null; - } - } - /** * Helper method to get the string info of the tag. */ diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 27a3ea13d..8090c88a4 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -26,7 +26,6 @@ import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; -import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; @@ -61,9 +60,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, private static final String TAG = "WidgetsContainerView"; private static final boolean DEBUG = false; - /* {@link RecyclerView} will keep following # of views in cache, before recycling. */ - private static final int WIDGET_CACHE_SIZE = 2; - private static final int SPRING_MODE_DELAY_MS = 150; /* Global instances that are used inside this container. */ @@ -119,7 +115,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, mView = (RecyclerView) findViewById(R.id.widgets_list_view); mView.setAdapter(mAdapter); mView.setLayoutManager(new LinearLayoutManager(getContext())); - mView.setItemViewCacheSize(WIDGET_CACHE_SIZE); mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index a5b2aff1b..051a3ab5e 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -120,7 +120,9 @@ public class WidgetsListAdapter extends Adapter { mIconCache.getTitleAndIconForApp(infoOut.packageName, UserHandleCompat.myUserHandle(), false /* useLowResIcon */, infoOut); } - ((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title); + + TextView tv = ((TextView) holder.getContent().findViewById(R.id.section)); + tv.setText(infoOut.title); ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image); iv.setImageBitmap(infoOut.iconBitmap); @@ -149,7 +151,7 @@ public class WidgetsListAdapter extends Adapter { @Override public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (DEBUG) { - Log.v(TAG, String.format("\nonCreateViewHolder, [widget#=%d]", viewType)); + Log.v(TAG, "\nonCreateViewHolder"); } ViewGroup container = (ViewGroup) mLayoutInflater.inflate( @@ -159,11 +161,15 @@ public class WidgetsListAdapter extends Adapter { @Override public void onViewRecycled(WidgetsRowViewHolder holder) { + if (DEBUG) { + Log.v(TAG, String.format("onViewDetachedFromWindow, [pos=%d]", + holder.getAdapterPosition())); + } ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); for (int i = 0; i < row.getChildCount(); i++) { WidgetCell widget = (WidgetCell) row.getChildAt(i); - widget.reset(); + widget.clear(); } } diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java index 99a192c89..249559ab9 100644 --- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java +++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java @@ -25,7 +25,7 @@ public class WidgetsRowViewHolder extends ViewHolder { ViewGroup mContent; - public WidgetsRowViewHolder(ViewGroup v) { + public WidgetsRowViewHolder(ViewGroup v) { super(v); mContent = v; } -- cgit v1.2.3 From e9b651eef1b9f3647eba94f833bff3fc52f5956b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 24 Apr 2015 11:44:51 -0700 Subject: Enabling accessibility drag and drop in folder > Moving DragAndDropAccessibilityDelegate to a separate class > Using getFocusedVirtualView() instead of using DownX and downY > Updating various accessibility strings Bug: 19776741 Change-Id: I85c2551d4d6172c30702e68f41b114bb999655b6 --- res/values/strings.xml | 18 +- src/com/android/launcher3/ButtonDropTarget.java | 3 + src/com/android/launcher3/CellLayout.java | 288 ++------------------- src/com/android/launcher3/DragController.java | 1 + src/com/android/launcher3/DropTarget.java | 2 + src/com/android/launcher3/Folder.java | 35 ++- .../launcher3/LauncherAccessibilityDelegate.java | 43 ++- src/com/android/launcher3/Workspace.java | 21 +- .../DragAndDropAccessibilityDelegate.java | 137 ++++++++++ .../accessibility/FolderAccessibilityHelper.java | 50 ++++ .../WorkspaceAccessibilityHelper.java | 167 ++++++++++++ 11 files changed, 478 insertions(+), 287 deletions(-) create mode 100644 src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java create mode 100644 src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java create mode 100644 src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java diff --git a/res/values/strings.xml b/res/values/strings.xml index a68f53a91..3b94eb305 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -307,10 +307,13 @@ s --> - Add to workspace + Add to home screen + + + Move here - Item added to workspace + Item added to home screen Item removed @@ -319,7 +322,13 @@ s --> Move Item - Move to empty cell %1$s, %2$s + Move to row %1$s column %2$s + + + Move to position %1$s + + + Move to favorites position %1$s Item moved @@ -327,6 +336,9 @@ s --> Add to folder: %1$s + + Add to folder with %1$s + Item added to folder diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index fb49df5df..daa07d08d 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -203,6 +203,9 @@ public abstract class ButtonDropTarget extends TextView DragLayer.ANIMATION_END_DISAPPEAR, null); } + @Override + public void prepareAccessibilityDrop() { } + @Thunk abstract void completeDrop(DragObject d); @Override diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 85653bef7..f5d2f7dc6 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -35,11 +35,8 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Bundle; import android.os.Parcelable; import android.support.v4.view.ViewCompat; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; -import android.support.v4.widget.ExploreByTouchHelper; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -51,7 +48,9 @@ import android.view.accessibility.AccessibilityEvent; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.FolderIcon.FolderRingAnimator; -import com.android.launcher3.LauncherAccessibilityDelegate.DragType; +import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; +import com.android.launcher3.accessibility.FolderAccessibilityHelper; +import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -60,10 +59,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.List; import java.util.Stack; public class CellLayout extends ViewGroup { + public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2; + public static final int FOLDER_ACCESSIBILITY_DRAG = 1; + static final String TAG = "CellLayout"; private Launcher mLauncher; @@ -178,12 +179,8 @@ public class CellLayout extends ViewGroup { private final static Paint sPaint = new Paint(); // Related to accessible drag and drop - DragAndDropAccessibilityDelegate mTouchHelper = new DragAndDropAccessibilityDelegate(this); + private DragAndDropAccessibilityDelegate mTouchHelper; private boolean mUseTouchHelper = false; - OnClickListener mOldClickListener = null; - OnClickListener mOldWorkspaceListener = null; - @Thunk int mDownX = 0; - @Thunk int mDownY = 0; public CellLayout(Context context) { this(context, null); @@ -311,14 +308,22 @@ public class CellLayout extends ViewGroup { } @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public void enableAccessibleDrag(boolean enable) { + public void enableAccessibleDrag(boolean enable, int dragType) { mUseTouchHelper = enable; + Log.e("HIGHRES", getParent() + " " + enable + " " + dragType, new Exception()); if (!enable) { ViewCompat.setAccessibilityDelegate(this, null); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); setOnClickListener(mLauncher); } else { + if (dragType == WORKSPACE_ACCESSIBILITY_DRAG && + !(mTouchHelper instanceof WorkspaceAccessibilityHelper)) { + mTouchHelper = new WorkspaceAccessibilityHelper(this); + } else if (dragType == FOLDER_ACCESSIBILITY_DRAG && + !(mTouchHelper instanceof FolderAccessibilityHelper)) { + mTouchHelper = new FolderAccessibilityHelper(this); + } ViewCompat.setAccessibilityDelegate(this, mTouchHelper); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); @@ -341,15 +346,6 @@ public class CellLayout extends ViewGroup { return super.dispatchHoverEvent(event); } - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mDownX = (int) event.getX(); - mDownY = (int) event.getY(); - } - return super.dispatchTouchEvent(event); - } - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mUseTouchHelper || @@ -359,252 +355,6 @@ public class CellLayout extends ViewGroup { return false; } - class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper implements OnClickListener { - private final Rect mTempRect = new Rect(); - - public DragAndDropAccessibilityDelegate(View forView) { - super(forView); - } - - private int getViewIdAt(float x, float y) { - if (x < 0 || y < 0 || x > getMeasuredWidth() || y > getMeasuredHeight()) { - return ExploreByTouchHelper.INVALID_ID; - } - - // Map coords to cell - int cellX = (int) Math.floor(x / (mCellWidth + mWidthGap)); - int cellY = (int) Math.floor(y / (mCellHeight + mHeightGap)); - - // Map cell to id - int id = cellX * mCountY + cellY; - return id; - } - - @Override - protected int getVirtualViewAt(float x, float y) { - return nearestDropLocation(getViewIdAt(x, y)); - } - - protected int nearestDropLocation(int id) { - int count = mCountX * mCountY; - for (int delta = 0; delta < count; delta++) { - if (id + delta <= (count - 1)) { - int target = intersectsValidDropTarget(id + delta); - if (target >= 0) { - return target; - } - } else if (id - delta >= 0) { - int target = intersectsValidDropTarget(id - delta); - if (target >= 0) { - return target; - } - } - } - return ExploreByTouchHelper.INVALID_ID; - } - - /** - * Find the virtual view id corresponding to the top left corner of any drop region by which - * the passed id is contained. For an icon, this is simply - * - * @param id the id we're interested examining (ie. does it fit there?) - * @return the view id of the top left corner of a valid drop region or -1 if there is no - * such valid region. For the icon, this can just be -1 or id. - */ - protected int intersectsValidDropTarget(int id) { - LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - if (delegate == null) { - return -1; - } - - int y = id % mCountY; - int x = id / mCountY; - LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); - - if (dragInfo.dragType == DragType.WIDGET) { - // For a widget, every cell must be vacant. In addition, we will return any valid - // drop target by which the passed id is contained. - boolean fits = false; - - // These represent the amount that we can back off if we hit a problem. They - // get consumed as we move up and to the right, trying new regions. - int spanX = dragInfo.info.spanX; - int spanY = dragInfo.info.spanY; - - for (int m = 0; m < spanX; m++) { - for (int n = 0; n < spanY; n++) { - - fits = true; - int x0 = x - m; - int y0 = y - n; - - if (x0 < 0 || y0 < 0) continue; - - for (int i = x0; i < x0 + spanX; i++) { - if (!fits) break; - for (int j = y0; j < y0 + spanY; j++) { - if (i >= mCountX || j >= mCountY || mOccupied[i][j]) { - fits = false; - break; - } - } - } - if (fits) { - return x0 * mCountY + y0; - } - } - } - return -1; - } else { - // For an icon, we simply check the view directly below - View child = getChildAt(x, y); - if (child == null || child == dragInfo.item) { - // Empty cell. Good for an icon or folder. - return id; - } else if (dragInfo.dragType != DragType.FOLDER) { - // For icons, we can consider cells that have another icon or a folder. - ItemInfo info = (ItemInfo) child.getTag(); - if (info instanceof AppInfo || info instanceof FolderInfo || - info instanceof ShortcutInfo) { - return id; - } - } - return -1; - } - } - - @Override - protected void getVisibleVirtualViews(List virtualViews) { - // We create a virtual view for each cell of the grid - // The cell ids correspond to cells in reading order. - int nCells = mCountX * mCountY; - - for (int i = 0; i < nCells; i++) { - if (intersectsValidDropTarget(i) >= 0) { - virtualViews.add(i); - } - } - } - - @Override - protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) { - LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - if (delegate == null) { - return false; - } - - if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { - String confirmation = getConfirmationForIconDrop(viewId); - delegate.handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); - return true; - } - return false; - } - - @Override - public void onClick(View arg0) { - LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - if (delegate == null) { - return; - } - - int viewId = getViewIdAt(mDownX, mDownY); - - String confirmation = getConfirmationForIconDrop(viewId); - delegate.handleAccessibleDrop(CellLayout.this, getItemBounds(viewId), confirmation); - } - - @Override - protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) { - if (id == ExploreByTouchHelper.INVALID_ID) { - throw new IllegalArgumentException("Invalid virtual view id"); - } - // We're required to set something here. - event.setContentDescription(""); - } - - @Override - protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) { - if (id == ExploreByTouchHelper.INVALID_ID) { - throw new IllegalArgumentException("Invalid virtual view id"); - } - - node.setContentDescription(getLocationDescriptionForIconDrop(id)); - node.setBoundsInParent(getItemBounds(id)); - - node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); - node.setClickable(true); - node.setFocusable(true); - } - - private String getLocationDescriptionForIconDrop(int id) { - LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - if (delegate == null) { - return ""; - } - - int y = id % mCountY; - int x = id / mCountY; - LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); - - Resources res = getContext().getResources(); - View child = getChildAt(x, y); - if (child == null || child == dragInfo.item) { - return res.getString(R.string.move_to_empty_cell, x + 1, y + 1); - } else { - ItemInfo info = (ItemInfo) child.getTag(); - if (info instanceof AppInfo || info instanceof ShortcutInfo) { - return res.getString(R.string.create_folder_with, info.title); - } else if (info instanceof FolderInfo) { - return res.getString(R.string.add_to_folder, info.title); - } - } - return ""; - } - - private String getConfirmationForIconDrop(int id) { - LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - if (delegate == null) { - return ""; - } - - int y = id % mCountY; - int x = id / mCountY; - LauncherAccessibilityDelegate.DragInfo dragInfo = delegate.getDragInfo(); - - Resources res = getContext().getResources(); - View child = getChildAt(x, y); - if (child == null || child == dragInfo.item) { - return res.getString(R.string.item_moved); - } else { - ItemInfo info = (ItemInfo) child.getTag(); - if (info instanceof AppInfo || info instanceof ShortcutInfo) { - return res.getString(R.string.folder_created); - - } else if (info instanceof FolderInfo) { - return res.getString(R.string.added_to_folder); - } - } - return ""; - } - - private Rect getItemBounds(int id) { - int cellY = id % mCountY; - int cellX = id / mCountY; - int x = getPaddingLeft() + (int) (cellX * (mCellWidth + mWidthGap)); - int y = getPaddingTop() + (int) (cellY * (mCellHeight + mHeightGap)); - - Rect bounds = mTempRect; - bounds.set(x, y, x + mCellWidth, y + mCellHeight); - return bounds; - } - } - public void enableHardwareLayer(boolean hasLayer) { mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint); } @@ -897,6 +647,10 @@ public class CellLayout extends ViewGroup { mShortcutsAndWidgets.setIsHotseat(isHotseat); } + public boolean isHotseat() { + return mIsHotseat; + } + public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params, boolean markCells) { final LayoutParams lp = params; @@ -982,7 +736,7 @@ public class CellLayout extends ViewGroup { * @param y Y coordinate of the point * @param result Array of 2 ints to hold the x and y coordinate of the cell */ - void pointToCellExact(int x, int y, int[] result) { + public void pointToCellExact(int x, int y, int[] result) { final int hStartPadding = getPaddingLeft(); final int vStartPadding = getPaddingTop(); diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 3b21c2b55..a8960996e 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -658,6 +658,7 @@ public class DragController { mDragObject.y = coordinates[1]; checkTouchMove(dropTarget); + dropTarget.prepareAccessibilityDrop(); // Perform the drop drop(location[0], location[1]); endDrag(); diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index c5cca3b28..3628e573d 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -183,6 +183,8 @@ public interface DropTarget { */ boolean acceptDrop(DragObject dragObject); + void prepareAccessibilityDrop(); + // These methods are implemented in Views void getHitRectRelativeToDragLayer(Rect outRect); void getLocationInDragLayer(int[] loc); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 03a9019e8..e0aeceae8 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -49,8 +49,10 @@ import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.launcher3.CellLayout.CellInfo; import com.android.launcher3.DragController.DragListener; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.util.Thunk; @@ -63,7 +65,7 @@ import java.util.Collections; */ public class Folder extends LinearLayout implements DragSource, View.OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener, DragListener, UninstallSource { + View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource { private static final String TAG = "Launcher.Folder"; /** @@ -237,7 +239,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public boolean onLongClick(View v) { // Return if global dragging is not enabled if (!mLauncher.isDraggingEnabled()) return true; + return beginDrag(v, false); + } + private boolean beginDrag(View v, boolean accessible) { Object tag = v.getTag(); if (tag instanceof ShortcutInfo) { ShortcutInfo item = (ShortcutInfo) tag; @@ -245,7 +250,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return false; } - mLauncher.getWorkspace().beginDragShared(v, new Point(), this, false); + mLauncher.getWorkspace().beginDragShared(v, new Point(), this, accessible); mCurrentDragInfo = item; mEmptyCellRank = item.rank; @@ -259,6 +264,20 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return true; } + @Override + public void startDrag(CellInfo cellInfo, boolean accessible) { + beginDrag(cellInfo.cell, accessible); + } + + @Override + public void enableAccessibleDrag(boolean enable) { + mLauncher.getSearchBar().enableAccessibleDrag(enable); + for (int i = 0; i < mContent.getChildCount(); i++) { + mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG); + } + mLauncher.getWorkspace().setAddNewPageOnDrag(!enable); + } + public boolean isEditingName() { return mIsEditingName; } @@ -711,6 +730,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } + /** + * When performing an accessibility drop, onDrop is sent immediately after onDragEnter. So we + * need to complete all transient states based on timers. + */ + @Override + public void prepareAccessibilityDrop() { + if (mReorderAlarm.alarmPending()) { + mReorderAlarm.cancelAlarm(); + mReorderAlarmListener.onAlarm(mReorderAlarm); + } + } + public void onDropCompleted(final View target, final DragObject d, final boolean isFlingToDelete, final boolean success) { if (mDeferDropAfterUninstall) { diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index a60e16024..4255fa466 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -25,24 +25,25 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { public static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; public static final int MOVE = R.id.action_move; - enum DragType { + public enum DragType { ICON, FOLDER, WIDGET } public static class DragInfo { - DragType dragType; - ItemInfo info; - View item; + public DragType dragType; + public ItemInfo info; + public View item; } - private DragInfo mDragInfo = null; - private final SparseArray mActions = new SparseArray(); @Thunk final Launcher mLauncher; + private DragInfo mDragInfo = null; + private AccessibilityDragSource mDragSource = null; + public LauncherAccessibilityDelegate(Launcher launcher) { mLauncher = launcher; @@ -197,10 +198,23 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { Rect pos = new Rect(); mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos); - mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY()); - mLauncher.getWorkspace().enableAccessibleDrag(true); - mLauncher.getWorkspace().startDrag(cellInfo, true); + + Workspace workspace = mLauncher.getWorkspace(); + + Folder folder = workspace.getOpenFolder(); + if (folder != null) { + if (folder.getItemsInReadingOrder().contains(item)) { + mDragSource = folder; + } else { + mLauncher.closeFolder(); + } + } + if (mDragSource == null) { + mDragSource = workspace; + } + mDragSource.enableAccessibleDrag(true); + mDragSource.startDrag(cellInfo, true); } public boolean onBackPressed() { @@ -218,6 +232,15 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { private void endAccessibleDrag() { mDragInfo = null; - mLauncher.getWorkspace().enableAccessibleDrag(false); + if (mDragSource != null) { + mDragSource.enableAccessibleDrag(false); + mDragSource = null; + } + } + + public static interface AccessibilityDragSource { + void startDrag(CellLayout.CellInfo cellInfo, boolean accessible); + + void enableAccessibleDrag(boolean enable); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 2efd20739..07d1c98f1 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -60,6 +60,7 @@ import android.widget.TextView; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; +import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.compat.UserHandleCompat; @@ -82,7 +83,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class Workspace extends SmoothPagedView implements DropTarget, DragSource, DragScroller, View.OnTouchListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, - Insettable, UninstallSource { + Insettable, UninstallSource, AccessibilityDragSource { private static final String TAG = "Launcher.Workspace"; private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0; @@ -125,6 +126,7 @@ public class Workspace extends SmoothPagedView @Thunk Runnable mRemoveEmptyScreenRunnable; @Thunk boolean mDeferRemoveExtraEmptyScreen = false; + @Thunk boolean mAddNewPageOnDrag = true; /** * CellInfo for the cell that is currently being dragged @@ -390,7 +392,7 @@ public class Workspace extends SmoothPagedView post(new Runnable() { @Override public void run() { - if (mIsDragOccuring) { + if (mIsDragOccuring && mAddNewPageOnDrag) { mDeferRemoveExtraEmptyScreen = false; addExtraEmptyScreenOnDrag(); } @@ -398,6 +400,9 @@ public class Workspace extends SmoothPagedView }); } + public void setAddNewPageOnDrag(boolean addPage) { + mAddNewPageOnDrag = addPage; + } public void deferRemoveExtraEmptyScreen() { mDeferRemoveExtraEmptyScreen = true; @@ -562,7 +567,7 @@ public class Workspace extends SmoothPagedView LauncherAccessibilityDelegate delegate = LauncherAppState.getInstance().getAccessibilityDelegate(); if (delegate != null && delegate.isInAccessibleDrag()) { - newScreen.enableAccessibleDrag(true); + newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); } return screenId; } @@ -1601,10 +1606,11 @@ public class Workspace extends SmoothPagedView } @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override public void enableAccessibleDrag(boolean enable) { for (int i = 0; i < getChildCount(); i++) { CellLayout child = (CellLayout) getChildAt(i); - child.enableAccessibleDrag(enable); + child.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); } if (enable) { @@ -1615,7 +1621,8 @@ public class Workspace extends SmoothPagedView setOnClickListener(mLauncher); } mLauncher.getSearchBar().enableAccessibleDrag(enable); - mLauncher.getHotseat().getLayout().enableAccessibleDrag(enable); + mLauncher.getHotseat().getLayout() + .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); } public boolean hasCustomContent() { @@ -2262,6 +2269,7 @@ public class Workspace extends SmoothPagedView startDrag(cellInfo, false); } + @Override public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) { View child = cellInfo.cell; @@ -2625,6 +2633,9 @@ public class Workspace extends SmoothPagedView return false; } + @Override + public void prepareAccessibilityDrop() { } + public void onDrop(final DragObject d) { mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter); CellLayout dropTargetLayout = mDropToLayout; diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java new file mode 100644 index 000000000..0f1724155 --- /dev/null +++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java @@ -0,0 +1,137 @@ +/* + * 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.accessibility; + +import android.content.Context; +import android.graphics.Rect; +import android.os.Bundle; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.widget.ExploreByTouchHelper; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.CellLayout; +import com.android.launcher3.LauncherAccessibilityDelegate; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; + +import java.util.List; + +/** + * Helper class to make drag-and-drop in a {@link CellLayout} accessible. + */ +public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper + implements OnClickListener { + protected static final int INVALID_POSITION = -1; + + private static final int[] sTempArray = new int[2]; + + protected final CellLayout mView; + protected final Context mContext; + protected final LauncherAccessibilityDelegate mDelegate; + + private final Rect mTempRect = new Rect(); + + public DragAndDropAccessibilityDelegate(CellLayout forView) { + super(forView); + mView = forView; + mContext = mView.getContext(); + mDelegate = LauncherAppState.getInstance().getAccessibilityDelegate(); + } + + @Override + protected int getVirtualViewAt(float x, float y) { + if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) { + return INVALID_ID; + } + mView.pointToCellExact((int) x, (int) y, sTempArray); + + // Map cell to id + int id = sTempArray[0] + sTempArray[1] * mView.getCountX(); + return intersectsValidDropTarget(id); + } + + /** + * @return the view id of the top left corner of a valid drop region or + * {@link #INVALID_POSITION} if there is no such valid region. + */ + protected abstract int intersectsValidDropTarget(int id); + + @Override + protected void getVisibleVirtualViews(List virtualViews) { + // We create a virtual view for each cell of the grid + // The cell ids correspond to cells in reading order. + int nCells = mView.getCountX() * mView.getCountY(); + + for (int i = 0; i < nCells; i++) { + if (intersectsValidDropTarget(i) == i) { + virtualViews.add(i); + } + } + } + + @Override + protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) { + if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) { + String confirmation = getConfirmationForIconDrop(viewId); + mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation); + return true; + } + return false; + } + + @Override + public void onClick(View v) { + onPerformActionForVirtualView(getFocusedVirtualView(), + AccessibilityNodeInfoCompat.ACTION_CLICK, null); + } + + @Override + protected void onPopulateEventForVirtualView(int id, AccessibilityEvent event) { + if (id == INVALID_ID) { + throw new IllegalArgumentException("Invalid virtual view id"); + } + event.setContentDescription(mContext.getString(R.string.action_move_here)); + } + + @Override + protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) { + if (id == INVALID_ID) { + throw new IllegalArgumentException("Invalid virtual view id"); + } + + node.setContentDescription(getLocationDescriptionForIconDrop(id)); + node.setBoundsInParent(getItemBounds(id)); + + node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); + node.setClickable(true); + node.setFocusable(true); + } + + protected abstract String getLocationDescriptionForIconDrop(int id); + + protected abstract String getConfirmationForIconDrop(int id); + + private Rect getItemBounds(int id) { + int cellX = id % mView.getCountX(); + int cellY = id / mView.getCountX(); + LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo(); + mView.cellToRect(cellX, cellY, dragInfo.info.spanX, dragInfo.info.spanY, mTempRect); + return mTempRect; + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java new file mode 100644 index 000000000..fc105b4a4 --- /dev/null +++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java @@ -0,0 +1,50 @@ +/* + * 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.accessibility; + +import com.android.launcher3.CellLayout; +import com.android.launcher3.FolderPagedView; +import com.android.launcher3.R; + +/** + * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder. + */ +public class FolderAccessibilityHelper extends DragAndDropAccessibilityDelegate { + private final int mStartPosition; + + public FolderAccessibilityHelper(CellLayout layout) { + super(layout); + FolderPagedView parent = (FolderPagedView) layout.getParent(); + + int index = parent.indexOfChild(layout); + mStartPosition = 1 + index * layout.getCountX() * layout.getCountY(); + } + @Override + protected int intersectsValidDropTarget(int id) { + return id; + } + + @Override + protected String getLocationDescriptionForIconDrop(int id) { + return mContext.getString(R.string.move_to_position, id + mStartPosition); + } + + @Override + protected String getConfirmationForIconDrop(int id) { + return mContext.getString(R.string.item_moved); + } +} diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java new file mode 100644 index 000000000..42e9e3c58 --- /dev/null +++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java @@ -0,0 +1,167 @@ +/* + * 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.accessibility; + +import android.text.TextUtils; +import android.view.View; + +import com.android.launcher3.AppInfo; +import com.android.launcher3.CellLayout; +import com.android.launcher3.FolderInfo; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherAccessibilityDelegate; +import com.android.launcher3.LauncherAccessibilityDelegate.DragType; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; + +/** + * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace. + */ +public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate { + + public WorkspaceAccessibilityHelper(CellLayout layout) { + super(layout); + } + + /** + * Find the virtual view id corresponding to the top left corner of any drop region by which + * the passed id is contained. For an icon, this is simply + */ + @Override + protected int intersectsValidDropTarget(int id) { + int mCountX = mView.getCountX(); + int mCountY = mView.getCountY(); + + int x = id % mCountX; + int y = id / mCountX; + LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo(); + + if (dragInfo.dragType == DragType.WIDGET && mView.isHotseat()) { + return INVALID_POSITION; + } + + if (dragInfo.dragType == DragType.WIDGET) { + // For a widget, every cell must be vacant. In addition, we will return any valid + // drop target by which the passed id is contained. + boolean fits = false; + + // These represent the amount that we can back off if we hit a problem. They + // get consumed as we move up and to the right, trying new regions. + int spanX = dragInfo.info.spanX; + int spanY = dragInfo.info.spanY; + + for (int m = 0; m < spanX; m++) { + for (int n = 0; n < spanY; n++) { + + fits = true; + int x0 = x - m; + int y0 = y - n; + + if (x0 < 0 || y0 < 0) continue; + + for (int i = x0; i < x0 + spanX; i++) { + if (!fits) break; + for (int j = y0; j < y0 + spanY; j++) { + if (i >= mCountX || j >= mCountY || mView.isOccupied(i, j)) { + fits = false; + break; + } + } + } + if (fits) { + return x0 + mCountX * y0; + } + } + } + return INVALID_POSITION; + } else { + // For an icon, we simply check the view directly below + View child = mView.getChildAt(x, y); + if (child == null || child == dragInfo.item) { + // Empty cell. Good for an icon or folder. + return id; + } else if (dragInfo.dragType != DragType.FOLDER) { + // For icons, we can consider cells that have another icon or a folder. + ItemInfo info = (ItemInfo) child.getTag(); + if (info instanceof AppInfo || info instanceof FolderInfo || + info instanceof ShortcutInfo) { + return id; + } + } + return INVALID_POSITION; + } + } + + @Override + protected String getConfirmationForIconDrop(int id) { + int x = id % mView.getCountX(); + int y = id / mView.getCountX(); + LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo(); + + View child = mView.getChildAt(x, y); + if (child == null || child == dragInfo.item) { + return mContext.getString(R.string.item_moved); + } else { + ItemInfo info = (ItemInfo) child.getTag(); + if (info instanceof AppInfo || info instanceof ShortcutInfo) { + return mContext.getString(R.string.folder_created); + + } else if (info instanceof FolderInfo) { + return mContext.getString(R.string.added_to_folder); + } + } + return ""; + } + + @Override + protected String getLocationDescriptionForIconDrop(int id) { + int x = id % mView.getCountX(); + int y = id / mView.getCountX(); + LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo(); + + View child = mView.getChildAt(x, y); + if (child == null || child == dragInfo.item) { + if (mView.isHotseat()) { + return mContext.getString(R.string.move_to_hotseat_position, id + 1); + } else { + return mContext.getString(R.string.move_to_empty_cell, y + 1, x + 1); + } + } else { + ItemInfo info = (ItemInfo) child.getTag(); + if (info instanceof ShortcutInfo) { + return mContext.getString(R.string.create_folder_with, info.title); + } else if (info instanceof FolderInfo) { + if (TextUtils.isEmpty(info.title.toString().trim())) { + // Find the first item in the folder. + FolderInfo folder = (FolderInfo) info; + ShortcutInfo firstItem = null; + for (ShortcutInfo shortcut : folder.contents) { + if (firstItem == null || firstItem.rank > shortcut.rank) { + firstItem = shortcut; + } + } + + if (firstItem != null) { + return mContext.getString(R.string.add_to_folder_with_app, firstItem.title); + } + } + return mContext.getString(R.string.add_to_folder, info.title); + } + } + return ""; + } +} -- cgit v1.2.3 From 73a22a58ceb53411d48df7bbbeb966168b92850c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 28 Apr 2015 16:51:09 -0700 Subject: Using utility method for serializing bitmap Change-Id: I066f40cf867326b0a116351f00b19350b4c26912 --- .../android/launcher3/LauncherBackupHelper.java | 24 +++++----------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 064f4363a..92bbb4019 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -88,11 +88,6 @@ public class LauncherBackupHelper implements BackupHelper { /** widgets contain previews, which are very large, dribble them out */ private static final int MAX_WIDGETS_PER_PASS = 5; - private static final int IMAGE_COMPRESSION_QUALITY = 75; - - private static final Bitmap.CompressFormat IMAGE_FORMAT = - android.graphics.Bitmap.CompressFormat.PNG; - private static final String[] FAVORITE_PROJECTION = { Favorites._ID, // 0 Favorites.MODIFIED, // 1 @@ -969,10 +964,7 @@ public class LauncherBackupHelper implements BackupHelper { private Resource packIcon(int dpi, Bitmap icon) { Resource res = new Resource(); res.dpi = dpi; - ByteArrayOutputStream os = new ByteArrayOutputStream(); - if (icon.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) { - res.data = os.toByteArray(); - } + res.data = Utilities.flattenBitmap(icon); return res; } @@ -990,20 +982,14 @@ public class LauncherBackupHelper implements BackupHelper { widget.icon = new Resource(); Drawable fullResIcon = iconCache.getFullResIcon(provider.getPackageName(), info.icon); Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - if (icon.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) { - widget.icon.data = os.toByteArray(); - widget.icon.dpi = dpi; - } + widget.icon.data = Utilities.flattenBitmap(icon); + widget.icon.dpi = dpi; } if (info.previewImage != 0) { widget.preview = new Resource(); Bitmap preview = previewLoader.generateWidgetPreview(info, previewWidth, null); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - if (preview.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) { - widget.preview.data = os.toByteArray(); - widget.preview.dpi = dpi; - } + widget.preview.data = Utilities.flattenBitmap(preview); + widget.preview.dpi = dpi; } return widget; } -- cgit v1.2.3 From db184fdc7bd652e45216a453541ab4a266457a3a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 28 Apr 2015 20:43:07 -0700 Subject: Enabling folder paged view to scroll itself with accessibility focus traversal Change-Id: I9095d5bf2de8e2053e0aaa27fb385b158e493964 --- src/com/android/launcher3/FolderPagedView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index c68ef72b3..a07a3dc2c 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -95,6 +95,7 @@ public class FolderPagedView extends PagedView { mIconCache = app.getIconCache(); rtlLayout = getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL; + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } public void setFolder(Folder folder) { @@ -237,6 +238,7 @@ public class FolderPagedView extends PagedView { CellLayout page = new CellLayout(getContext()); page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); + page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); page.setInvertIfRtl(true); page.setGridSize(mGridCountX, mGridCountY); -- cgit v1.2.3 From 3a2698b3087c027b81a99a5d429cb0c07977029a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 28 Apr 2015 21:36:46 -0700 Subject: Updating drop target assets Change-Id: I33f7dc30b10a617ae4784f067491ab01b27b7733 --- res/drawable-hdpi/ic_launcher_clear_active_holo.png | Bin 1181 -> 0 bytes res/drawable-hdpi/ic_launcher_clear_normal_holo.png | Bin 1003 -> 0 bytes res/drawable-hdpi/ic_launcher_info_active.png | Bin 0 -> 3895 bytes res/drawable-hdpi/ic_launcher_info_active_holo.png | Bin 4714 -> 0 bytes res/drawable-hdpi/ic_launcher_info_normal.png | Bin 0 -> 4437 bytes res/drawable-hdpi/ic_launcher_info_normal_holo.png | Bin 2692 -> 0 bytes res/drawable-hdpi/ic_launcher_remove_active.png | Bin 0 -> 2695 bytes res/drawable-hdpi/ic_launcher_remove_normal.png | Bin 0 -> 2142 bytes res/drawable-hdpi/ic_launcher_trashcan_active_holo.png | Bin 2657 -> 0 bytes res/drawable-hdpi/ic_launcher_trashcan_normal_holo.png | Bin 1750 -> 0 bytes res/drawable-hdpi/ic_launcher_uninstall_active.png | Bin 0 -> 1483 bytes res/drawable-hdpi/ic_launcher_uninstall_normal.png | Bin 0 -> 1540 bytes res/drawable-mdpi/ic_launcher_clear_active_holo.png | Bin 949 -> 0 bytes res/drawable-mdpi/ic_launcher_clear_normal_holo.png | Bin 802 -> 0 bytes res/drawable-mdpi/ic_launcher_info_active.png | Bin 0 -> 2597 bytes res/drawable-mdpi/ic_launcher_info_active_holo.png | Bin 2736 -> 0 bytes res/drawable-mdpi/ic_launcher_info_normal.png | Bin 0 -> 2729 bytes res/drawable-mdpi/ic_launcher_info_normal_holo.png | Bin 1518 -> 0 bytes res/drawable-mdpi/ic_launcher_remove_active.png | Bin 0 -> 1853 bytes res/drawable-mdpi/ic_launcher_remove_normal.png | Bin 0 -> 1614 bytes res/drawable-mdpi/ic_launcher_trashcan_active_holo.png | Bin 1783 -> 0 bytes res/drawable-mdpi/ic_launcher_trashcan_normal_holo.png | Bin 1109 -> 0 bytes res/drawable-mdpi/ic_launcher_uninstall_active.png | Bin 0 -> 1274 bytes res/drawable-mdpi/ic_launcher_uninstall_normal.png | Bin 0 -> 1275 bytes res/drawable-xhdpi/ic_launcher_clear_active_holo.png | Bin 1595 -> 0 bytes res/drawable-xhdpi/ic_launcher_clear_normal_holo.png | Bin 1392 -> 0 bytes res/drawable-xhdpi/ic_launcher_info_active.png | Bin 0 -> 5449 bytes res/drawable-xhdpi/ic_launcher_info_active_holo.png | Bin 6841 -> 0 bytes res/drawable-xhdpi/ic_launcher_info_normal.png | Bin 0 -> 6041 bytes res/drawable-xhdpi/ic_launcher_info_normal_holo.png | Bin 3837 -> 0 bytes res/drawable-xhdpi/ic_launcher_remove_active.png | Bin 0 -> 2700 bytes res/drawable-xhdpi/ic_launcher_remove_normal.png | Bin 0 -> 2393 bytes res/drawable-xhdpi/ic_launcher_trashcan_active_holo.png | Bin 3431 -> 0 bytes res/drawable-xhdpi/ic_launcher_trashcan_normal_holo.png | Bin 2240 -> 0 bytes res/drawable-xhdpi/ic_launcher_uninstall_active.png | Bin 0 -> 1893 bytes res/drawable-xhdpi/ic_launcher_uninstall_normal.png | Bin 0 -> 1979 bytes res/drawable-xxhdpi/ic_launcher_clear_active_holo.png | Bin 2357 -> 0 bytes res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png | Bin 2738 -> 0 bytes res/drawable-xxhdpi/ic_launcher_info_active.png | Bin 0 -> 8542 bytes res/drawable-xxhdpi/ic_launcher_info_active_holo.png | Bin 8022 -> 0 bytes res/drawable-xxhdpi/ic_launcher_info_normal.png | Bin 0 -> 9762 bytes res/drawable-xxhdpi/ic_launcher_info_normal_holo.png | Bin 7833 -> 0 bytes res/drawable-xxhdpi/ic_launcher_remove_active.png | Bin 0 -> 5126 bytes res/drawable-xxhdpi/ic_launcher_remove_normal.png | Bin 0 -> 5087 bytes res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png | Bin 4493 -> 0 bytes res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png | Bin 4522 -> 0 bytes res/drawable-xxhdpi/ic_launcher_uninstall_active.png | Bin 0 -> 2673 bytes res/drawable-xxhdpi/ic_launcher_uninstall_normal.png | Bin 0 -> 2773 bytes res/drawable-xxxhdpi/ic_launcher_info_active.png | Bin 0 -> 7999 bytes res/drawable-xxxhdpi/ic_launcher_info_normal.png | Bin 0 -> 8414 bytes res/drawable-xxxhdpi/ic_launcher_remove_active.png | Bin 0 -> 3843 bytes res/drawable-xxxhdpi/ic_launcher_remove_normal.png | Bin 0 -> 3721 bytes res/drawable-xxxhdpi/ic_launcher_uninstall_active.png | Bin 0 -> 2776 bytes res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png | Bin 0 -> 2869 bytes res/drawable/info_target_selector.xml | 4 ++-- res/drawable/remove_target_selector.xml | 4 ++-- res/drawable/uninstall_target_selector.xml | 4 ++-- res/values/colors.xml | 5 +++-- src/com/android/launcher3/UninstallDropTarget.java | 2 +- 59 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 res/drawable-hdpi/ic_launcher_clear_active_holo.png delete mode 100644 res/drawable-hdpi/ic_launcher_clear_normal_holo.png create mode 100644 res/drawable-hdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-hdpi/ic_launcher_info_active_holo.png create mode 100644 res/drawable-hdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-hdpi/ic_launcher_info_normal_holo.png create mode 100644 res/drawable-hdpi/ic_launcher_remove_active.png create mode 100644 res/drawable-hdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-hdpi/ic_launcher_trashcan_active_holo.png delete mode 100644 res/drawable-hdpi/ic_launcher_trashcan_normal_holo.png create mode 100644 res/drawable-hdpi/ic_launcher_uninstall_active.png create mode 100644 res/drawable-hdpi/ic_launcher_uninstall_normal.png delete mode 100644 res/drawable-mdpi/ic_launcher_clear_active_holo.png delete mode 100644 res/drawable-mdpi/ic_launcher_clear_normal_holo.png create mode 100644 res/drawable-mdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-mdpi/ic_launcher_info_active_holo.png create mode 100644 res/drawable-mdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-mdpi/ic_launcher_info_normal_holo.png create mode 100644 res/drawable-mdpi/ic_launcher_remove_active.png create mode 100644 res/drawable-mdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-mdpi/ic_launcher_trashcan_active_holo.png delete mode 100644 res/drawable-mdpi/ic_launcher_trashcan_normal_holo.png create mode 100644 res/drawable-mdpi/ic_launcher_uninstall_active.png create mode 100644 res/drawable-mdpi/ic_launcher_uninstall_normal.png delete mode 100644 res/drawable-xhdpi/ic_launcher_clear_active_holo.png delete mode 100644 res/drawable-xhdpi/ic_launcher_clear_normal_holo.png create mode 100644 res/drawable-xhdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-xhdpi/ic_launcher_info_active_holo.png create mode 100644 res/drawable-xhdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-xhdpi/ic_launcher_info_normal_holo.png create mode 100644 res/drawable-xhdpi/ic_launcher_remove_active.png create mode 100644 res/drawable-xhdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-xhdpi/ic_launcher_trashcan_active_holo.png delete mode 100644 res/drawable-xhdpi/ic_launcher_trashcan_normal_holo.png create mode 100644 res/drawable-xhdpi/ic_launcher_uninstall_active.png create mode 100644 res/drawable-xhdpi/ic_launcher_uninstall_normal.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_clear_active_holo.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png create mode 100644 res/drawable-xxhdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_info_active_holo.png create mode 100644 res/drawable-xxhdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_info_normal_holo.png create mode 100644 res/drawable-xxhdpi/ic_launcher_remove_active.png create mode 100644 res/drawable-xxhdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png create mode 100644 res/drawable-xxhdpi/ic_launcher_uninstall_active.png create mode 100644 res/drawable-xxhdpi/ic_launcher_uninstall_normal.png create mode 100644 res/drawable-xxxhdpi/ic_launcher_info_active.png create mode 100644 res/drawable-xxxhdpi/ic_launcher_info_normal.png create mode 100644 res/drawable-xxxhdpi/ic_launcher_remove_active.png create mode 100644 res/drawable-xxxhdpi/ic_launcher_remove_normal.png create mode 100644 res/drawable-xxxhdpi/ic_launcher_uninstall_active.png create mode 100644 res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png diff --git a/res/drawable-hdpi/ic_launcher_clear_active_holo.png b/res/drawable-hdpi/ic_launcher_clear_active_holo.png deleted file mode 100644 index cdd0052b3..000000000 Binary files a/res/drawable-hdpi/ic_launcher_clear_active_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_clear_normal_holo.png b/res/drawable-hdpi/ic_launcher_clear_normal_holo.png deleted file mode 100644 index 84549ff94..000000000 Binary files a/res/drawable-hdpi/ic_launcher_clear_normal_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_info_active.png b/res/drawable-hdpi/ic_launcher_info_active.png new file mode 100644 index 000000000..f7a3b68fe Binary files /dev/null and b/res/drawable-hdpi/ic_launcher_info_active.png differ diff --git a/res/drawable-hdpi/ic_launcher_info_active_holo.png b/res/drawable-hdpi/ic_launcher_info_active_holo.png deleted file mode 100644 index c534e5670..000000000 Binary files a/res/drawable-hdpi/ic_launcher_info_active_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_info_normal.png b/res/drawable-hdpi/ic_launcher_info_normal.png new file mode 100644 index 000000000..780d7962a Binary files /dev/null and b/res/drawable-hdpi/ic_launcher_info_normal.png differ diff --git a/res/drawable-hdpi/ic_launcher_info_normal_holo.png b/res/drawable-hdpi/ic_launcher_info_normal_holo.png deleted file mode 100644 index c9bcd7f37..000000000 Binary files a/res/drawable-hdpi/ic_launcher_info_normal_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_remove_active.png b/res/drawable-hdpi/ic_launcher_remove_active.png new file mode 100644 index 000000000..e53de0da4 Binary files /dev/null and b/res/drawable-hdpi/ic_launcher_remove_active.png differ diff --git a/res/drawable-hdpi/ic_launcher_remove_normal.png b/res/drawable-hdpi/ic_launcher_remove_normal.png new file mode 100644 index 000000000..91e19ca34 Binary files /dev/null and b/res/drawable-hdpi/ic_launcher_remove_normal.png differ diff --git a/res/drawable-hdpi/ic_launcher_trashcan_active_holo.png b/res/drawable-hdpi/ic_launcher_trashcan_active_holo.png deleted file mode 100644 index 82b1b5917..000000000 Binary files a/res/drawable-hdpi/ic_launcher_trashcan_active_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_trashcan_normal_holo.png b/res/drawable-hdpi/ic_launcher_trashcan_normal_holo.png deleted file mode 100644 index 3fc2e8347..000000000 Binary files a/res/drawable-hdpi/ic_launcher_trashcan_normal_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_uninstall_active.png b/res/drawable-hdpi/ic_launcher_uninstall_active.png new file mode 100644 index 000000000..22b97ee50 Binary files /dev/null and b/res/drawable-hdpi/ic_launcher_uninstall_active.png differ diff --git a/res/drawable-hdpi/ic_launcher_uninstall_normal.png b/res/drawable-hdpi/ic_launcher_uninstall_normal.png new file mode 100644 index 000000000..7aea5d06e Binary files /dev/null and b/res/drawable-hdpi/ic_launcher_uninstall_normal.png differ diff --git a/res/drawable-mdpi/ic_launcher_clear_active_holo.png b/res/drawable-mdpi/ic_launcher_clear_active_holo.png deleted file mode 100644 index 2683beaa3..000000000 Binary files a/res/drawable-mdpi/ic_launcher_clear_active_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_clear_normal_holo.png b/res/drawable-mdpi/ic_launcher_clear_normal_holo.png deleted file mode 100644 index 219f3e5cf..000000000 Binary files a/res/drawable-mdpi/ic_launcher_clear_normal_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_info_active.png b/res/drawable-mdpi/ic_launcher_info_active.png new file mode 100644 index 000000000..ea712722d Binary files /dev/null and b/res/drawable-mdpi/ic_launcher_info_active.png differ diff --git a/res/drawable-mdpi/ic_launcher_info_active_holo.png b/res/drawable-mdpi/ic_launcher_info_active_holo.png deleted file mode 100644 index f84b4a6ba..000000000 Binary files a/res/drawable-mdpi/ic_launcher_info_active_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_info_normal.png b/res/drawable-mdpi/ic_launcher_info_normal.png new file mode 100644 index 000000000..8c60159f7 Binary files /dev/null and b/res/drawable-mdpi/ic_launcher_info_normal.png differ diff --git a/res/drawable-mdpi/ic_launcher_info_normal_holo.png b/res/drawable-mdpi/ic_launcher_info_normal_holo.png deleted file mode 100644 index eac578f5c..000000000 Binary files a/res/drawable-mdpi/ic_launcher_info_normal_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_remove_active.png b/res/drawable-mdpi/ic_launcher_remove_active.png new file mode 100644 index 000000000..f36cfdd2f Binary files /dev/null and b/res/drawable-mdpi/ic_launcher_remove_active.png differ diff --git a/res/drawable-mdpi/ic_launcher_remove_normal.png b/res/drawable-mdpi/ic_launcher_remove_normal.png new file mode 100644 index 000000000..60829b9e1 Binary files /dev/null and b/res/drawable-mdpi/ic_launcher_remove_normal.png differ diff --git a/res/drawable-mdpi/ic_launcher_trashcan_active_holo.png b/res/drawable-mdpi/ic_launcher_trashcan_active_holo.png deleted file mode 100644 index 0350e558d..000000000 Binary files a/res/drawable-mdpi/ic_launcher_trashcan_active_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_trashcan_normal_holo.png b/res/drawable-mdpi/ic_launcher_trashcan_normal_holo.png deleted file mode 100644 index 799b62f8b..000000000 Binary files a/res/drawable-mdpi/ic_launcher_trashcan_normal_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_uninstall_active.png b/res/drawable-mdpi/ic_launcher_uninstall_active.png new file mode 100644 index 000000000..e4ee9112d Binary files /dev/null and b/res/drawable-mdpi/ic_launcher_uninstall_active.png differ diff --git a/res/drawable-mdpi/ic_launcher_uninstall_normal.png b/res/drawable-mdpi/ic_launcher_uninstall_normal.png new file mode 100644 index 000000000..aefbc695b Binary files /dev/null and b/res/drawable-mdpi/ic_launcher_uninstall_normal.png differ diff --git a/res/drawable-xhdpi/ic_launcher_clear_active_holo.png b/res/drawable-xhdpi/ic_launcher_clear_active_holo.png deleted file mode 100644 index 1a7e53ddb..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_clear_active_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_clear_normal_holo.png b/res/drawable-xhdpi/ic_launcher_clear_normal_holo.png deleted file mode 100644 index d4965d9f8..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_clear_normal_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_info_active.png b/res/drawable-xhdpi/ic_launcher_info_active.png new file mode 100644 index 000000000..b438f9eb2 Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher_info_active.png differ diff --git a/res/drawable-xhdpi/ic_launcher_info_active_holo.png b/res/drawable-xhdpi/ic_launcher_info_active_holo.png deleted file mode 100644 index b8cdbc4f8..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_info_active_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_info_normal.png b/res/drawable-xhdpi/ic_launcher_info_normal.png new file mode 100644 index 000000000..5c49816ff Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher_info_normal.png differ diff --git a/res/drawable-xhdpi/ic_launcher_info_normal_holo.png b/res/drawable-xhdpi/ic_launcher_info_normal_holo.png deleted file mode 100644 index f503fb82d..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_info_normal_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_remove_active.png b/res/drawable-xhdpi/ic_launcher_remove_active.png new file mode 100644 index 000000000..14ac79d41 Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher_remove_active.png differ diff --git a/res/drawable-xhdpi/ic_launcher_remove_normal.png b/res/drawable-xhdpi/ic_launcher_remove_normal.png new file mode 100644 index 000000000..8188805cc Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher_remove_normal.png differ diff --git a/res/drawable-xhdpi/ic_launcher_trashcan_active_holo.png b/res/drawable-xhdpi/ic_launcher_trashcan_active_holo.png deleted file mode 100644 index c155274bd..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_trashcan_active_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_trashcan_normal_holo.png b/res/drawable-xhdpi/ic_launcher_trashcan_normal_holo.png deleted file mode 100644 index 2ec7ad9b4..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_trashcan_normal_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_uninstall_active.png b/res/drawable-xhdpi/ic_launcher_uninstall_active.png new file mode 100644 index 000000000..2c19b329d Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher_uninstall_active.png differ diff --git a/res/drawable-xhdpi/ic_launcher_uninstall_normal.png b/res/drawable-xhdpi/ic_launcher_uninstall_normal.png new file mode 100644 index 000000000..a093f28c9 Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher_uninstall_normal.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_clear_active_holo.png b/res/drawable-xxhdpi/ic_launcher_clear_active_holo.png deleted file mode 100644 index 95cf84115..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_clear_active_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png b/res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png deleted file mode 100644 index b0f5a2702..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_info_active.png b/res/drawable-xxhdpi/ic_launcher_info_active.png new file mode 100644 index 000000000..d354dd33b Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher_info_active.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_info_active_holo.png b/res/drawable-xxhdpi/ic_launcher_info_active_holo.png deleted file mode 100644 index 57f332a94..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_info_active_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_info_normal.png b/res/drawable-xxhdpi/ic_launcher_info_normal.png new file mode 100644 index 000000000..c270be286 Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher_info_normal.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_info_normal_holo.png b/res/drawable-xxhdpi/ic_launcher_info_normal_holo.png deleted file mode 100644 index 94f0955d6..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_info_normal_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_remove_active.png b/res/drawable-xxhdpi/ic_launcher_remove_active.png new file mode 100644 index 000000000..9df44042e Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher_remove_active.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_remove_normal.png b/res/drawable-xxhdpi/ic_launcher_remove_normal.png new file mode 100644 index 000000000..5bc8f0c8d Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher_remove_normal.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png b/res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png deleted file mode 100644 index 3bb098c2b..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png b/res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png deleted file mode 100644 index 550cc5bb8..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxhdpi/ic_launcher_uninstall_active.png new file mode 100644 index 000000000..db7d339e6 Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher_uninstall_active.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_uninstall_normal.png b/res/drawable-xxhdpi/ic_launcher_uninstall_normal.png new file mode 100644 index 000000000..4fce55b1e Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher_uninstall_normal.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_info_active.png b/res/drawable-xxxhdpi/ic_launcher_info_active.png new file mode 100644 index 000000000..162e23dc2 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher_info_active.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_info_normal.png b/res/drawable-xxxhdpi/ic_launcher_info_normal.png new file mode 100644 index 000000000..270e15d7f Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher_info_normal.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_remove_active.png b/res/drawable-xxxhdpi/ic_launcher_remove_active.png new file mode 100644 index 000000000..c0b8ea2ee Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher_remove_active.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_remove_normal.png b/res/drawable-xxxhdpi/ic_launcher_remove_normal.png new file mode 100644 index 000000000..ed96c55e8 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher_remove_normal.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png new file mode 100644 index 000000000..75896f3a2 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png b/res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png new file mode 100644 index 000000000..61490b9ca Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png differ diff --git a/res/drawable/info_target_selector.xml b/res/drawable/info_target_selector.xml index f3a7016c3..51caece53 100644 --- a/res/drawable/info_target_selector.xml +++ b/res/drawable/info_target_selector.xml @@ -19,6 +19,6 @@ --> - - + + diff --git a/res/drawable/remove_target_selector.xml b/res/drawable/remove_target_selector.xml index 5e071fbfa..9025e8a1a 100644 --- a/res/drawable/remove_target_selector.xml +++ b/res/drawable/remove_target_selector.xml @@ -19,6 +19,6 @@ --> - - + + diff --git a/res/drawable/uninstall_target_selector.xml b/res/drawable/uninstall_target_selector.xml index 229942e3a..175cc20dd 100644 --- a/res/drawable/uninstall_target_selector.xml +++ b/res/drawable/uninstall_target_selector.xml @@ -19,6 +19,6 @@ --> - - + + diff --git a/res/values/colors.xml b/res/values/colors.xml index 58a4d4ce1..9695b2606 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -20,8 +20,9 @@ - #DAFF0000 - #DA0099CC + #DAC1C1C1 + #DAF0592B + #DA009688 #80000000 #20000000 diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 4c52d7e0f..c3511358d 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -26,7 +26,7 @@ public class UninstallDropTarget extends ButtonDropTarget { protected void onFinishInflate() { super.onFinishInflate(); // Get the hover color - mHoverColor = getResources().getColor(R.color.delete_target_hover_tint); + mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint); setDrawable(R.drawable.uninstall_target_selector); } -- cgit v1.2.3 From c525d80dcd06b70910de0ea9ef063a032c0c8fad Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Apr 2015 11:05:34 -0700 Subject: Removing obsolete resources Change-Id: I9a8d3d23e772a3ffc161ac97a5f08808aab79d5b --- res/layout/add_list_item.xml | 25 -------- res/layout/rename_folder.xml | 42 ------------ res/values/strings.xml | 109 +------------------------------- src/com/android/launcher3/Launcher.java | 4 +- 4 files changed, 3 insertions(+), 177 deletions(-) delete mode 100644 res/layout/add_list_item.xml delete mode 100644 res/layout/rename_folder.xml diff --git a/res/layout/add_list_item.xml b/res/layout/add_list_item.xml deleted file mode 100644 index e937d7bc1..000000000 --- a/res/layout/add_list_item.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/res/layout/rename_folder.xml b/res/layout/rename_folder.xml deleted file mode 100644 index 21a335c4a..000000000 --- a/res/layout/rename_folder.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index a68f53a91..b5e02f29d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -34,8 +34,6 @@ Launcher3 Home - - Android Core Apps @@ -48,10 +46,6 @@ Downloaded app disabled in Safe mode Widgets disabled in Safe mode - - Widgets - - Widgets Show Mem @@ -62,17 +56,6 @@ %1$d \u00d7 %2$d - - %1$s (%2$d \u00d7 %3$d) - - Couldn\'t drop item on this Home screen. - - Choose widget to create - Search Apps @@ -81,54 +64,18 @@ No Apps found matching \"%1$s\" - - - - Folder name - - Rename folder - - OK - - Cancel - - + - - Add to Home screen - - Apps - - Shortcuts - - Widgets - - No more room on your Home screens. No more room on this Home screen. No more room in the Favorites tray - - This widget is too large for the Favorites tray - - Shortcut \"%s\" created. - - Shortcut \"%s\" already exists. - - - Choose shortcut - - Choose app Apps Home - - Uninstall Remove @@ -137,35 +84,6 @@ s --> App info - - Apps - - Remove - - - Uninstall update - - - - - - - - Uninstall app - - App details - - 1 app selected - - 1 widget selected - - 1 folder selected - - 1 shortcut selected - @@ -196,9 +114,6 @@ s --> This is a system app and can\'t be uninstalled. - - Rocket Launcher - Unnamed Folder @@ -212,22 +127,10 @@ s --> Page %1$d of %2$d Home screen %1$d of %2$d - - Apps page %1$d of %2$d - - Widgets page %1$d of %2$d Welcome - - Make yourself at home. - - - - - - Create more screens for apps and folders Copy your app icons @@ -236,24 +139,14 @@ s --> COPY ICONS START FRESH - - Organize your space - - Touch & hold background to manage wallpaper, widgets and settings. Wallpapers, widgets, & settings Touch & hold background to customize GOT IT - - Here\'s a folder - - To create one like this, touch & hold an app, then move it over another. OK - - Error: custom workspace layout passed in but custom cling was not overwritten diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8c920f0b8..8258984c3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3546,9 +3546,9 @@ public class Launcher extends Activity text.clear(); // Populate event with a fake title based on the current state. if (mState == State.APPS) { - text.add("Apps"); + text.add(getString(R.string.all_apps_button_label)); } else if (mState == State.WIDGETS) { - text.add("Widgets"); + text.add(getString(R.string.widget_button_text)); } else { text.add(getString(R.string.all_apps_home_button_label)); } -- cgit v1.2.3 From fe0d1f2458e6a442613b070ae549124a4780e759 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 28 Apr 2015 22:01:31 -0700 Subject: Animating dragview color change when dragging over ButtonDropTarget Change-Id: I642438be681769812302dba0b6deca38f89f7a16 --- proguard.flags | 6 ++- res/values/config.xml | 3 -- src/com/android/launcher3/ButtonDropTarget.java | 49 +++++++++++++++----- src/com/android/launcher3/DragView.java | 61 ++++++++++++++++++++++--- 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/proguard.flags b/proguard.flags index e2a4b5b31..6eb594825 100644 --- a/proguard.flags +++ b/proguard.flags @@ -65,4 +65,8 @@ -keep class com.android.launcher3.AppsContainerRecyclerView { public void setFastScrollerAlpha(float); public float getFastScrollerAlpha(); -} \ No newline at end of file +} + +-keep class com.android.launcher3.ButtonDropTarget { + public int getTextColor(); +} diff --git a/res/values/config.xml b/res/values/config.xml index 47394a1f8..7ce40594f 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -55,9 +55,6 @@ 150 - - 0 - 900 diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index fb49df5df..ee8710cd1 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -16,14 +16,16 @@ package com.android.launcher3; +import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; +import android.os.Build; import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; @@ -42,8 +44,6 @@ public abstract class ButtonDropTarget extends TextView private static int DRAG_VIEW_DROP_DURATION = 285; - protected final int mTransitionDuration; - protected Launcher mLauncher; private int mBottomDragPadding; protected TextView mText; @@ -58,16 +58,15 @@ public abstract class ButtonDropTarget extends TextView protected ColorStateList mOriginalTextColor; protected TransitionDrawable mDrawable; + private ObjectAnimator mCurrentColorAnim; + public ButtonDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - - Resources r = getResources(); - mTransitionDuration = r.getInteger(R.integer.config_dropTargetBgTransitionDuration); - mBottomDragPadding = r.getDimensionPixelSize(R.dimen.drop_target_drag_padding); + mBottomDragPadding = getResources().getDimensionPixelSize(R.dimen.drop_target_drag_padding); } @Override @@ -123,8 +122,13 @@ public abstract class ButtonDropTarget extends TextView @Override public final void onDragEnter(DragObject d) { d.dragView.setColor(mHoverColor); - mDrawable.startTransition(mTransitionDuration); - setTextColor(mHoverColor); + if (Utilities.isLmpOrAbove()) { + mDrawable.startTransition(DragView.COLOR_CHANGE_DURATION); + animateTextColor(mHoverColor); + } else { + mDrawable.startTransition(0); + setTextColor(mHoverColor); + } } @Override @@ -133,8 +137,23 @@ public abstract class ButtonDropTarget extends TextView } protected void resetHoverColor() { - mDrawable.resetTransition(); - setTextColor(mOriginalTextColor); + if (Utilities.isLmpOrAbove()) { + mDrawable.reverseTransition(DragView.COLOR_CHANGE_DURATION); + animateTextColor(mOriginalTextColor.getDefaultColor()); + } else { + mDrawable.resetTransition(); + setTextColor(mOriginalTextColor); + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void animateTextColor(int targetColor) { + if (mCurrentColorAnim != null) { + mCurrentColorAnim.cancel(); + } + mCurrentColorAnim = ObjectAnimator.ofArgb(this, "textColor", targetColor); + mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION); + mCurrentColorAnim.start(); } @Override @@ -152,6 +171,10 @@ public abstract class ButtonDropTarget extends TextView public final void onDragStart(DragSource source, Object info, int dragAction) { mActive = supportsDrop(source, info); mDrawable.resetTransition(); + if (mCurrentColorAnim != null) { + mCurrentColorAnim.cancel(); + mCurrentColorAnim = null; + } setTextColor(mOriginalTextColor); ((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE); } @@ -271,4 +294,8 @@ public abstract class ButtonDropTarget extends TextView LauncherAppState.getInstance().getAccessibilityDelegate() .handleAccessibleDrop(this, null, getAccessibilityDropConfirmation()); } + + public int getTextColor() { + return getTextColors().getDefaultColor(); + } } diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index b1a6266cc..a4b6704ac 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -16,22 +16,30 @@ package com.android.launcher3; +import android.animation.FloatArrayEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.Point; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; +import android.os.Build; import android.view.View; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.util.Thunk; +import java.util.Arrays; + public class DragView extends View { + public static int COLOR_CHANGE_DURATION = 200; + @Thunk static float sDragAlpha = 1f; private Bitmap mBitmap; @@ -54,6 +62,9 @@ public class DragView extends View { // size. This is ignored for non-icons. private float mIntrinsicIconScale = 1f; + private float[] mCurrentFilter; + private ValueAnimator mFilterAnimator; + /** * Construct the drag view. *

@@ -229,11 +240,50 @@ public class DragView extends View { mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); } if (color != 0) { - mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); + ColorMatrix m1 = new ColorMatrix(); + m1.setSaturation(0); + + ColorMatrix m2 = new ColorMatrix(); + m2.setScale(Color.red(color) / 255f, Color.green(color) / 255f, + Color.blue(color) / 255f, Color.alpha(color) / 255f); + m1.postConcat(m2); + + if (Utilities.isLmpOrAbove()) { + animateFilterTo(m1.getArray()); + } else { + mPaint.setColorFilter(new ColorMatrixColorFilter(m1)); + invalidate(); + } } else { - mPaint.setColorFilter(null); + if (!Utilities.isLmpOrAbove() || mCurrentFilter == null) { + mPaint.setColorFilter(null); + invalidate(); + } else { + animateFilterTo(new ColorMatrix().getArray()); + } } - invalidate(); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void animateFilterTo(float[] targetFilter) { + float[] oldFilter = mCurrentFilter == null ? new ColorMatrix().getArray() : mCurrentFilter; + mCurrentFilter = Arrays.copyOf(oldFilter, oldFilter.length); + + if (mFilterAnimator != null) { + mFilterAnimator.cancel(); + } + mFilterAnimator = ValueAnimator.ofObject(new FloatArrayEvaluator(mCurrentFilter), + oldFilter, targetFilter); + mFilterAnimator.setDuration(COLOR_CHANGE_DURATION); + mFilterAnimator.addUpdateListener(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mPaint.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter)); + invalidate(); + } + }); + mFilterAnimator.start(); } public boolean hasDrawn() { @@ -301,4 +351,3 @@ public class DragView extends View { } } } - -- cgit v1.2.3 From a911672f45900fc0ed746e0d84c43c6e5ad89b6a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Apr 2015 13:55:58 -0700 Subject: Simplifying add to workspace by not going through the worker thread Change-Id: I3260786bee257aea93ade49e6fa18008232addbe --- src/com/android/launcher3/CellLayout.java | 4 + src/com/android/launcher3/Launcher.java | 13 -- .../launcher3/LauncherAccessibilityDelegate.java | 103 +++++++++----- src/com/android/launcher3/LauncherModel.java | 152 ++++----------------- 4 files changed, 102 insertions(+), 170 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 85653bef7..83919809d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -3294,4 +3294,8 @@ public class CellLayout extends ViewGroup { public boolean lastDownOnOccupiedCell() { return mLastDownOnOccupiedCell; } + + public boolean findVacantCell(int spanX, int spanY, int[] outXY) { + return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied); + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8258984c3..2ef5d0ac4 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3702,19 +3702,6 @@ public class Launcher extends Activity } } - @Override - public void bindAddPendingItem(final PendingAddItemInfo info, final long container, - final long screenId, final int[] cell, final int spanX, final int spanY) { - showWorkspace(true, new Runnable() { - - @Override - public void run() { - mWorkspace.snapToPage(mWorkspace.getPageIndexForScreenId(screenId)); - addPendingItem(info, container, screenId, cell, spanX, spanY); - } - }); - } - private boolean shouldShowWeightWatcher() { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index a60e16024..bb32fa112 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -5,13 +5,13 @@ import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.View.AccessibilityDelegate; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import com.android.launcher3.LauncherModel.ScreenPosProvider; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -19,11 +19,13 @@ import java.util.ArrayList; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class LauncherAccessibilityDelegate extends AccessibilityDelegate { - public static final int REMOVE = R.id.action_remove; - public static final int INFO = R.id.action_info; - public static final int UNINSTALL = R.id.action_uninstall; - public static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; - public static final int MOVE = R.id.action_move; + private static final String TAG = "LauncherAccessibilityDelegate"; + + private static final int REMOVE = R.id.action_remove; + private static final int INFO = R.id.action_info; + private static final int UNINSTALL = R.id.action_uninstall; + private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; + private static final int MOVE = R.id.action_move; enum DragType { ICON, @@ -39,8 +41,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { private DragInfo mDragInfo = null; - private final SparseArray mActions = - new SparseArray(); + private final SparseArray mActions = new SparseArray<>(); @Thunk final Launcher mLauncher; public LauncherAccessibilityDelegate(Launcher launcher) { @@ -56,7 +57,6 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { launcher.getText(R.string.action_add_to_workspace))); mActions.put(MOVE, new AccessibilityAction(MOVE, launcher.getText(R.string.action_move))); - } @Override @@ -93,7 +93,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { return super.performAccessibilityAction(host, action, args); } - public boolean performAction(View host, ItemInfo item, int action) { + public boolean performAction(View host, final ItemInfo item, int action) { if (action == REMOVE) { if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) { announceConfirmation(R.string.item_removed); @@ -108,32 +108,32 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { } else if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { - final int preferredPage = mLauncher.getWorkspace().getCurrentPage(); - final ScreenPosProvider screenProvider = new ScreenPosProvider() { + final int[] coordinates = new int[2]; + final long screenId = findSpaceOnWorkspace(item, coordinates); + mLauncher.showWorkspace(true, new Runnable() { @Override - public int getScreenIndex(ArrayList screenIDs) { - return preferredPage; - } - }; - if (item instanceof AppInfo) { - final ArrayList addShortcuts = new ArrayList(); - addShortcuts.add(((AppInfo) item).makeShortcut()); - mLauncher.showWorkspace(true, new Runnable() { - @Override - public void run() { - mLauncher.getModel().addAndBindAddedWorkspaceItems( - mLauncher, addShortcuts, screenProvider, 0, true); - announceConfirmation(R.string.item_added_to_workspace); + public void run() { + if (item instanceof AppInfo) { + ShortcutInfo info = ((AppInfo) item).makeShortcut(); + LauncherModel.addItemToDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates[0], coordinates[1]); + + ArrayList itemList = new ArrayList<>(); + itemList.add(info); + mLauncher.bindItems(itemList, 0, itemList.size(), true); + } else if (item instanceof PendingAddItemInfo) { + PendingAddItemInfo info = (PendingAddItemInfo) item; + Workspace workspace = mLauncher.getWorkspace(); + workspace.snapToPage(workspace.getPageIndexForScreenId(screenId)); + mLauncher.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates, info.spanX, info.spanY); } - }); - return true; - } else if (item instanceof PendingAddItemInfo) { - mLauncher.getModel().addAndBindPendingItem( - mLauncher, (PendingAddItemInfo) item, screenProvider, 0); - announceConfirmation(R.string.item_added_to_workspace); - return true; - } + announceConfirmation(R.string.item_added_to_workspace); + } + }); + return true; } return false; } @@ -220,4 +220,41 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { mDragInfo = null; mLauncher.getWorkspace().enableAccessibleDrag(false); } + + /** + * Find empty space on the workspace and returns the screenId. + */ + private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) { + Workspace workspace = mLauncher.getWorkspace(); + ArrayList workspaceScreens = workspace.getScreenOrder(); + long screenId; + + // First check if there is space on the current screen. + int screenIndex = workspace.getCurrentPage(); + screenId = workspaceScreens.get(screenIndex); + CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex); + + boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); + screenIndex = workspace.hasCustomContent() ? 1 : 0; + while (!found && screenIndex < workspaceScreens.size()) { + screenId = workspaceScreens.get(screenIndex); + layout = (CellLayout) workspace.getPageAt(screenIndex); + found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); + screenIndex++; + } + + if (found) { + return screenId; + } + + workspace.addExtraEmptyScreen(); + screenId = workspace.commitExtraEmptyScreen(); + layout = workspace.getScreenWithId(screenId); + found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); + + if (!found) { + Log.wtf(TAG, "Not enough space on an empty screen"); + } + return screenId; + } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 00afd98d0..97a283006 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -35,7 +35,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Environment; @@ -203,18 +202,12 @@ public class LauncherModel extends BroadcastReceiver public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); public void dumpLogsToLocalData(); - public void bindAddPendingItem(PendingAddItemInfo info, long container, long screenId, - int[] cell, int spanX, int spanY); } public interface ItemInfoFilter { public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn); } - public interface ScreenPosProvider { - int getScreenIndex(ArrayList screenIDs); - } - LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { Context context = app.getContext(); @@ -406,19 +399,7 @@ public class LauncherModel extends BroadcastReceiver runOnWorkerThread(r); } - public void addAndBindAddedWorkspaceItems(final Context context, - final ArrayList workspaceApps) { - addAndBindAddedWorkspaceItems(context, workspaceApps, - new ScreenPosProvider() { - - @Override - public int getScreenIndex(ArrayList screenIDs) { - return screenIDs.isEmpty() ? 0 : 1; - } - }, 1, false); - } - - private static boolean findNextAvailableIconSpaceInScreen(ArrayList occupiedPos, + private static boolean findNextAvailableIconSpaceInScreen(ArrayList occupiedPos, int[] xy, int spanX, int spanY) { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -426,9 +407,11 @@ public class LauncherModel extends BroadcastReceiver final int yCount = (int) grid.numRows; boolean[][] occupied = new boolean[xCount][yCount]; if (occupiedPos != null) { - for (Rect r : occupiedPos) { - for (int x = r.left; 0 <= x && x < r.right && x < xCount; x++) { - for (int y = r.top; 0 <= y && y < r.bottom && y < yCount; y++) { + for (ItemInfo r : occupiedPos) { + int right = r.cellX + r.spanX; + int bottom = r.cellY + r.spanY; + for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) { + for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) { occupied[x][y] = true; } } @@ -443,53 +426,24 @@ public class LauncherModel extends BroadcastReceiver */ @Thunk static Pair findSpaceForItem( Context context, - ScreenPosProvider preferredScreen, - int fallbackStartScreen, ArrayList workspaceScreens, ArrayList addedWorkspaceScreensFinal, int spanX, int spanY) { - // Load position of items which are on the desktop. We can't use sBgItemsIdMap because - // loadWorkspace() may not have been called. - final ContentResolver cr = context.getContentResolver(); - Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { - LauncherSettings.Favorites.SCREEN, - LauncherSettings.Favorites.CELLX, - LauncherSettings.Favorites.CELLY, - LauncherSettings.Favorites.SPANX, - LauncherSettings.Favorites.SPANY, - LauncherSettings.Favorites.CONTAINER - }, - "container=?", - new String[] { Integer.toString(LauncherSettings.Favorites.CONTAINER_DESKTOP) }, - null); - - final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); - final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); - LongSparseArray> screenItems = new LongSparseArray>(); - try { - while (c.moveToNext()) { - Rect rect = new Rect(); - rect.left = c.getInt(cellXIndex); - rect.top = c.getInt(cellYIndex); - rect.right = rect.left + Math.max(1, c.getInt(spanXIndex)); - rect.bottom = rect.top + Math.max(1, c.getInt(spanYIndex)); - - long screenId = c.getInt(screenIndex); - ArrayList items = screenItems.get(screenId); - if (items == null) { - items = new ArrayList(); - screenItems.put(screenId, items); - } - items.add(rect); - } - } catch (Exception e) { - screenItems.clear(); - } finally { - c.close(); + LongSparseArray> screenItems = new LongSparseArray<>(); + + // Use sBgItemsIdMap as all the items are already loaded. + // TODO: Throw exception is above condition is not met. + synchronized (sBgLock) { + for (ItemInfo info : sBgItemsIdMap) { + if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { + ArrayList items = screenItems.get(info.screenId); + if (items == null) { + items = new ArrayList<>(); + screenItems.put(info.screenId, items); + } + items.add(info); + } + } } // Find appropriate space for the item. @@ -499,7 +453,7 @@ public class LauncherModel extends BroadcastReceiver int screenCount = workspaceScreens.size(); // First check the preferred screen. - int preferredScreenIndex = preferredScreen.getScreenIndex(workspaceScreens); + int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1; if (preferredScreenIndex < screenCount) { screenId = workspaceScreens.get(preferredScreenIndex); found = findNextAvailableIconSpaceInScreen( @@ -507,8 +461,8 @@ public class LauncherModel extends BroadcastReceiver } if (!found) { - // Search on any of the screens. - for (int screen = fallbackStartScreen; screen < screenCount; screen++) { + // Search on any of the screens starting from the first screen. + for (int screen = 1; screen < screenCount; screen++) { screenId = workspaceScreens.get(screen); if (findNextAvailableIconSpaceInScreen( screenItems.get(screenId), cordinates, spanX, spanY)) { @@ -538,59 +492,9 @@ public class LauncherModel extends BroadcastReceiver /** * Adds the provided items to the workspace. - * @param preferredScreen the screen where we should try to add the app first - * @param fallbackStartScreen the screen to start search for empty space if - * preferredScreen is not available. - */ - public void addAndBindPendingItem( - final Context context, - final PendingAddItemInfo addInfo, - final ScreenPosProvider preferredScreen, - final int fallbackStartScreen) { - final Callbacks callbacks = getCallback(); - // Process the newly added applications and add them to the database first - Runnable r = new Runnable() { - public void run() { - final ArrayList addedWorkspaceScreensFinal = new ArrayList(); - ArrayList workspaceScreens = loadWorkspaceScreensDb(context); - - // Find appropriate space for the item. - Pair coords = findSpaceForItem(context, preferredScreen, - fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal, - addInfo.spanX, - addInfo.spanY); - final long screenId = coords.first; - final int[] cordinates = coords.second; - - // Update the workspace screens - updateWorkspaceScreenOrder(context, workspaceScreens); - runOnMainThread(new Runnable() { - public void run() { - Callbacks cb = getCallback(); - if (callbacks == cb && cb != null) { - cb.bindAddScreens(addedWorkspaceScreensFinal); - cb.bindAddPendingItem(addInfo, - LauncherSettings.Favorites.CONTAINER_DESKTOP, - screenId, cordinates, addInfo.spanX, addInfo.spanY); - } - } - }); - } - }; - runOnWorkerThread(r); - } - - /** - * Adds the provided items to the workspace. - * @param preferredScreen the screen where we should try to add the app first - * @param fallbackStartScreen the screen to start search for empty space if - * preferredScreen is not available. */ public void addAndBindAddedWorkspaceItems(final Context context, - final ArrayList workspaceApps, - final ScreenPosProvider preferredScreen, - final int fallbackStartScreen, - final boolean allowDuplicate) { + final ArrayList workspaceApps) { final Callbacks callbacks = getCallback(); if (workspaceApps.isEmpty()) { return; @@ -607,7 +511,7 @@ public class LauncherModel extends BroadcastReceiver ArrayList workspaceScreens = loadWorkspaceScreensDb(context); synchronized(sBgLock) { for (ItemInfo item : workspaceApps) { - if (!allowDuplicate && item instanceof ShortcutInfo) { + if (item instanceof ShortcutInfo) { // Short-circuit this logic if the icon exists somewhere on the workspace if (shortcutExists(context, item.getIntent(), item.user)) { continue; @@ -615,8 +519,8 @@ public class LauncherModel extends BroadcastReceiver } // Find appropriate space for the item. - Pair coords = findSpaceForItem(context, preferredScreen, - fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal, + Pair coords = findSpaceForItem(context, + workspaceScreens, addedWorkspaceScreensFinal, 1, 1); long screenId = coords.first; int[] cordinates = coords.second; -- cgit v1.2.3 From 9ae77771d751a5a7fe87f660ab70908b5f55d9fc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Apr 2015 16:30:23 -0700 Subject: Adding accessibility action to move item out of a folder Change-Id: If67e3472793d6e0913adfbc9d78f64de7f751664 --- res/values/config.xml | 1 + res/values/strings.xml | 2 ++ .../launcher3/LauncherAccessibilityDelegate.java | 32 ++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/res/values/config.xml b/res/values/config.xml index 7ce40594f..21e1d6987 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -99,4 +99,5 @@ + diff --git a/res/values/strings.xml b/res/values/strings.xml index a5a681a4f..57f23ae98 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -241,4 +241,6 @@ Folder created + + Move to home screen diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index a527db423..8a9a0508c 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -4,6 +4,7 @@ import android.annotation.TargetApi; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -26,6 +27,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { private static final int UNINSTALL = R.id.action_uninstall; private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; private static final int MOVE = R.id.action_move; + private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; public enum DragType { ICON, @@ -58,6 +60,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { launcher.getText(R.string.action_add_to_workspace))); mActions.put(MOVE, new AccessibilityAction(MOVE, launcher.getText(R.string.action_move))); + mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE, + launcher.getText(R.string.action_move_to_workspace))); } @Override @@ -80,6 +84,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { || (item instanceof LauncherAppWidgetInfo) || (item instanceof FolderInfo)) { info.addAction(mActions.get(MOVE)); + + if (item.container >= 0) { + info.addAction(mActions.get(MOVE_TO_WORKSPACE)); + } } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { info.addAction(mActions.get(ADD_TO_WORKSPACE)); } @@ -135,6 +143,30 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { } }); return true; + } else if (action == MOVE_TO_WORKSPACE) { + Folder folder = mLauncher.getWorkspace().getOpenFolder(); + mLauncher.closeFolder(folder); + ShortcutInfo info = (ShortcutInfo) item; + folder.getInfo().remove(info); + + final int[] coordinates = new int[2]; + final long screenId = findSpaceOnWorkspace(item, coordinates); + LauncherModel.moveItemInDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates[0], coordinates[1]); + + // Bind the item in next frame so that if a new workspace page was created, + // it will get laid out. + new Handler().post(new Runnable() { + + @Override + public void run() { + ArrayList itemList = new ArrayList<>(); + itemList.add(item); + mLauncher.bindItems(itemList, 0, itemList.size(), true); + announceConfirmation(R.string.item_moved); + } + }); } return false; } -- cgit v1.2.3 From 9aaef938c3f0db6c78555459eb84be25ab87f98e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 30 Apr 2015 10:32:25 -0700 Subject: Removing debug log Change-Id: Ifb6adc0826d744f2bbc654c37c2470754f364a2b --- src/com/android/launcher3/CellLayout.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 94f227595..65c67025f 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -310,7 +310,6 @@ public class CellLayout extends ViewGroup { @TargetApi(Build.VERSION_CODES.LOLLIPOP) public void enableAccessibleDrag(boolean enable, int dragType) { mUseTouchHelper = enable; - Log.e("HIGHRES", getParent() + " " + enable + " " + dragType, new Exception()); if (!enable) { ViewCompat.setAccessibilityDelegate(this, null); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); -- cgit v1.2.3 From 111c83579a165fe9d8f915cda4e08de47e16c056 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 30 Apr 2015 15:43:26 -0700 Subject: Removes code that is incompatible with google3, Velvet build rules. Change-Id: Ibce31b52f5ffc4a03bdd48c575521c774f17a353 --- src/com/android/launcher3/FolderInfo.java | 1 - src/com/android/launcher3/widget/WidgetsListAdapter.java | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 69b2ae78c..80b156413 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -33,7 +33,6 @@ public class FolderInfo extends ItemInfo { /** * The folder is locked in sorted mode - * @deprecated */ public static final int FLAG_ITEMS_SORTED = 0x00000001; diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 051a3ab5e..a7728a11b 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -161,10 +161,6 @@ public class WidgetsListAdapter extends Adapter { @Override public void onViewRecycled(WidgetsRowViewHolder holder) { - if (DEBUG) { - Log.v(TAG, String.format("onViewDetachedFromWindow, [pos=%d]", - holder.getAdapterPosition())); - } ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); for (int i = 0; i < row.getChildCount(); i++) { -- cgit v1.2.3 From ddec73471eb6cc1f15eb9421a205bb2362509075 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Apr 2015 18:12:37 -0700 Subject: Updating fling to delete anim Change-Id: I1c63e88b3e605113ea66afca9dcfbc30de1d4c8e --- src/com/android/launcher3/ButtonDropTarget.java | 2 +- src/com/android/launcher3/DeleteDropTarget.java | 175 ++------------------- src/com/android/launcher3/DragController.java | 3 +- src/com/android/launcher3/DropTarget.java | 4 +- src/com/android/launcher3/Folder.java | 3 +- src/com/android/launcher3/Workspace.java | 2 +- src/com/android/launcher3/util/FlingAnimation.java | 104 ++++++++++++ 7 files changed, 124 insertions(+), 169 deletions(-) create mode 100644 src/com/android/launcher3/util/FlingAnimation.java diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 7cf002e40..683c511da 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -117,7 +117,7 @@ public abstract class ButtonDropTarget extends TextView } @Override - public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { } + public void onFlingToDelete(DragObject d, PointF vec) { } @Override public final void onDragEnter(DragObject d) { diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index e741b9787..08186f517 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -17,30 +17,18 @@ package com.android.launcher3; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; import android.graphics.PointF; -import android.graphics.Rect; import android.os.AsyncTask; import android.util.AttributeSet; import android.view.View; -import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; -import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.util.FlingAnimation; import com.android.launcher3.util.Thunk; -import com.android.launcher3.widget.WidgetsContainerView; public class DeleteDropTarget extends ButtonDropTarget { - private static int FLING_DELETE_ANIMATION_DURATION = 350; - private static float FLING_TO_DELETE_FRICTION = 0.035f; - private static int MODE_FLING_DELETE_TO_TRASH = 0; - private static int MODE_FLING_DELETE_ALONG_VECTOR = 1; - - private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR; - public DeleteDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -119,146 +107,19 @@ public class DeleteDropTarget extends ButtonDropTarget { return true; } - /** - * Creates an animation from the current drag view to the delete trash icon. - */ - private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer, - DragObject d, PointF vel, ViewConfiguration config) { - - int width = mDrawable.getIntrinsicWidth(); - int height = mDrawable.getIntrinsicHeight(); - final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), - width, height); - final Rect from = new Rect(); - dragLayer.getViewRectRelativeToSelf(d.dragView, from); - - // Calculate how far along the velocity vector we should put the intermediate point on - // the bezier curve - float velocity = Math.abs(vel.length()); - float vp = Math.min(1f, velocity / (config.getScaledMaximumFlingVelocity() / 2f)); - int offsetY = (int) (-from.top * vp); - int offsetX = (int) (offsetY / (vel.y / vel.x)); - final float y2 = from.top + offsetY; // intermediate t/l - final float x2 = from.left + offsetX; - final float x1 = from.left; // drag view t/l - final float y1 = from.top; - final float x3 = to.left; // delete target t/l - final float y3 = to.top; - - final TimeInterpolator scaleAlphaInterpolator = new TimeInterpolator() { - @Override - public float getInterpolation(float t) { - return t * t * t * t * t * t * t * t; - } - }; - return new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final DragView dragView = (DragView) dragLayer.getAnimatedView(); - float t = ((Float) animation.getAnimatedValue()).floatValue(); - float tp = scaleAlphaInterpolator.getInterpolation(t); - float initialScale = dragView.getInitialScale(); - float finalAlpha = 0.5f; - float scale = dragView.getScaleX(); - float x1o = ((1f - scale) * dragView.getMeasuredWidth()) / 2f; - float y1o = ((1f - scale) * dragView.getMeasuredHeight()) / 2f; - float x = (1f - t) * (1f - t) * (x1 - x1o) + 2 * (1f - t) * t * (x2 - x1o) + - (t * t) * x3; - float y = (1f - t) * (1f - t) * (y1 - y1o) + 2 * (1f - t) * t * (y2 - x1o) + - (t * t) * y3; - - dragView.setTranslationX(x); - dragView.setTranslationY(y); - dragView.setScaleX(initialScale * (1f - tp)); - dragView.setScaleY(initialScale * (1f - tp)); - dragView.setAlpha(finalAlpha + (1f - finalAlpha) * (1f - tp)); - } - }; - } - - /** - * Creates an animation from the current drag view along its current velocity vector. - * For this animation, the alpha runs for a fixed duration and we update the position - * progressively. - */ - private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener { - private DragLayer mDragLayer; - private PointF mVelocity; - private Rect mFrom; - private long mPrevTime; - private boolean mHasOffsetForScale; - private float mFriction; - - private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); - - public FlingAlongVectorAnimatorUpdateListener(DragLayer dragLayer, PointF vel, Rect from, - long startTime, float friction) { - mDragLayer = dragLayer; - mVelocity = vel; - mFrom = from; - mPrevTime = startTime; - mFriction = 1f - (dragLayer.getResources().getDisplayMetrics().density * friction); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final DragView dragView = (DragView) mDragLayer.getAnimatedView(); - float t = ((Float) animation.getAnimatedValue()).floatValue(); - long curTime = AnimationUtils.currentAnimationTimeMillis(); - - if (!mHasOffsetForScale) { - mHasOffsetForScale = true; - float scale = dragView.getScaleX(); - float xOffset = ((scale - 1f) * dragView.getMeasuredWidth()) / 2f; - float yOffset = ((scale - 1f) * dragView.getMeasuredHeight()) / 2f; - - mFrom.left += xOffset; - mFrom.top += yOffset; - } - - mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f); - mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f); - - dragView.setTranslationX(mFrom.left); - dragView.setTranslationY(mFrom.top); - dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); - - mVelocity.x *= mFriction; - mVelocity.y *= mFriction; - mPrevTime = curTime; - } - }; - private AnimatorUpdateListener createFlingAlongVectorAnimatorListener(final DragLayer dragLayer, - DragObject d, PointF vel, final long startTime, final int duration, - ViewConfiguration config) { - final Rect from = new Rect(); - dragLayer.getViewRectRelativeToSelf(d.dragView, from); - - return new FlingAlongVectorAnimatorUpdateListener(dragLayer, vel, from, startTime, - FLING_TO_DELETE_FRICTION); - } - - public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) { - final boolean isWidgets = d.dragSource instanceof WidgetsContainerView; - final boolean isAllapps = d.dragSource instanceof AppsContainerView; - + @Override + public void onFlingToDelete(final DragObject d, PointF vel) { // Don't highlight the icon as it's animating d.dragView.setColor(0); d.dragView.updateInitialScaleToCurrentScale(); - // Don't highlight the target if we are flinging from AllApps - if (isWidgets || isAllapps) { - resetHoverColor(); - } - - if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) { - // Defer animating out the drop target if we are animating to it - mSearchDropTargetBar.deferOnDragEnd(); - mSearchDropTargetBar.finishAnimations(); - } - final ViewConfiguration config = ViewConfiguration.get(mLauncher); final DragLayer dragLayer = mLauncher.getDragLayer(); - final int duration = FLING_DELETE_ANIMATION_DURATION; + FlingAnimation fling = new FlingAnimation(d, vel, + getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), + mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()), + dragLayer); + + final int duration = fling.getDuration(); final long startTime = AnimationUtils.currentAnimationTimeMillis(); // NOTE: Because it takes time for the first frame of animation to actually be @@ -282,27 +143,17 @@ public class DeleteDropTarget extends ButtonDropTarget { return Math.min(1f, mOffset + t); } }; - AnimatorUpdateListener updateCb = null; - if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) { - updateCb = createFlingToTrashAnimatorListener(dragLayer, d, vel, config); - } else if (mFlingDeleteMode == MODE_FLING_DELETE_ALONG_VECTOR) { - updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime, - duration, config); - } Runnable onAnimationEndRunnable = new Runnable() { @Override public void run() { - // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up - // itself, otherwise, complete the drop to initiate the deletion process - if (!isWidgets || !isAllapps) { - mLauncher.exitSpringLoadedDragMode(); - completeDrop(d); - } + mLauncher.exitSpringLoadedDragMode(); + completeDrop(d); mLauncher.getDragController().onDeferredEndFling(d); } }; - dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable, + + dragLayer.animateView(d.dragView, fling, duration, tInterpolator, onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); } diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index a8960996e..5fea9d889 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -710,8 +710,7 @@ public class DragController { mDragObject.dragComplete = true; mFlingToDeleteDropTarget.onDragExit(mDragObject); if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) { - mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, mDragObject.x, mDragObject.y, - vel); + mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, vel); accepted = true; } mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject, true, diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 3628e573d..a3828c1d0 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -29,7 +29,7 @@ public interface DropTarget { public static final String TAG = "DropTarget"; - class DragObject { + public static class DragObject { public int x = -1; public int y = -1; @@ -164,7 +164,7 @@ public interface DropTarget { * of onDrop(). (This is only called on objects that are set as the DragController's * fling-to-delete target. */ - void onFlingToDelete(DragObject dragObject, int x, int y, PointF vec); + void onFlingToDelete(DragObject dragObject, PointF vec); /** * Check if a drop action can occur at, or near, the requested location. diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index e0aeceae8..4af3fc63b 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -835,7 +835,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return true; } - public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { + @Override + public void onFlingToDelete(DragObject d, PointF vec) { // Do nothing } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 07d1c98f1..4c76092ea 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3917,7 +3917,7 @@ public class Workspace extends SmoothPagedView } @Override - public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { + public void onFlingToDelete(DragObject d, PointF vec) { // Do nothing } diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java new file mode 100644 index 000000000..55c5d7dc2 --- /dev/null +++ b/src/com/android/launcher3/util/FlingAnimation.java @@ -0,0 +1,104 @@ +package com.android.launcher3.util; + +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.graphics.PointF; +import android.graphics.Rect; +import android.view.animation.DecelerateInterpolator; + +import com.android.launcher3.DragLayer; +import com.android.launcher3.DragView; +import com.android.launcher3.DropTarget.DragObject; + +public class FlingAnimation implements AnimatorUpdateListener { + + /** + * Maximum acceleration in one dimension (pixels per milliseconds) + */ + private static final float MAX_ACCELERATION = 0.5f; + private static final int DRAG_END_DELAY = 300; + + protected final DragObject mDragObject; + protected final Rect mIconRect; + protected final DragLayer mDragLayer; + protected final Rect mFrom; + protected final int mDuration; + protected final float mUX, mUY; + protected final float mAnimationTimeFraction; + protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); + + protected float mAX, mAY; + + /** + * @param vel initial fling velocity in pixels per second. + */ + public FlingAnimation(DragObject d, PointF vel, Rect iconRect, DragLayer dragLayer) { + mDragObject = d; + mUX = vel.x / 1000; + mUY = vel.y / 1000; + mIconRect = iconRect; + + mDragLayer = dragLayer; + mFrom = new Rect(); + dragLayer.getViewRectRelativeToSelf(d.dragView, mFrom); + + float scale = d.dragView.getScaleX(); + float xOffset = ((scale - 1f) * d.dragView.getMeasuredWidth()) / 2f; + float yOffset = ((scale - 1f) * d.dragView.getMeasuredHeight()) / 2f; + mFrom.left += xOffset; + mFrom.right -= xOffset; + mFrom.top += yOffset; + mFrom.bottom -= yOffset; + + mDuration = initDuration(); + mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY); + } + + /** + * The fling animation is based on the following system + * - Apply a constant force in the y direction to causing the fling to decelerate. + * - The animation runs for the time taken by the object to go out of the screen. + * - Calculate a constant acceleration in x direction such that the object reaches + * {@link #mIconRect} in the given time. + */ + protected int initDuration() { + float sY = -mFrom.bottom; + + float d = mUY * mUY + 2 * sY * MAX_ACCELERATION; + if (d >= 0) { + // sY can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for y direction. + mAY = MAX_ACCELERATION; + } else { + // sY is not reachable, decrease the acceleration so that sY is almost reached. + d = 0; + mAY = mUY * mUY / (2 * -sY); + } + double t = (-mUY - Math.sqrt(d)) / mAY; + + float sX = -mFrom.exactCenterX() + mIconRect.exactCenterX(); + + // Find horizontal acceleration such that: u*t + a*t*t/2 = s + mAX = (float) ((sX - t * mUX) * 2 / (t * t)); + return (int) Math.round(t); + } + + public final int getDuration() { + return mDuration + DRAG_END_DELAY; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = animation.getAnimatedFraction(); + if (t > mAnimationTimeFraction) { + t = 1; + } else { + t = t / mAnimationTimeFraction; + } + final DragView dragView = (DragView) mDragLayer.getAnimatedView(); + final float time = t * mDuration; + dragView.setTranslationX(time * mUX + mFrom.left + mAX * time * time / 2); + dragView.setTranslationY(time * mUY + mFrom.top + mAY * time * time / 2); + dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); + } +} -- cgit v1.2.3 From 31abc291deb7a8879fc2ef675814efdb6fa9d56f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 1 May 2015 10:42:32 -0700 Subject: Clearing drag state after the close animation is complete Bug: 19350802 Change-Id: I048dfa2743692c6c7d56dcacab900ec2d17504ed --- src/com/android/launcher3/Folder.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index e0aeceae8..ed8eea73f 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -704,9 +704,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (mInfo.opened) { mLauncher.closeFolder(); mRearrangeOnClose = true; + } else if (mState == STATE_ANIMATING) { + mRearrangeOnClose = true; } else { rearrangeChildren(); + clearDragInfo(); } + } + + private void clearDragInfo() { mCurrentDragInfo = null; mCurrentDragView = null; mSuppressOnAdd = false; @@ -1037,6 +1043,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } mSuppressFolderDeletion = false; + clearDragInfo(); } @Thunk void replaceFolderWithFinalItem() { -- cgit v1.2.3 From ffae07e447d64d29eace9bed321d837aba214e4a Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 1 May 2015 10:55:53 -0700 Subject: WidgetTray UI - change text color for the section and widget name Change-Id: I8ce966d151bc62a9eb23ca3fa8c13c3aeb0c7f37 --- res/layout/widget_cell.xml | 4 ++-- res/values/colors.xml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index cb1c812cb..ab23b842e 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -44,7 +44,7 @@ android:ellipsize="end" android:fadingEdge="horizontal" - android:textColor="#FFFFFFFF" + android:textColor="@color/widgets_view_item_text_color" android:textSize="16sp" android:textAlignment="viewStart" android:fontFamily="sans-serif-condensed" @@ -61,7 +61,7 @@ android:layout_marginLeft="5dp" android:layout_weight="0" android:gravity="start" - android:textColor="#FFFFFFFF" + android:textColor="@color/widgets_view_item_text_color" android:textSize="16sp" android:textAlignment="viewStart" android:fontFamily="sans-serif-condensed" diff --git a/res/values/colors.xml b/res/values/colors.xml index 9695b2606..0c3714b03 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -42,6 +42,7 @@ #009688 - #009688 + #FFFFFF + #C4C4C4 #263238 -- cgit v1.2.3 From dfde999ccd0db397c7e4610444bb3fb69f7aa183 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 1 May 2015 12:31:32 -0700 Subject: Various managed profile fixes > When matching existing shortcut, match the uri as intent doesn't implement equals > Fixing user matching when searching for apps in all-apps Bug: 20749743 Change-Id: I14f3e2134e774727176e865d74108ef79de874d6 --- .../android/launcher3/AlphabeticalAppsList.java | 2 +- src/com/android/launcher3/LauncherModel.java | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 2ee5a62ed..c7ee2e99a 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -254,7 +254,7 @@ public class AlphabeticalAppsList { int length = apps.size(); for (int i = 0; i < length; ++i) { AppInfo info = apps.get(i); - if (info.user.equals(info.user) + if (info.user.equals(targetInfo.user) && info.intent.getComponent().equals(targetComponent)) { return i; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 97a283006..957073552 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -881,22 +881,22 @@ public class LauncherModel extends BroadcastReceiver * TODO: Throw exception is above condition is not met. */ @Thunk static boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) { - final Intent intentWithPkg, intentWithoutPkg; + final String intentWithPkg, intentWithoutPkg; final String packageName; if (intent.getComponent() != null) { // If component is not null, an intent with null package will produce // the same result and should also be a match. packageName = intent.getComponent().getPackageName(); if (intent.getPackage() != null) { - intentWithPkg = intent; - intentWithoutPkg = new Intent(intent).setPackage(null); + intentWithPkg = intent.toUri(0); + intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0); } else { - intentWithPkg = new Intent(intent).setPackage(packageName); - intentWithoutPkg = intent; + intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0); + intentWithoutPkg = intent.toUri(0); } } else { - intentWithPkg = intent; - intentWithoutPkg = intent; + intentWithPkg = intent.toUri(0); + intentWithoutPkg = intent.toUri(0); packageName = intent.getPackage(); } @@ -904,9 +904,11 @@ public class LauncherModel extends BroadcastReceiver for (ItemInfo item : sBgItemsIdMap) { if (item instanceof ShortcutInfo) { ShortcutInfo info = (ShortcutInfo) item; - if (intentWithPkg.equals(info.getIntent()) - || intentWithoutPkg.equals(info.getIntent())) { - return true; + if (info.getIntent() != null && info.user.equals(user)) { + String s = info.getIntent().toUri(0); + if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) { + return true; + } } } } -- cgit v1.2.3 From 957c13f032fc4088b3273cec08603c67c069e2c6 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 1 May 2015 13:02:20 -0700 Subject: Handling managed profile creation in the model as the activity can get killed in the background. Change-Id: I95f62cf268fe642c9a2bb4975eb92bf8e12d23a9 --- src/com/android/launcher3/Launcher.java | 5 ----- src/com/android/launcher3/LauncherAppState.java | 4 ++++ src/com/android/launcher3/LauncherModel.java | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2ef5d0ac4..7688a3dc7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1675,9 +1675,6 @@ public class Launcher extends Activity mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE, LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS); - } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) - || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { - getModel().forceReload(); } } }; @@ -1691,8 +1688,6 @@ public class Launcher extends Activity filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_USER_PRESENT); // For handling managed profiles - filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED); - filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); if (ENABLE_DEBUG_INTENTS) { filter.addAction(DebugIntents.DELETE_DATABASE); filter.addAction(DebugIntents.MIGRATE_DATABASE); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 7f31e4999..d51df32b0 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -112,6 +112,10 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); + // For handling managed profiles + filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); + sContext.registerReceiver(mModel, filter); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 957073552..e62c689fb 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1279,6 +1279,9 @@ public class LauncherModel extends BroadcastReceiver if (callbacks != null) { callbacks.bindSearchablesChanged(); } + } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) + || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { + forceReload(); } } -- cgit v1.2.3 From e809332032c8f0c2f361eb8fc753f3b57d9cfaaa Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 1 May 2015 14:21:57 -0700 Subject: Set preloading for the WidgetTray recycler view. b/20339302 Introduced a constant that we can use to tweak how much extra screen space worth of widgets are preloaded. Change-Id: I47f0fec134b4e268140482694a1903cf902731c6 --- src/com/android/launcher3/widget/WidgetsContainerView.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 8090c88a4..22e29f304 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -24,6 +24,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -62,6 +63,9 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, private static final int SPRING_MODE_DELAY_MS = 150; + /* Coefficient multiplied to the screen height for preloading widgets. */ + private static final int PRELOAD_SCREEN_HEIGHT_MULTIPLE = 1; + /* Global instances that are used inside this container. */ private Launcher mLauncher; private DragController mDragController; @@ -114,8 +118,15 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, } mView = (RecyclerView) findViewById(R.id.widgets_list_view); mView.setAdapter(mAdapter); - mView.setLayoutManager(new LinearLayoutManager(getContext())); + // This extends the layout space so that preloading happen for the {@link RecyclerView} + mView.setLayoutManager(new LinearLayoutManager(getContext()) { + @Override + protected int getExtraLayoutSpace(State state) { + return super.getExtraLayoutSpace(state) + + WidgetsContainerView.this.getHeight() * PRELOAD_SCREEN_HEIGHT_MULTIPLE; + } + }); mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); } -- cgit v1.2.3 From 756a28aeece3b00f40415f1fccae6f7d57104bfb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 23 Apr 2015 17:07:55 -0700 Subject: Adding workprofile shortcuts after the loader has completed Change-Id: Id466d8e164feeac427d969d6bc0ed3ca7a218ac4 --- src/com/android/launcher3/LauncherModel.java | 61 +++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 957073552..92f076f0b 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -107,6 +107,7 @@ public class LauncherModel extends BroadcastReceiver @Thunk DeferredHandler mHandler = new DeferredHandler(); @Thunk LoaderTask mLoaderTask; @Thunk boolean mIsLoaderTaskRunning; + @Thunk boolean mHasLoaderCompletedOnce; private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings"; @@ -128,6 +129,12 @@ public class LauncherModel extends BroadcastReceiver // a normal load, we also clear this set of Runnables. static final ArrayList mDeferredBindRunnables = new ArrayList(); + /** + * Set of runnables to be called on the background thread after the workspace binding + * is complete. + */ + static final ArrayList mBindCompleteRunnables = new ArrayList(); + @Thunk WeakReference mCallbacks; // < only access in worker thread > @@ -263,6 +270,19 @@ public class LauncherModel extends BroadcastReceiver } } + /** + * Runs the specified runnable after the loader is complete + */ + private void runAfterBindCompletes(Runnable r) { + if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) { + synchronized (mBindCompleteRunnables) { + mBindCompleteRunnables.add(r); + } + } else { + runOnWorkerThread(r); + } + } + boolean canMigrateFromOldLauncherDb(Launcher launcher) { return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ; } @@ -424,7 +444,7 @@ public class LauncherModel extends BroadcastReceiver * Find a position on the screen for the given size or adds a new screen. * @return screenId and the coordinates for the item. */ - @Thunk static Pair findSpaceForItem( + @Thunk Pair findSpaceForItem( Context context, ArrayList workspaceScreens, ArrayList addedWorkspaceScreensFinal, @@ -432,7 +452,7 @@ public class LauncherModel extends BroadcastReceiver LongSparseArray> screenItems = new LongSparseArray<>(); // Use sBgItemsIdMap as all the items are already loaded. - // TODO: Throw exception is above condition is not met. + assertWorkspaceLoaded(); synchronized (sBgLock) { for (ItemInfo info : sBgItemsIdMap) { if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { @@ -875,12 +895,18 @@ public class LauncherModel extends BroadcastReceiver updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase"); } + private void assertWorkspaceLoaded() { + if (LauncherAppState.isDogfoodBuild() && (isLoadingWorkspace() || !mHasLoaderCompletedOnce)) { + throw new RuntimeException("Trying to add shortcut while loader is running"); + } + } + /** * Returns true if the shortcuts already exists on the workspace. This must be called after * the workspace has been loaded. We identify a shortcut by its intent. - * TODO: Throw exception is above condition is not met. */ - @Thunk static boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) { + @Thunk boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) { + assertWorkspaceLoaded(); final String intentWithPkg, intentWithoutPkg; final String packageName; if (intent.getComponent() != null) { @@ -1387,6 +1413,16 @@ public class LauncherModel extends BroadcastReceiver mHandler.post(r); } } + + // Run all the bind complete runnables after workspace is bound. + if (!mBindCompleteRunnables.isEmpty()) { + synchronized (mBindCompleteRunnables) { + for (final Runnable r : mBindCompleteRunnables) { + runOnWorkerThread(r); + } + mBindCompleteRunnables.clear(); + } + } } public void stopLoader() { @@ -1615,6 +1651,7 @@ public class LauncherModel extends BroadcastReceiver mLoaderTask = null; } mIsLoaderTaskRunning = false; + mHasLoaderCompletedOnce = true; } } @@ -2794,7 +2831,7 @@ public class LauncherModel extends BroadcastReceiver for (UserHandleCompat user : profiles) { // Query for the set of apps final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - List apps = mLauncherApps.getActivityList(null, user); + final List apps = mLauncherApps.getActivityList(null, user); if (DEBUG_LOADERS) { Log.d(TAG, "getActivityList took " + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); @@ -2849,11 +2886,15 @@ public class LauncherModel extends BroadcastReceiver mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache)); } - if (!user.equals(UserHandleCompat.myUserHandle())) { - ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); - if (heuristic != null) { - heuristic.processUserApps(apps); - } + final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); + if (heuristic != null) { + runAfterBindCompletes(new Runnable() { + + @Override + public void run() { + heuristic.processUserApps(apps); + } + }); } } // Huh? Shouldn't this be inside the Runnable below? -- cgit v1.2.3 From 3290ec4cc220f97460b3b19f28adaf829ea971e1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 4 May 2015 13:53:42 -0700 Subject: Preventing launcher crash because of invalid install shortcut intent Change-Id: Ibede31c8f018b17da5f272aa505a31f40a1b76f3 --- src/com/android/launcher3/InstallShortcutReceiver.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 27dda6404..23bcc8577 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -146,10 +146,13 @@ public class InstallShortcutReceiver extends BroadcastReceiver { return; } - if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0)); PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context); - info = convertToLauncherActivityIfPossible(info); + if (info.launchIntent == null || info.label == null) { + if (DBG) Log.e(TAG, "Invalid install shortcut intent"); + return; + } + info = convertToLauncherActivityIfPossible(info); queuePendingShortcutInfo(info, context); } -- cgit v1.2.3 From cd99cd3ed6a84b9dcd8f4c60b0941760f85a64ce Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 29 Apr 2015 11:03:24 -0700 Subject: Launcher-side changes to support prediction bar. Change-Id: Ib9974ad8888ad87137c774f9f531fae9d56b899a --- src/com/android/launcher3/AppsContainerView.java | 15 +--- src/com/android/launcher3/Launcher.java | 79 +++++++++++++++++----- src/com/android/launcher3/LauncherCallbacks.java | 1 + src/com/android/launcher3/LauncherExtension.java | 4 ++ .../LauncherStateTransitionAnimation.java | 8 +-- src/com/android/launcher3/SearchDropTargetBar.java | 6 -- src/com/android/launcher3/Workspace.java | 9 --- 7 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index b24714505..f7adaf81d 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -269,19 +269,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett // Start the drag mLauncher.getWorkspace().beginDragShared(v, mLastTouchPos, this, false); - - // We delay entering spring-loaded mode slightly to make sure the UI - // thready is free of any work. - postDelayed(new Runnable() { - @Override - public void run() { - // We don't enter spring-loaded mode if the drag has been cancelled - if (mLauncher.getDragController().isDragging()) { - // Go into spring loaded mode (must happen before we startDrag()) - mLauncher.enterSpringLoadedDragMode(); - } - } - }, 150); + // Enter spring loaded mode + mLauncher.enterSpringLoadedDragMode(); return false; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8c920f0b8..a6752ddc3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -522,13 +522,19 @@ public class Launcher extends Activity mLauncherCallbacks.setLauncherAppsCallback(new Launcher.LauncherAppsCallbacks() { @Override public void onAllAppsBoundsChanged(Rect bounds) { + if (LOGD) { + Log.d(TAG, "onAllAppsBoundsChanged(Rect): " + bounds); + } mAppsView.setFixedBounds(Launcher.this, bounds); } @Override public void dismissAllApps() { - showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, null, - false /* notifyLauncherCallbacks */); + // Dismiss All Apps if we aren't already paused/invisible + if (!mPaused) { + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, + null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */); + } } }); return true; @@ -1012,6 +1018,13 @@ public class Launcher extends Activity } mOnResumeState = State.NONE; + // Restore the apps state if we are in all apps + if (mState == State.APPS) { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onAllAppsShown(); + } + } + // Background was set to gradient in onPause(), restore to black if in all apps. setWorkspaceBackground(mState == State.WORKSPACE); @@ -1072,7 +1085,7 @@ public class Launcher extends Activity mWorkspace.getCustomContentCallbacks().onShow(true); } } - mWorkspace.updateInteractionForState(); + updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); mWorkspace.onResume(); if (!isWorkspaceLoading()) { @@ -2101,8 +2114,6 @@ public class Launcher extends Activity public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { - showWorkspace(true); - if (initialQuery == null) { // Use any text typed in the launcher as the initial query initialQuery = getTypedText(); @@ -2121,6 +2132,9 @@ public class Launcher extends Activity if (clearTextImmediately) { clearTypedText(); } + + // We need to show the workspace after starting the search + showWorkspace(true); } /** @@ -2866,6 +2880,21 @@ public class Launcher extends Activity } } + /** Updates the interaction state. */ + public void updateInteraction(Workspace.State fromState, Workspace.State toState) { + // Only update the interacting state if we are transitioning to/from a view without an + // overlay + boolean fromStateWithoutOverlay = fromState != Workspace.State.NORMAL && + fromState != Workspace.State.NORMAL_HIDDEN; + boolean toStateWithoutOverlay = toState != Workspace.State.NORMAL && + toState != Workspace.State.NORMAL_HIDDEN; + if (toStateWithoutOverlay) { + onInteractionBegin(); + } else if (fromStateWithoutOverlay) { + onInteractionEnd(); + } + } + void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) { try { LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this); @@ -3170,7 +3199,6 @@ public class Launcher extends Activity if (v instanceof Workspace) { if (!mWorkspace.isInOverviewMode()) { - if (!mWorkspace.isTouchActive()) { showOverviewMode(true); mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, @@ -3342,8 +3370,12 @@ public class Launcher extends Activity // Send an accessibility event to announce the context change getWindow().getDecorView() .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - - onWorkspaceShown(animated); + if (notifyLauncherCallbacks) { + // Dismiss all apps when the workspace is shown + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onAllAppsHidden(); + } + } } } @@ -3353,10 +3385,6 @@ public class Launcher extends Activity WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null /* onCompleteRunnable */); mState = State.WORKSPACE; - onWorkspaceShown(animated); - } - - public void onWorkspaceShown(boolean animated) { } /** @@ -3416,6 +3444,18 @@ public class Launcher extends Activity .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } + /** + * Updates the workspace and interaction state on state change, and return the animation to this + * new state. + */ + public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage, + boolean animated, HashMap layerViews) { + Workspace.State fromState = mWorkspace.getState(); + Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews); + updateInteraction(fromState, toState); + return anim; + } + public void enterSpringLoadedDragMode() { Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name())); @@ -3434,6 +3474,14 @@ public class Launcher extends Activity final Runnable onCompleteRunnable) { if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; + if (successfulDrop) { + // We need to trigger all apps hidden to notify search to update itself before the + // delayed call to showWorkspace below + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onAllAppsHidden(); + } + } + mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -3454,13 +3502,10 @@ public class Launcher extends Activity void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { - mStateTransitionAnimation.startAnimationToAllApps(true /* animated */); - mState = State.APPS; + showAppsView(true, false); } else if (mState == State.WIDGETS_SPRING_LOADED) { - mStateTransitionAnimation.startAnimationToWidgets(true /* animated */); - mState = State.WIDGETS; + showWidgetsView(true, false); } - // Otherwise, we are not in spring loaded mode, so don't do anything. } void lockAllApps() { diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 2fee81c3d..25c86c977 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -51,6 +51,7 @@ public interface LauncherCallbacks { public void finishBindingItems(final boolean upgradePath); public void onClickAllAppsButton(View v); public void onAllAppsShown(); + public void onAllAppsHidden(); public void bindAllApplications(ArrayList apps); public void onClickFolderIcon(View v); public void onClickAppShortcut(View v); diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index e4fdbbc7c..8174af045 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -127,6 +127,10 @@ public class LauncherExtension extends Launcher { public void onAllAppsShown() { } + @Override + public void onAllAppsHidden() { + } + @Override public void bindAllApplications(ArrayList apps) { } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index f91cfa07b..1effe1e37 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -249,8 +249,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation( - toWorkspaceState, -1, animated, layerViews); + Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1, + animated, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); @@ -546,8 +546,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation( - toWorkspaceState, toWorkspacePage, animated, layerViews); + Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, + toWorkspacePage, animated, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index 44a76b73b..4cdf1cac9 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -180,9 +180,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D prepareStartAnimation(mDropTargetBar); mShowDropTargetBarAnim.start(); hideSearchBar(true); - if (mQSBSearchBar != null) { - mQSBSearchBar.setVisibility(View.GONE); - } } /** @@ -193,9 +190,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D prepareStartAnimation(mDropTargetBar); mShowDropTargetBarAnim.reverse(); showSearchBar(true); - if (mQSBSearchBar != null) { - mQSBSearchBar.setVisibility(View.VISIBLE); - } } /* diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 2efd20739..9acdcae98 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2040,14 +2040,6 @@ public class Workspace extends SmoothPagedView return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview; } - public void updateInteractionForState() { - if (mState != State.NORMAL) { - mLauncher.onInteractionBegin(); - } else { - mLauncher.onInteractionEnd(); - } - } - /** * Sets the current workspace {@link State}, returning an animation transitioning the workspace * to that new state. @@ -2060,7 +2052,6 @@ public class Workspace extends SmoothPagedView // Update the current state mState = toState; - updateInteractionForState(); updateAccessibilityFlags(); return workspaceAnim; -- cgit v1.2.3 From 8821ca9a6a5bf34308f9b70c2cabf9da17330497 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 4 May 2015 16:28:20 -0700 Subject: WidgetTray performance improvement (less sorting): Part 1 - No longer return sorted list from the package manager since, the only time the widget list requires to be sorted is when recycler view renders them. - Made getWidgetsAndShortcuts private to guarantee that widgets are being loaded in the worker thread. Change-Id: I2c35973c1226e831514380dd38fc2f88b1b91d02 --- src/com/android/launcher3/Launcher.java | 28 ++++++++-------------- src/com/android/launcher3/LauncherModel.java | 22 +++++++++-------- src/com/android/launcher3/WidgetPreviewLoader.java | 11 +++++++-- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2ef5d0ac4..efe27ad6c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3584,14 +3584,13 @@ public class Launcher extends Activity * in onResume. * * This needs to be called from incoming places where resources might have been loaded - * while we are paused. That is becaues the Configuration might be wrong - * when we're not running, and if it comes back to what it was when we - * were paused, we are not restarted. + * while the activity is paused. That is because the Configuration (e.g., rotation) might be + * wrong when we're not running, and if the activity comes back to what the configuration was + * when we were paused, activity is not restarted. * * Implementation of the method from LauncherModel.Callbacks. * - * @return true if we are currently paused. The caller might be able to - * skip some work in that case since we will come back again. + * @return {@code true} if we are currently paused. The caller might be able to skip some work */ private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { if (mPaused) { @@ -4135,10 +4134,6 @@ public class Launcher extends Activity if (mAppsView != null) { mAppsView.setApps(apps); } - if (mWidgetsView != null) { - mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false), - getPackageManager()); - } if (mLauncherCallbacks != null) { mLauncherCallbacks.bindAllApplications(apps); } @@ -4274,26 +4269,23 @@ public class Launcher extends Activity } } - /** - * A number of packages were updated. - */ @Thunk ArrayList mWidgetsAndShortcuts; private Runnable mBindPackagesUpdatedRunnable = new Runnable() { public void run() { - bindPackagesUpdated(mWidgetsAndShortcuts); - mWidgetsAndShortcuts = null; + bindAllPackages(mWidgetsAndShortcuts); } }; - public void bindPackagesUpdated(final ArrayList widgetsAndShortcuts) { + @Override + public void bindAllPackages(final ArrayList widgetsAndShortcuts) { if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) { mWidgetsAndShortcuts = widgetsAndShortcuts; return; } - if (mWidgetsView != null) { - mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false), - getPackageManager()); + if (mWidgetsView != null && widgetsAndShortcuts != null) { + mWidgetsView.addWidgets(widgetsAndShortcuts, getPackageManager()); + mWidgetsAndShortcuts = null; } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 97a283006..0624478ec 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -197,7 +197,7 @@ public class LauncherModel extends BroadcastReceiver public void bindRestoreItemsChange(HashSet updates); public void bindComponentsRemoved(ArrayList packageNames, ArrayList appInfos, UserHandleCompat user, int reason); - public void bindPackagesUpdated(ArrayList widgetsAndShortcuts); + public void bindAllPackages(ArrayList widgetsAndShortcuts); public void bindSearchablesChanged(); public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); @@ -1594,9 +1594,6 @@ public class LauncherModel extends BroadcastReceiver if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); loadAndBindAllApps(); - // Remove entries for packages which changed while the launcher was dead. - LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(); - // Restore the default thread priority after we are done loading items synchronized (mLock) { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); @@ -2865,6 +2862,7 @@ public class LauncherModel extends BroadcastReceiver final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAllApplications(added); + loadAndBindWidgetsAndShortcuts(mContext,callbacks); if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - bindTime) + "ms"); @@ -3280,29 +3278,33 @@ public class LauncherModel extends BroadcastReceiver runOnWorkerThread(new Runnable(){ @Override public void run() { - final ArrayList list = - getSortedWidgetsAndShortcuts(context, true /* refresh */); + final ArrayList list = getWidgetsAndShortcuts(context, true /* refresh */); mHandler.post(new Runnable() { @Override public void run() { Callbacks cb = getCallback(); if (callbacks == cb && cb != null) { - callbacks.bindPackagesUpdated(list); + callbacks.bindAllPackages(list); } } }); + // update the Widget entries inside DB on the worker thread. + LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(list); } }); } - // Returns a list of ResolveInfos/AppWidgetInfos in sorted order - public static ArrayList getSortedWidgetsAndShortcuts(Context context, boolean refresh) { + /** + * Returns a list of ResolveInfos/AppWidgetInfos. + * + * @see #loadAndBindWidgetsAndShortcuts + */ + private ArrayList getWidgetsAndShortcuts(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); - Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context)); return widgetsAndShortcuts; } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 412cbcd6d..93bfeaffd 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -24,6 +24,7 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Process; import android.util.Log; import android.util.LongSparseArray; @@ -34,6 +35,9 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetCell; +import junit.framework.Assert; + +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -202,11 +206,14 @@ public class WidgetPreviewLoader { * 2. Any preview for an absent package is removed * This ensures that we remove entries for packages which changed while the launcher was dead. */ - public void removeObsoletePreviews() { + public void removeObsoletePreviews(ArrayList list) { + // This method should always be called from the worker thread. + Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid()); + LongSparseArray userIdCache = new LongSparseArray<>(); LongSparseArray> validPackages = new LongSparseArray<>(); - for (Object obj : LauncherModel.getSortedWidgetsAndShortcuts(mContext, false)) { + for (Object obj : list) { final UserHandleCompat user; final String pkg; if (obj instanceof ResolveInfo) { -- cgit v1.2.3 From 3857d7aeb320476419d81ecfa1895bbe769a0bc6 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 4 May 2015 15:28:34 -0700 Subject: Fixing crash in All Apps. Bug 20431579 Change-Id: Iba6ce88a931cb56f111f5b2ea44f81c5059a934f --- src/com/android/launcher3/AppsGridAdapter.java | 75 +++++++++++++++----------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 954c59ffc..5bc3981df 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -33,13 +33,13 @@ class AppsGridAdapter extends RecyclerView.Adapter { */ public static class ViewHolder extends RecyclerView.ViewHolder { public View mContent; - public boolean mIsSectionRow; + public boolean mIsSectionHeader; public boolean mIsEmptyRow; - public ViewHolder(View v, boolean isSectionRow, boolean isEmptyRow) { + public ViewHolder(View v, boolean isSectionHeader, boolean isEmptyRow) { super(v); mContent = v; - mIsSectionRow = isSectionRow; + mIsSectionHeader = isSectionHeader; mIsEmptyRow = isEmptyRow; } } @@ -72,36 +72,26 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { List items = mApps.getAdapterItems(); - if (items.isEmpty()) { - return; - } - for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); - if (holder != null) { - GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) - child.getLayoutParams(); - if (!holder.mIsSectionRow && !holder.mIsEmptyRow && !lp.isItemRemoved()) { - if (items.get(holder.getPosition() - 1).isSectionHeader) { - // Draw at the parent - AlphabeticalAppsList.AdapterItem item = - items.get(holder.getPosition()); - String section = item.sectionName; - mSectionTextPaint.getTextBounds(section, 0, section.length(), - mTmpBounds); - if (mIsRtl) { - int left = parent.getWidth() - mPaddingStart - mStartMargin; - c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, - child.getTop() + (2 * child.getPaddingTop()) + - mTmpBounds.height(), mSectionTextPaint); - } else { - int left = mPaddingStart; - c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, - child.getTop() + (2 * child.getPaddingTop()) + - mTmpBounds.height(), mSectionTextPaint); - } - } + if (shouldDrawItemSection(holder, child, items)) { + // Draw at the parent + AlphabeticalAppsList.AdapterItem item = + items.get(holder.getPosition()); + String section = item.sectionName; + mSectionTextPaint.getTextBounds(section, 0, section.length(), + mTmpBounds); + if (mIsRtl) { + int left = parent.getWidth() - mPaddingStart - mStartMargin; + c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, + child.getTop() + (2 * child.getPaddingTop()) + + mTmpBounds.height(), mSectionTextPaint); + } else { + int left = mPaddingStart; + c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, + child.getTop() + (2 * child.getPaddingTop()) + + mTmpBounds.height(), mSectionTextPaint); } } } @@ -112,6 +102,31 @@ class AppsGridAdapter extends RecyclerView.Adapter { RecyclerView.State state) { // Do nothing } + + private boolean shouldDrawItemSection(ViewHolder holder, View child, + List items) { + // Ensure item is not already removed + GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) + child.getLayoutParams(); + if (lp.isItemRemoved()) { + return false; + } + // Ensure we have a valid holder + if (holder == null) { + return false; + } + // Ensure it's not an empty row + if (holder.mIsEmptyRow) { + return false; + } + // Ensure we have a holder position + int pos = holder.getPosition(); + if (pos <= 0 || pos >= items.size()) { + return false; + } + // Only draw the first item in the section (the first one after the section header) + return items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader; + } } private LayoutInflater mLayoutInflater; -- cgit v1.2.3 From 227239e7c05d26c4098980b991d3472077c01ccb Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 4 May 2015 18:17:35 -0700 Subject: update widgets model when package is updated Fixing a bug where onProviderChanged is called before Package update. And icon loaded on the widget tray is the default icon. Change-Id: I4ffea846d057920fd894537432ac5881642bc570 --- src/com/android/launcher3/LauncherAppWidgetHost.java | 3 ++- src/com/android/launcher3/LauncherModel.java | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index e32e0d9fe..c274f2ecd 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -79,7 +79,8 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } protected void onProvidersChanged() { - mLauncher.getModel().loadAndBindWidgetsAndShortcuts(mLauncher, mLauncher); + mLauncher.getModel().loadAndBindWidgetsAndShortcuts(mLauncher, mLauncher, + true /* refresh */); if (!mProviderChangeListeners.isEmpty()) { for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) { callback.run(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7fdd52327..f283c2f31 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2867,7 +2867,7 @@ public class LauncherModel extends BroadcastReceiver final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAllApplications(added); - loadAndBindWidgetsAndShortcuts(mContext,callbacks); + loadAndBindWidgetsAndShortcuts(mContext, callbacks, true /* refresh */); if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - bindTime) + "ms"); @@ -3228,9 +3228,10 @@ public class LauncherModel extends BroadcastReceiver } }); } - if (Build.VERSION.SDK_INT < 17) { - loadAndBindWidgetsAndShortcuts(context, callbacks); - } + + // onProvidersChanged method (API >= 17) already refreshed the widget list + loadAndBindWidgetsAndShortcuts(context, callbacks, Build.VERSION.SDK_INT < 17); + // Write all the logs to disk mHandler.post(new Runnable() { public void run() { @@ -3279,11 +3280,12 @@ public class LauncherModel extends BroadcastReceiver } } - public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks) { + public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks, + final boolean refresh) { runOnWorkerThread(new Runnable(){ @Override public void run() { - final ArrayList list = getWidgetsAndShortcuts(context, true /* refresh */); + final ArrayList list = getWidgetsAndShortcuts(context, refresh); mHandler.post(new Runnable() { @Override public void run() { -- cgit v1.2.3 From bb785c6eb38f9c4b1f71920cb6e57ca8f11d4908 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 5 May 2015 10:05:08 -0700 Subject: Ensuring we wait until resume before binding all apps. Bug: 20759810 --- src/com/android/launcher3/Launcher.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4533089e9..339b4e498 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4165,12 +4165,29 @@ public class Launcher extends Activity mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); } + /** + * 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 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; + return; + } + if (mAppsView != null) { mAppsView.setApps(apps); } -- cgit v1.2.3 From 9892e5874061233dec57107d1d3d7d5d8e1f5d93 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 5 May 2015 10:07:23 -0700 Subject: Use valid context inside LauncherModel when calling loadAndBindXXX b/20851075 Change-Id: Ie3feb387a142d4e11a0a18bbcc4fd4932cfe4439 --- src/com/android/launcher3/LauncherModel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f283c2f31..95ff6a49b 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2863,11 +2863,13 @@ public class LauncherModel extends BroadcastReceiver // Post callback on main thread mHandler.post(new Runnable() { public void run() { + final long bindTime = SystemClock.uptimeMillis(); final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAllApplications(added); - loadAndBindWidgetsAndShortcuts(mContext, callbacks, true /* refresh */); + loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, + true /* refresh */); if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - bindTime) + "ms"); -- cgit v1.2.3 From ccc414bb1e18206d2a3d8d797070278bdb286354 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 30 Apr 2015 12:28:16 -0700 Subject: Accessibility: Folder drag and drop fixes > Do not allow empty cells on the last page to be click targets > Make drop targets accessible > Do not close folder on tap outside Change-Id: I7cae20e45097092f41408b888e20b6c40c17d981 --- src/com/android/launcher3/DragLayer.java | 49 +++++++++++++++++----- src/com/android/launcher3/Folder.java | 3 ++ src/com/android/launcher3/FolderPagedView.java | 4 ++ .../accessibility/FolderAccessibilityHelper.java | 16 ++++--- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index 91f97fa4a..2efdb06b9 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -38,7 +38,6 @@ import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.TextView; -import com.android.launcher3.InsettableFrameLayout.LayoutParams; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -153,6 +152,14 @@ public class DragLayer extends InsettableFrameLayout { return false; } + private boolean isEventOverDropTargetBar(MotionEvent ev) { + getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect); + if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { + return true; + } + return false; + } + public void setBlockTouch(boolean block) { mBlockTouches = block; } @@ -188,10 +195,16 @@ public class DragLayer extends InsettableFrameLayout { } } - getDescendantRectRelativeToSelf(currentFolder, hitRect); if (!isEventOverFolder(currentFolder, ev)) { - mLauncher.closeFolder(); - return true; + if (isInAccessibleDrag()) { + // Do not close the folder if in drag and drop. + if (!isEventOverDropTargetBar(ev)) { + return true; + } + } else { + mLauncher.closeFolder(); + return true; + } } } return false; @@ -228,11 +241,12 @@ public class DragLayer extends InsettableFrameLayout { getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (accessibilityManager.isTouchExplorationEnabled()) { final int action = ev.getAction(); - boolean isOverFolder; + boolean isOverFolderOrSearchBar; switch (action) { case MotionEvent.ACTION_HOVER_ENTER: - isOverFolder = isEventOverFolder(currentFolder, ev); - if (!isOverFolder) { + isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || + (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); + if (!isOverFolderOrSearchBar) { sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); mHoverPointClosesFolder = true; return true; @@ -240,12 +254,13 @@ public class DragLayer extends InsettableFrameLayout { mHoverPointClosesFolder = false; break; case MotionEvent.ACTION_HOVER_MOVE: - isOverFolder = isEventOverFolder(currentFolder, ev); - if (!isOverFolder && !mHoverPointClosesFolder) { + isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) || + (isInAccessibleDrag() && isEventOverDropTargetBar(ev)); + if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) { sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); mHoverPointClosesFolder = true; return true; - } else if (!isOverFolder) { + } else if (!isOverFolderOrSearchBar) { return true; } mHoverPointClosesFolder = false; @@ -268,6 +283,12 @@ public class DragLayer extends InsettableFrameLayout { } } + private boolean isInAccessibleDrag() { + LauncherAccessibilityDelegate delegate = LauncherAppState + .getInstance().getAccessibilityDelegate(); + return delegate != null && delegate.isInAccessibleDrag(); + } + @Override public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { Folder currentFolder = mLauncher.getWorkspace().getOpenFolder(); @@ -275,6 +296,10 @@ public class DragLayer extends InsettableFrameLayout { if (child == currentFolder) { return super.onRequestSendAccessibilityEvent(child, event); } + + if (isInAccessibleDrag() && child instanceof SearchDropTargetBar) { + return super.onRequestSendAccessibilityEvent(child, event); + } // Skip propagating onRequestSendAccessibilityEvent all for other children // when a folder is open return false; @@ -288,6 +313,10 @@ public class DragLayer extends InsettableFrameLayout { if (currentFolder != null) { // Only add the folder as a child for accessibility when it is open childrenForAccessibility.add(currentFolder); + + if (isInAccessibleDrag()) { + childrenForAccessibility.add(mLauncher.getSearchBar()); + } } else { super.addChildrenForAccessibility(childrenForAccessibility); } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index a2828054c..b1aba63c3 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -275,6 +275,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList for (int i = 0; i < mContent.getChildCount(); i++) { mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG); } + + mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : + IMPORTANT_FOR_ACCESSIBILITY_AUTO); mLauncher.getWorkspace().setAddNewPageOnDrag(!enable); } diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index a07a3dc2c..1d198ba14 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -514,6 +514,10 @@ public class FolderPagedView extends PagedView { } } + public int getAllocatedContentSize() { + return mAllocatedContentSize; + } + /** * Reorders the items such that the {@param empty} spot moves to {@param target} */ diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java index fc105b4a4..ff9989036 100644 --- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java +++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java @@ -24,23 +24,29 @@ import com.android.launcher3.R; * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder. */ public class FolderAccessibilityHelper extends DragAndDropAccessibilityDelegate { + + /** + * 0-index position for the first cell in {@link #mView} in {@link #mParent}. + */ private final int mStartPosition; + private final FolderPagedView mParent; + public FolderAccessibilityHelper(CellLayout layout) { super(layout); - FolderPagedView parent = (FolderPagedView) layout.getParent(); + mParent = (FolderPagedView) layout.getParent(); - int index = parent.indexOfChild(layout); - mStartPosition = 1 + index * layout.getCountX() * layout.getCountY(); + int index = mParent.indexOfChild(layout); + mStartPosition = index * layout.getCountX() * layout.getCountY(); } @Override protected int intersectsValidDropTarget(int id) { - return id; + return Math.min(id, mParent.getAllocatedContentSize() - mStartPosition - 1); } @Override protected String getLocationDescriptionForIconDrop(int id) { - return mContext.getString(R.string.move_to_position, id + mStartPosition); + return mContext.getString(R.string.move_to_position, id + mStartPosition + 1); } @Override -- cgit v1.2.3 From ed4121e1fc3e9d5ea189ab57a08447993d9e0d50 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 5 May 2015 14:30:44 -0700 Subject: Fixing all apps relayout issue. Bug: 20617223 Change-Id: Ia7efbdb1c8744ca58070b6b20e96bcb19389c0d1 --- src/com/android/launcher3/AppsContainerView.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index f7adaf81d..c3cf629b8 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -232,8 +232,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mFixedBounds.set(fixedBounds); } - updateBackgrounds(); - updatePaddings(); + // Post the updates since they can trigger a relayout, and this call can be triggered from + // a layout pass itself. + post(new Runnable() { + @Override + public void run() { + updateBackgrounds(); + updatePaddings(); + } + }); } @Override -- cgit v1.2.3 From 1d08f70441999c66b76c97e48b4149e1433be3c3 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 4 May 2015 15:50:25 -0700 Subject: Accessibility: Page re-ordering in overview mode Change-Id: I5fc0ad326a63b6768cb1fae55ee6e05a9fc2b659 --- res/values/config.xml | 2 + res/values/strings.xml | 9 +++ src/com/android/launcher3/PagedView.java | 6 +- src/com/android/launcher3/Workspace.java | 59 +++++++++++--- .../WorkspaceStateTransitionAnimation.java | 17 +++- .../OverviewScreenAccessibilityDelegate.java | 92 ++++++++++++++++++++++ 6 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java diff --git a/res/values/config.xml b/res/values/config.xml index 21e1d6987..6ef863532 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -100,4 +100,6 @@ + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 57f23ae98..1681fc626 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -243,4 +243,13 @@ Move to home screen + + + Move screen to left + + + Move screen to right + + + Screen moved diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index a4593ecb4..0739babea 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -559,7 +559,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** * Sets the current page. */ - void setCurrentPage(int currentPage) { + public void setCurrentPage(int currentPage) { if (!mScroller.isFinished()) { abortScrollerAnimation(true); } @@ -2535,7 +2535,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected void onStartReordering() { + public void onStartReordering() { // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.) mTouchState = TOUCH_STATE_REORDERING; mIsReordering = true; @@ -2555,7 +2555,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected void onEndReordering() { + public void onEndReordering() { mIsReordering = false; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index e7a41e09b..f2fa59bcd 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -63,6 +63,7 @@ import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; +import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; @@ -277,6 +278,8 @@ public class Workspace extends SmoothPagedView // Handles workspace state transitions private WorkspaceStateTransitionAnimation mStateTransitionAnimation; + private AccessibilityDelegate mPagesAccessibilityDelegate; + private final Runnable mBindPages = new Runnable() { @Override public void run() { @@ -2000,14 +2003,14 @@ public class Workspace extends SmoothPagedView range[1] = Math.max(0, end); } - protected void onStartReordering() { + public void onStartReordering() { super.onStartReordering(); showOutlines(); // Reordering handles its own animations, disable the automatic ones. disableLayoutTransitions(); } - protected void onEndReordering() { + public void onEndReordering() { super.onEndReordering(); if (mLauncher.isWorkspaceLoading()) { @@ -2068,11 +2071,45 @@ public class Workspace extends SmoothPagedView return mState; } - private void updateAccessibilityFlags() { - int accessible = mState == State.NORMAL ? - IMPORTANT_FOR_ACCESSIBILITY_NO : - IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; - setImportantForAccessibility(accessible); + public void updateAccessibilityFlags() { + if (Utilities.isLmpOrAbove()) { + int total = getPageCount(); + for (int i = numCustomPages(); i < total; i++) { + updateAccessibilityFlags((CellLayout) getPageAt(i), i); + } + if (mState == State.NORMAL) { + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + } else { + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + } + } else { + int accessible = mState == State.NORMAL ? + IMPORTANT_FOR_ACCESSIBILITY_NO : + IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; + setImportantForAccessibility(accessible); + } + } + + private void updateAccessibilityFlags(CellLayout page, int pageNo) { + if (mState == State.OVERVIEW) { + page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + page.getShortcutsAndWidgets().setImportantForAccessibility( + IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + page.setContentDescription(getPageDescription(pageNo)); + + if (mPagesAccessibilityDelegate == null) { + mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this); + } + page.setAccessibilityDelegate(mPagesAccessibilityDelegate); + } else { + int accessible = mState == State.NORMAL ? + IMPORTANT_FOR_ACCESSIBILITY_NO : + IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; + page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + page.getShortcutsAndWidgets().setImportantForAccessibility(accessible); + page.setContentDescription(null); + page.setAccessibilityDelegate(null); + } } @Override @@ -4460,11 +4497,15 @@ public class Workspace extends SmoothPagedView } protected String getCurrentPageDescription() { - int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; - int delta = numCustomPages(); if (hasCustomContent() && getNextPage() == 0) { return mCustomContentDescription; } + int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; + return getPageDescription(page); + } + + private String getPageDescription(int page) { + int delta = numCustomPages(); return String.format(getContext().getString(R.string.workspace_scroll_format), page + 1 - delta, getChildCount() - delta); } diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index a0cedeb63..61a64e3f3 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -24,8 +24,11 @@ import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; + import com.android.launcher3.util.Thunk; import java.util.HashMap; @@ -190,7 +193,7 @@ public class WorkspaceStateTransitionAnimation { final HashMap layerViews) { AccessibilityManager am = (AccessibilityManager) mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); - boolean accessibilityEnabled = am.isEnabled(); + final boolean accessibilityEnabled = am.isEnabled(); // Reinitialize animation arrays for the current workspace state reinitializeAnimationArrays(); @@ -301,7 +304,7 @@ public class WorkspaceStateTransitionAnimation { } final View searchBar = mLauncher.getOrCreateQsbBar(); - final View overviewPanel = mLauncher.getOverviewPanel(); + final ViewGroup overviewPanel = mLauncher.getOverviewPanel(); final View hotseat = mLauncher.getHotseat(); final View pageIndicator = mWorkspace.getPageIndicator(); if (animated) { @@ -424,6 +427,11 @@ public class WorkspaceStateTransitionAnimation { @Override public void onAnimationEnd(Animator animation) { mStateAnimator = null; + + if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { + overviewPanel.getChildAt(0).performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } } }); } else { @@ -443,6 +451,11 @@ public class WorkspaceStateTransitionAnimation { mWorkspace.setScaleX(mNewScale); mWorkspace.setScaleY(mNewScale); mWorkspace.setTranslationY(finalWorkspaceTranslationY); + + if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { + overviewPanel.getChildAt(0).performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } } if (stateIsNormal) { diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java new file mode 100644 index 000000000..d3f5230b2 --- /dev/null +++ b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java @@ -0,0 +1,92 @@ +/* + * 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.accessibility; + +import android.content.Context; +import android.os.Bundle; +import android.util.SparseArray; +import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import com.android.launcher3.R; +import com.android.launcher3.Workspace; + +public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate { + + private static final int MOVE_BACKWARD = R.id.action_move_screen_backwards; + private static final int MOVE_FORWARD = R.id.action_move_screen_forwards; + + private final SparseArray mActions = new SparseArray<>(); + private final Workspace mWorkspace; + + public OverviewScreenAccessibilityDelegate(Workspace workspace) { + mWorkspace = workspace; + + Context context = mWorkspace.getContext(); + boolean isRtl = mWorkspace.isLayoutRtl(); + mActions.put(MOVE_BACKWARD, new AccessibilityAction(MOVE_BACKWARD, + context.getText(isRtl ? R.string.action_move_screen_right : + R.string.action_move_screen_left))); + mActions.put(MOVE_FORWARD, new AccessibilityAction(MOVE_FORWARD, + context.getText(isRtl ? R.string.action_move_screen_left : + R.string.action_move_screen_right))); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (host != null) { + if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS ) { + int index = mWorkspace.indexOfChild(host); + mWorkspace.setCurrentPage(index); + } else if (action == MOVE_FORWARD) { + movePage(mWorkspace.indexOfChild(host) + 1, host); + return true; + } else if (action == MOVE_BACKWARD) { + movePage(mWorkspace.indexOfChild(host) - 1, host); + return true; + } + } + + return super.performAccessibilityAction(host, action, args); + } + + private void movePage(int finalIndex, View view) { + mWorkspace.onStartReordering(); + mWorkspace.removeView(view); + mWorkspace.addView(view, finalIndex); + mWorkspace.onEndReordering(); + mWorkspace.announceForAccessibility(mWorkspace.getContext().getText(R.string.screen_moved)); + + mWorkspace.updateAccessibilityFlags(); + view.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + + int index = mWorkspace.indexOfChild(host); + if (index < mWorkspace.getChildCount() - 1) { + info.addAction(mActions.get(MOVE_FORWARD)); + } + if (index > mWorkspace.numCustomPages()) { + info.addAction(mActions.get(MOVE_BACKWARD)); + } + } +} -- cgit v1.2.3 From 8e2133b2c2bde86f913d817942bafdcf6818470b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 6 May 2015 13:39:07 -0700 Subject: Removing some dead code in paged view > Removing fling to delete pages > Removing delete drop target for pages > Removing syncpages support (was used by paged all apps) Change-Id: I0a35ae30da8c48a27aac341bbee9fb5623665902 --- .../android/launcher3/AppsCustomizeCellLayout.java | 71 --- src/com/android/launcher3/FolderPagedView.java | 10 - src/com/android/launcher3/Launcher.java | 3 +- src/com/android/launcher3/PagedView.java | 681 ++------------------- src/com/android/launcher3/Workspace.java | 12 - 5 files changed, 68 insertions(+), 709 deletions(-) delete mode 100644 src/com/android/launcher3/AppsCustomizeCellLayout.java diff --git a/src/com/android/launcher3/AppsCustomizeCellLayout.java b/src/com/android/launcher3/AppsCustomizeCellLayout.java deleted file mode 100644 index a50fb6821..000000000 --- a/src/com/android/launcher3/AppsCustomizeCellLayout.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2010 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.Context; -import android.view.View; - -public class AppsCustomizeCellLayout extends CellLayout implements Page { - - final FocusIndicatorView mFocusHandlerView; - - public AppsCustomizeCellLayout(Context context) { - super(context); - - mFocusHandlerView = new FocusIndicatorView(context); - addView(mFocusHandlerView, 0); - mFocusHandlerView.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE; - mFocusHandlerView.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE; - } - - @Override - public void removeAllViewsOnPage() { - removeAllViews(); - setLayerType(LAYER_TYPE_NONE, null); - } - - @Override - public void removeViewOnPageAt(int index) { - removeViewAt(index); - } - - @Override - public int getPageChildCount() { - return getChildCount(); - } - - @Override - public View getChildOnPageAt(int i) { - return getChildAt(i); - } - - @Override - public int indexOfChildOnPage(View v) { - return indexOfChild(v); - } - - /** - * Clears all the key listeners for the individual icons. - */ - public void resetChildrenOnKeyListeners() { - ShortcutAndWidgetContainer children = getShortcutsAndWidgets(); - int childCount = children.getChildCount(); - for (int j = 0; j < childCount; ++j) { - children.getChildAt(j).setOnKeyListener(null); - } - } -} diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index a07a3dc2c..a1c909a1f 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -78,7 +78,6 @@ public class FolderPagedView extends PagedView { public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); LauncherAppState app = LauncherAppState.getInstance(); - setDataIsReady(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); if (ALLOW_FOLDER_SCROLL) { @@ -346,15 +345,6 @@ public class FolderPagedView extends PagedView { } } - @Override - protected void loadAssociatedPages(int page, boolean immediateAndOnly) { } - - @Override - public void syncPages() { } - - @Override - public void syncPageItems(int page, boolean immediate) { } - public int getDesiredWidth() { return getPageCount() > 0 ? getPageAt(0).getDesiredWidth() : 0; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 339b4e498..ee8be6267 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3219,8 +3219,7 @@ public class Launcher extends Activity // The hotseat touch handling does not go through Workspace, and we always allow long press // on hotseat items. final boolean inHotseat = isHotseatLayout(v); - boolean allowLongPress = inHotseat || mWorkspace.allowLongPress(); - if (allowLongPress && !mDragController.isDragging()) { + if (!mDragController.isDragging()) { if (itemUnderLongClick == null) { // User long pressed on empty space mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index a4593ecb4..d4e8ab5c8 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -22,13 +22,10 @@ import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.os.Parcel; @@ -47,23 +44,12 @@ import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; -import android.view.animation.AnimationUtils; -import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; import com.android.launcher3.util.Thunk; import java.util.ArrayList; -interface Page { - public int getPageChildCount(); - public View getChildOnPageAt(int i); - public void removeAllViewsOnPage(); - public void removeViewOnPageAt(int i); - public int indexOfChildOnPage(View v); -} - /** * An abstraction of the original Workspace which supports browsing through a * sequential list of "pages" @@ -88,6 +74,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // The page is moved more than halfway, automatically move to the next page on touch up. private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f; + private static final float MAX_SCROLL_PROGRESS = 1.0f; + // The following constants need to be scaled based on density. The scaled versions will be // assigned to the corresponding member variables below. private static final int FLING_THRESHOLD_VELOCITY = 500; @@ -97,7 +85,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // We are disabling touch interaction of the widget region for factory ROM. private static final boolean DISABLE_TOUCH_INTERACTION = false; private static final boolean DISABLE_TOUCH_SIDE_PAGES = true; - private static final boolean DISABLE_FLING_TO_DELETE = true; public static final int INVALID_RESTORE_PAGE = -1001; @@ -170,7 +157,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mUnboundedScrollX; protected int[] mTempVisiblePagesRange = new int[2]; protected boolean mForceDrawAllChildrenNextFrame; - private boolean mSpacePagesAutomatically = false; // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise // it is equal to the scaled overscroll position. We use a separate value so as to prevent @@ -183,11 +169,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private PageSwitchListener mPageSwitchListener; - protected ArrayList mDirtyPageContent; - - // If true, syncPages and syncPageItems will be called to refresh pages - protected boolean mContentIsRefreshable = true; - // If true, modify alpha of neighboring pages as user scrolls left/right protected boolean mFadeInAdjacentScreens = false; @@ -198,22 +179,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // If true, the subclass should directly update scrollX itself in its computeScroll method // (SmoothPagedView does this) protected boolean mDeferScrollUpdate = false; - protected boolean mDeferLoadAssociatedPagesUntilScrollCompletes = false; protected boolean mIsPageMoving = false; - // All syncs and layout passes are deferred until data is ready. - protected boolean mIsDataReady = false; - - protected boolean mAllowLongPress = true; - private boolean mWasInOverscroll = false; // Page Indicator @Thunk int mPageIndicatorViewId; @Thunk PageIndicator mPageIndicator; - private boolean mAllowPagedViewAnimations = true; - // The viewport whether the pages are to be contained (the actual view may be larger than the // viewport) private Rect mViewport = new Rect(); @@ -221,10 +194,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Reordering // We use the min scale to determine how much to expand the actually PagedView measured // dimensions such that when we are zoomed out, the view is not clipped - private int REORDERING_DROP_REPOSITION_DURATION = 200; - protected int REORDERING_REORDER_REPOSITION_DURATION = 300; - protected int REORDERING_ZOOM_IN_OUT_DURATION = 250; - private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; + private static int REORDERING_DROP_REPOSITION_DURATION = 200; + private static int REORDERING_REORDER_REPOSITION_DURATION = 300; + private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; + private float mMinScale = 1f; private boolean mUseMinScale = false; protected View mDragView; @@ -242,28 +215,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private Runnable mPostReorderingPreZoomInRunnable; // Convenience/caching - private Matrix mTmpInvMatrix = new Matrix(); - private float[] mTmpPoint = new float[2]; - private int[] mTmpIntPoint = new int[2]; - private Rect mTmpRect = new Rect(); - private Rect mAltTmpRect = new Rect(); - - // Fling to delete - @Thunk int FLING_TO_DELETE_FADE_OUT_DURATION = 350; - private float FLING_TO_DELETE_FRICTION = 0.035f; - // The degrees specifies how much deviation from the up vector to still consider a fling "up" - private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f; - protected int mFlingToDeleteThresholdVelocity = -1400; - // Drag to delete - @Thunk boolean mDeferringForDelete = false; - @Thunk int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250; - private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350; - - // Drop to delete - private View mDeleteDropTarget; - - // Bouncer - private boolean mTopAlignPageWhenShrinkingForBouncer = false; + private static final Matrix sTmpInvMatrix = new Matrix(); + private static final float[] sTmpPoint = new float[2]; + private static final int[] sTmpIntPoint = new int[2]; + private static final Rect sTmpRect = new Rect(); protected final Rect mInsets = new Rect(); @@ -300,8 +255,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * Initializes various states for this workspace. */ protected void init() { - mDirtyPageContent = new ArrayList(); - mDirtyPageContent.ensureCapacity(32); mScroller = new LauncherScroller(getContext()); setDefaultInterpolator(new ScrollInterpolator()); mCurrentPage = 0; @@ -313,10 +266,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mDensity = getResources().getDisplayMetrics().density; - // Scale the fling-to-delete threshold by the density - mFlingToDeleteThresholdVelocity = - (int) (mFlingToDeleteThresholdVelocity * mDensity); - mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity); mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity); @@ -336,7 +285,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc ViewGroup grandParent = (ViewGroup) parent.getParent(); if (mPageIndicator == null && mPageIndicatorViewId > -1) { mPageIndicator = (PageIndicator) grandParent.findViewById(mPageIndicatorViewId); - mPageIndicator.removeAllMarkers(mAllowPagedViewAnimations); + mPageIndicator.removeAllMarkers(true); ArrayList markers = new ArrayList(); @@ -344,7 +293,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc markers.add(getPageIndicatorMarker(i)); } - mPageIndicator.addMarkers(markers, mAllowPagedViewAnimations); + mPageIndicator.addMarkers(markers, true); OnClickListener listener = getPageIndicatorClickListener(); if (listener != null) { @@ -362,33 +311,31 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return null; } + @Override protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); // Unhook the page indicator mPageIndicator = null; } - void setDeleteDropTarget(View v) { - mDeleteDropTarget = v; - } - // Convenience methods to map points from self to parent and vice versa - float[] mapPointFromViewToParent(View v, float x, float y) { - mTmpPoint[0] = x; - mTmpPoint[1] = y; - v.getMatrix().mapPoints(mTmpPoint); - mTmpPoint[0] += v.getLeft(); - mTmpPoint[1] += v.getTop(); - return mTmpPoint; - } - float[] mapPointFromParentToView(View v, float x, float y) { - mTmpPoint[0] = x - v.getLeft(); - mTmpPoint[1] = y - v.getTop(); - v.getMatrix().invert(mTmpInvMatrix); - mTmpInvMatrix.mapPoints(mTmpPoint); - return mTmpPoint; - } - - void updateDragViewTranslationDuringDrag() { + private float[] mapPointFromViewToParent(View v, float x, float y) { + sTmpPoint[0] = x; + sTmpPoint[1] = y; + v.getMatrix().mapPoints(sTmpPoint); + sTmpPoint[0] += v.getLeft(); + sTmpPoint[1] += v.getTop(); + return sTmpPoint; + } + private float[] mapPointFromParentToView(View v, float x, float y) { + sTmpPoint[0] = x - v.getLeft(); + sTmpPoint[1] = y - v.getTop(); + v.getMatrix().invert(sTmpInvMatrix); + sTmpInvMatrix.mapPoints(sTmpPoint); + return sTmpPoint; + } + + private void updateDragViewTranslationDuringDrag() { if (mDragView != null) { float x = (mLastMotionX - mDownMotionX) + (getScrollX() - mDownScrollX) + (mDragViewBaselineLeft - mDragView.getLeft()); @@ -462,18 +409,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } - /** - * Called by subclasses to mark that data is ready, and that we can begin loading and laying - * out pages. - */ - protected void setDataIsReady() { - mIsDataReady = true; - } - - protected boolean isDataReady() { - return mIsDataReady; - } - /** * Returns the index of the currently displayed page. */ @@ -516,17 +451,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc forceFinishScroller(); } - /** - * Called during AllApps/Home transitions to avoid unnecessary work. When that other animation - * {@link #updateCurrentPageScroll()} should be called, to correctly set the final state and - * re-enable scrolling. - */ - void stopScrolling() { - mCurrentPage = getNextPage(); - notifyPageSwitchListener(); - forceFinishScroller(); - } - private void abortScrollerAnimation(boolean resetNextPage) { mScroller.abortAnimation(); // We need to clean up the next page here to avoid computeScrollHelper from @@ -750,12 +674,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mNextPage = INVALID_PAGE; notifyPageSwitchListener(); - // Load the associated pages if necessary - if (mDeferLoadAssociatedPagesUntilScrollCompletes) { - loadAssociatedPages(mCurrentPage); - mDeferLoadAssociatedPagesUntilScrollCompletes = false; - } - // We don't want to trigger a page end moving unless the page has settled // and the user has stopped scrolling if (mTouchState == TOUCH_STATE_REST) { @@ -779,10 +697,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc computeScrollHelper(); } - protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) { - return mTopAlignPageWhenShrinkingForBouncer; - } - public static class LayoutParams extends ViewGroup.LayoutParams { public boolean isFullScreenPage = false; @@ -814,7 +728,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (!mIsDataReady || getChildCount() == 0) { + if (getChildCount() == 0) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } @@ -918,27 +832,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } - if (mSpacePagesAutomatically) { - int spacing = (getViewportWidth() - mInsets.left - mInsets.right - - referenceChildWidth) / 2; - if (spacing >= 0) { - setPageSpacing(spacing); - } - mSpacePagesAutomatically = false; - } setMeasuredDimension(scaledWidthSize, scaledHeightSize); } - /** - * This method should be called once before first layout / measure pass. - */ - protected void setSinglePageInViewport() { - mSpacePagesAutomatically = true; - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (!mIsDataReady || getChildCount() == 0) { + if (getChildCount() == 0) { return; } @@ -1040,8 +939,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc updateMaxScrollX(); } - if (mScroller.isFinished() && mChildCountOnLastLayout != childCount && - !mDeferringForDelete) { + if (mScroller.isFinished() && mChildCountOnLastLayout != childCount) { if (mRestorePage != INVALID_RESTORE_PAGE) { setCurrentPage(mRestorePage); mRestorePage = INVALID_RESTORE_PAGE; @@ -1087,14 +985,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected void enablePagedViewAnimations() { - mAllowPagedViewAnimations = true; - - } - protected void disablePagedViewAnimations() { - mAllowPagedViewAnimations = false; - } - @Override public void onChildViewAdded(View parent, View child) { // Update the page indicator, we don't update the page indicator as we @@ -1103,7 +993,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int pageIndex = indexOfChild(child); mPageIndicator.addMarker(pageIndex, getPageIndicatorMarker(pageIndex), - mAllowPagedViewAnimations); + true); } // This ensures that when children are added, they get the correct transforms / alphas @@ -1124,7 +1014,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Update the page indicator, we don't update the page indicator as we // add/remove pages if (mPageIndicator != null && !isReordering(false)) { - mPageIndicator.removeMarker(index, mAllowPagedViewAnimations); + mPageIndicator.removeMarker(index, true); } } @@ -1154,7 +1044,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Update the page indicator, we don't update the page indicator as we // add/remove pages if (mPageIndicator != null) { - mPageIndicator.removeAllMarkers(mAllowPagedViewAnimations); + mPageIndicator.removeAllMarkers(true); } super.removeAllViewsInLayout(); @@ -1175,7 +1065,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected void getVisiblePages(int[] range) { final int pageCount = getChildCount(); - mTmpIntPoint[0] = mTmpIntPoint[1] = 0; + sTmpIntPoint[0] = sTmpIntPoint[1] = 0; range[0] = -1; range[1] = -1; @@ -1188,9 +1078,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc for (int i = 0; i < count; i++) { View currPage = getPageAt(i); - mTmpIntPoint[0] = 0; - Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false); - if (mTmpIntPoint[0] > viewportWidth) { + sTmpIntPoint[0] = 0; + Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false); + if (sTmpIntPoint[0] > viewportWidth) { if (range[0] == -1) { continue; } else { @@ -1198,9 +1088,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - mTmpIntPoint[0] = currPage.getMeasuredWidth(); - Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false); - if (mTmpIntPoint[0] < 0) { + sTmpIntPoint[0] = currPage.getMeasuredWidth(); + Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false); + if (sTmpIntPoint[0] < 0) { if (range[0] == -1) { continue; } else { @@ -1397,9 +1287,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** Returns whether x and y originated within the buffered viewport */ private boolean isTouchPointInViewportWithBuffer(int x, int y) { - mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top, + sTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top, mViewport.right + mViewport.width() / 2, mViewport.bottom); - return mTmpRect.contains(x, y); + return sTmpRect.contains(x, y); } @Override @@ -1559,32 +1449,16 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected float getMaxScrollProgress() { - return 1.0f; - } - protected void cancelCurrentPageLongPress() { - if (mAllowLongPress) { - //mAllowLongPress = false; - // Try canceling the long press. It could also have been scheduled - // by a distant descendant, so use the mAllowLongPress flag to block - // everything - final View currentPage = getPageAt(mCurrentPage); - if (currentPage != null) { - currentPage.cancelLongPress(); - } + // Try canceling the long press. It could also have been scheduled + // by a distant descendant, so use the mAllowLongPress flag to block + // everything + final View currentPage = getPageAt(mCurrentPage); + if (currentPage != null) { + currentPage.cancelLongPress(); } } - protected float getBoundedScrollProgress(int screenCenter, View v, int page) { - final int halfScreenSize = getViewportWidth() / 2; - - screenCenter = Math.min(getScrollX() + halfScreenSize, screenCenter); - screenCenter = Math.max(halfScreenSize, screenCenter); - - return getScrollProgress(screenCenter, v, page); - } - protected float getScrollProgress(int screenCenter, View v, int page) { final int halfScreenSize = getViewportWidth() / 2; @@ -1605,8 +1479,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } float scrollProgress = delta / (totalDistance * 1.0f); - scrollProgress = Math.min(scrollProgress, getMaxScrollProgress()); - scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress()); + scrollProgress = Math.min(scrollProgress, MAX_SCROLL_PROGRESS); + scrollProgress = Math.max(scrollProgress, - MAX_SCROLL_PROGRESS); return scrollProgress; } @@ -1735,7 +1609,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mAllowOverScroll = enable; } - int getNearestHoverOverPageIndex() { + private int getNearestHoverOverPageIndex() { if (mDragView != null) { int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2) + mDragView.getTranslationX()); @@ -1842,19 +1716,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Find the closest page to the touch point final int dragViewIndex = indexOfChild(mDragView); - // Change the drag view if we are hovering over the drop target - boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget( - (int) mParentDownMotionX, (int) mParentDownMotionY); - setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete); - if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX); if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY); if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX); if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY); final int pageUnderPointIndex = getNearestHoverOverPageIndex(); - if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView) && - !isHoveringOverDelete) { + if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView)) { mTempVisiblePagesRange[0] = 0; mTempVisiblePagesRange[1] = getPageCount() - 1; getFreeScrollPageRange(mTempVisiblePagesRange); @@ -1901,9 +1769,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } removeView(mDragView); - onRemoveView(mDragView, false); addView(mDragView, pageUnderPointIndex); - onAddView(mDragView, pageUnderPointIndex); mSidePageHoverIndex = -1; if (mPageIndicator != null) { mPageIndicator.setActiveMarker(getNextPage()); @@ -2014,19 +1880,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mParentDownMotionX = pt[0]; mParentDownMotionY = pt[1]; updateDragViewTranslationDuringDrag(); - boolean handledFling = false; - if (!DISABLE_FLING_TO_DELETE) { - // Check the velocity and see if we are flinging-to-delete - PointF flingToDeleteVector = isFlingingToDelete(); - if (flingToDeleteVector != null) { - onFlingToDelete(flingToDeleteVector); - handledFling = true; - } - } - if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX, - (int) mParentDownMotionY)) { - onDropToDelete(); - } } else { if (!mCancelTap) { onUnhandledTap(ev); @@ -2055,11 +1908,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return true; } - public void onFlingToDelete(View v) {} - public void onRemoveView(View v, boolean deletePermanently) {} - public void onRemoveViewAnimationCompleted() {} - public void onAddView(View v, int index) {} - private void resetTouchState() { releaseVelocityTracker(); endReordering(); @@ -2155,22 +2003,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - protected int getChildWidth(int index) { - return getPageAt(index).getMeasuredWidth(); - } - - int getPageNearestToPoint(float x) { - int index = 0; - for (int i = 0; i < getChildCount(); ++i) { - if (x < getChildAt(i).getRight() - getScrollX()) { - return index; - } else { - index++; - } - } - return Math.min(index, getChildCount() - 1); - } - int getPageNearestToCenterOfScreen() { int minDistanceFromScreenCenter = Integer.MAX_VALUE; int minDistanceFromScreenCenterIndex = -1; @@ -2330,9 +2162,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc computeScroll(); } - // Defer loading associated pages until the scroll settles - mDeferLoadAssociatedPagesUntilScrollCompletes = true; - mForceScreenScrolled = true; invalidate(); } @@ -2359,27 +2188,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return result; } - /** - * @return True is long presses are still allowed for the current touch - */ - public boolean allowLongPress() { - return mAllowLongPress; - } - @Override public boolean performLongClick() { mCancelTap = true; return super.performLongClick(); } - /** - * Set true to allow long-press events to be triggered, usually checked by - * {@link Launcher} to accept or block dpad-initiated long-presses. - */ - public void setAllowLongPress(boolean allowLongPress) { - mAllowLongPress = allowLongPress; - } - public static class SavedState extends BaseSavedState { int currentPage = -1; @@ -2410,111 +2224,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc }; } - protected void loadAssociatedPages(int page) { - loadAssociatedPages(page, false); - } - protected void loadAssociatedPages(int page, boolean immediateAndOnly) { - if (mContentIsRefreshable) { - final int count = getChildCount(); - if (page < count) { - int lowerPageBound = getAssociatedLowerPageBound(page); - int upperPageBound = getAssociatedUpperPageBound(page); - if (DEBUG) Log.d(TAG, "loadAssociatedPages: " + lowerPageBound + "/" - + upperPageBound); - // First, clear any pages that should no longer be loaded - for (int i = 0; i < count; ++i) { - Page layout = (Page) getPageAt(i); - if ((i < lowerPageBound) || (i > upperPageBound)) { - if (layout.getPageChildCount() > 0) { - layout.removeAllViewsOnPage(); - } - mDirtyPageContent.set(i, true); - } - } - // Next, load any new pages - for (int i = 0; i < count; ++i) { - if ((i != page) && immediateAndOnly) { - continue; - } - if (lowerPageBound <= i && i <= upperPageBound) { - if (mDirtyPageContent.get(i)) { - syncPageItems(i, (i == page) && immediateAndOnly); - mDirtyPageContent.set(i, false); - } - } - } - } - } - } - - protected int getAssociatedLowerPageBound(int page) { - return Math.max(0, page - 1); - } - protected int getAssociatedUpperPageBound(int page) { - final int count = getChildCount(); - return Math.min(page + 1, count - 1); - } - - /** - * This method is called ONLY to synchronize the number of pages that the paged view has. - * To actually fill the pages with information, implement syncPageItems() below. It is - * guaranteed that syncPageItems() will be called for a particular page before it is shown, - * and therefore, individual page items do not need to be updated in this method. - */ - public abstract void syncPages(); - - /** - * This method is called to synchronize the items that are on a particular page. If views on - * the page can be reused, then they should be updated within this method. - */ - public abstract void syncPageItems(int page, boolean immediate); - - protected void invalidatePageData() { - invalidatePageData(-1, false); - } - protected void invalidatePageData(int currentPage) { - invalidatePageData(currentPage, false); - } - protected void invalidatePageData(int currentPage, boolean immediateAndOnly) { - if (!mIsDataReady) { - return; - } - - if (mContentIsRefreshable) { - // Force all scrolling-related behavior to end - forceFinishScroller(); - - // Update all the pages - syncPages(); - - // We must force a measure after we've loaded the pages to update the content width and - // to determine the full scroll width - measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - - // Set a new page as the current page if necessary - if (currentPage > -1) { - setCurrentPage(Math.min(getPageCount() - 1, currentPage)); - } - - // Mark each of the pages as dirty - final int count = getChildCount(); - mDirtyPageContent.clear(); - for (int i = 0; i < count; ++i) { - mDirtyPageContent.add(true); - } - - // Load any pages that are necessary for the current window of views - loadAssociatedPages(mCurrentPage, immediateAndOnly); - requestLayout(); - } - if (isPageMoving()) { - // If the page is moving, then snap it to the final position to ensure we don't get - // stuck between pages - snapToDestination(); - } - } - // Animate the drag view back to the original position void animateDragViewToOriginalPosition() { if (mDragView != null) { @@ -2605,279 +2314,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc onEndReordering(); } }; - if (!mDeferringForDelete) { - mPostReorderingPreZoomInRunnable = new Runnable() { - public void run() { - onCompleteRunnable.run(); - enableFreeScroll(); - }; - }; - - mPostReorderingPreZoomInRemainingAnimationCount = - NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT; - // Snap to the current page - snapToPage(indexOfChild(mDragView), 0); - // Animate the drag view back to the front position - animateDragViewToOriginalPosition(); - } else { - // Handled in post-delete-animation-callbacks - } - } - - /* - * Flinging to delete - IN PROGRESS - */ - private PointF isFlingingToDelete() { - ViewConfiguration config = ViewConfiguration.get(getContext()); - mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity()); - - if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) { - // Do a quick dot product test to ensure that we are flinging upwards - PointF vel = new PointF(mVelocityTracker.getXVelocity(), - mVelocityTracker.getYVelocity()); - PointF upVec = new PointF(0f, -1f); - float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) / - (vel.length() * upVec.length())); - if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) { - return vel; - } - } - return null; - } - - /** - * Creates an animation from the current drag view along its current velocity vector. - * For this animation, the alpha runs for a fixed duration and we update the position - * progressively. - */ - private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener { - private View mDragView; - private PointF mVelocity; - private Rect mFrom; - private long mPrevTime; - private float mFriction; - - private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); - - public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from, - long startTime, float friction) { - mDragView = dragView; - mVelocity = vel; - mFrom = from; - mPrevTime = startTime; - mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = ((Float) animation.getAnimatedValue()).floatValue(); - long curTime = AnimationUtils.currentAnimationTimeMillis(); - - mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f); - mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f); - mDragView.setTranslationX(mFrom.left); - mDragView.setTranslationY(mFrom.top); - mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); - - mVelocity.x *= mFriction; - mVelocity.y *= mFriction; - mPrevTime = curTime; - } - }; - - private static final int ANIM_TAG_KEY = 100; - - private Runnable createPostDeleteAnimationRunnable(final View dragView) { - return new Runnable() { - @Override + mPostReorderingPreZoomInRunnable = new Runnable() { public void run() { - int dragViewIndex = indexOfChild(dragView); - - // For each of the pages around the drag view, animate them from the previous - // position to the new position in the layout (as a result of the drag view moving - // in the layout) - // NOTE: We can make an assumption here because we have side-bound pages that we - // will always have pages to animate in from the left - getFreeScrollPageRange(mTempVisiblePagesRange); - boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]); - boolean slideFromLeft = (isLastWidgetPage || - dragViewIndex > mTempVisiblePagesRange[0]); - - // Setup the scroll to the correct page before we swap the views - if (slideFromLeft) { - snapToPageImmediately(dragViewIndex - 1); - } - - int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]); - int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1); - int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 ); - int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex); - ArrayList animations = new ArrayList(); - for (int i = lowerIndex; i <= upperIndex; ++i) { - View v = getChildAt(i); - // dragViewIndex < pageUnderPointIndex, so after we remove the - // drag view all subsequent views to pageUnderPointIndex will - // shift down. - int oldX = 0; - int newX = 0; - if (slideFromLeft) { - if (i == 0) { - // Simulate the page being offscreen with the page spacing - oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i) - - mPageSpacing; - } else { - oldX = getViewportOffsetX() + getChildOffset(i - 1); - } - newX = getViewportOffsetX() + getChildOffset(i); - } else { - oldX = getChildOffset(i) - getChildOffset(i - 1); - newX = 0; - } - - // Animate the view translation from its old position to its new - // position - AnimatorSet anim = (AnimatorSet) v.getTag(); - if (anim != null) { - anim.cancel(); - } - - // Note: Hacky, but we want to skip any optimizations to not draw completely - // hidden views - v.setAlpha(Math.max(v.getAlpha(), 0.01f)); - v.setTranslationX(oldX - newX); - anim = new AnimatorSet(); - anim.playTogether( - ObjectAnimator.ofFloat(v, "translationX", 0f), - ObjectAnimator.ofFloat(v, "alpha", 1f)); - animations.add(anim); - v.setTag(ANIM_TAG_KEY, anim); - } - - AnimatorSet slideAnimations = new AnimatorSet(); - slideAnimations.playTogether(animations); - slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION); - slideAnimations.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mDeferringForDelete = false; - onEndReordering(); - onRemoveViewAnimationCompleted(); - } - }); - slideAnimations.start(); - - removeView(dragView); - onRemoveView(dragView, true); - } - }; - } - - public void onFlingToDelete(PointF vel) { - final long startTime = AnimationUtils.currentAnimationTimeMillis(); - - // NOTE: Because it takes time for the first frame of animation to actually be - // called and we expect the animation to be a continuation of the fling, we have - // to account for the time that has elapsed since the fling finished. And since - // we don't have a startDelay, we will always get call to update when we call - // start() (which we want to ignore). - final TimeInterpolator tInterpolator = new TimeInterpolator() { - private int mCount = -1; - private long mStartTime; - private float mOffset; - /* Anonymous inner class ctor */ { - mStartTime = startTime; - } - - @Override - public float getInterpolation(float t) { - if (mCount < 0) { - mCount++; - } else if (mCount == 0) { - mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - - mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION); - mCount++; - } - return Math.min(1f, mOffset + t); - } + onCompleteRunnable.run(); + enableFreeScroll(); + }; }; - final Rect from = new Rect(); - final View dragView = mDragView; - from.left = (int) dragView.getTranslationX(); - from.top = (int) dragView.getTranslationY(); - AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel, - from, startTime, FLING_TO_DELETE_FRICTION); - - final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); - - // Create and start the animation - ValueAnimator mDropAnim = new ValueAnimator(); - mDropAnim.setInterpolator(tInterpolator); - mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION); - mDropAnim.setFloatValues(0f, 1f); - mDropAnim.addUpdateListener(updateCb); - mDropAnim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - onAnimationEndRunnable.run(); - } - }); - mDropAnim.start(); - mDeferringForDelete = true; - } - - /* Drag to delete */ - private boolean isHoveringOverDeleteDropTarget(int x, int y) { - if (mDeleteDropTarget != null) { - mAltTmpRect.set(0, 0, 0, 0); - View parent = (View) mDeleteDropTarget.getParent(); - if (parent != null) { - parent.getGlobalVisibleRect(mAltTmpRect); - } - mDeleteDropTarget.getGlobalVisibleRect(mTmpRect); - mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top); - return mTmpRect.contains(x, y); - } - return false; + mPostReorderingPreZoomInRemainingAnimationCount = + NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT; + // Snap to the current page + snapToPage(indexOfChild(mDragView), 0); + // Animate the drag view back to the front position + animateDragViewToOriginalPosition(); } - protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {} - - private void onDropToDelete() { - final View dragView = mDragView; - - final float toScale = 0f; - final float toAlpha = 0f; - - // Create and start the complex animation - ArrayList animations = new ArrayList(); - AnimatorSet motionAnim = new AnimatorSet(); - motionAnim.setInterpolator(new DecelerateInterpolator(2)); - motionAnim.playTogether( - ObjectAnimator.ofFloat(dragView, "scaleX", toScale), - ObjectAnimator.ofFloat(dragView, "scaleY", toScale)); - animations.add(motionAnim); - - AnimatorSet alphaAnim = new AnimatorSet(); - alphaAnim.setInterpolator(new LinearInterpolator()); - alphaAnim.playTogether( - ObjectAnimator.ofFloat(dragView, "alpha", toAlpha)); - animations.add(alphaAnim); - - final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); - - AnimatorSet anim = new AnimatorSet(); - anim.playTogether(animations); - anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - onAnimationEndRunnable.run(); - } - }); - anim.start(); - - mDeferringForDelete = true; - } + private static final int ANIM_TAG_KEY = 100; /* Accessibility */ @Override diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index e7a41e09b..3cb2aa86d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -94,7 +94,6 @@ public class Workspace extends SmoothPagedView protected static final int FADE_EMPTY_SCREEN_DURATION = 150; private static final int ADJACENT_SCREEN_DROP_DURATION = 300; - private static final int FLING_THRESHOLD_VELOCITY = 500; static final boolean MAP_NO_RECURSE = false; static final boolean MAP_RECURSE = true; @@ -303,13 +302,11 @@ public class Workspace extends SmoothPagedView */ public Workspace(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mContentIsRefreshable = false; mOutlineHelper = HolographicOutlineHelper.obtain(context); mDragEnforcer = new DropTarget.DragEnforcer(context); // With workspace, data is available straight from the get-go - setDataIsReady(); mLauncher = (Launcher) context; mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); @@ -445,7 +442,6 @@ public class Workspace extends SmoothPagedView display.getSize(mDisplaySize); mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); - mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); // Set the wallpaper dimensions when Launcher starts up setWallpaperDimension(); @@ -4446,14 +4442,6 @@ public class Workspace extends SmoothPagedView return super.getPageIndicatorMarker(pageIndex); } - @Override - public void syncPages() { - } - - @Override - public void syncPageItems(int page, boolean immediate) { - } - protected String getPageIndicatorDescription() { String settings = getResources().getString(R.string.settings_button_text); return getCurrentPageDescription() + ", " + settings; -- cgit v1.2.3 From 9ca9c1316da8382c1f663973072731033b5e533a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Apr 2015 14:57:22 -0700 Subject: Adding accessibility widget resize Change-Id: I954f01733474450cbeedba2406e1d6d373bb87a3 --- res/values/config.xml | 1 + res/values/strings.xml | 19 +++++ src/com/android/launcher3/CellLayout.java | 17 ++++ .../launcher3/LauncherAccessibilityDelegate.java | 99 +++++++++++++++++++++- 4 files changed, 135 insertions(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index 6ef863532..84ccef10c 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -102,4 +102,5 @@ + diff --git a/res/values/strings.xml b/res/values/strings.xml index 1681fc626..59625841b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -252,4 +252,23 @@ Screen moved + + + Resize + + + Increase width + + + Increase height + + + Decrease width + + + Decrease height + + + Widget resized to width %1$s height %2$s + diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 65c67025f..72eabf177 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -3051,4 +3051,21 @@ public class CellLayout extends ViewGroup { public boolean findVacantCell(int spanX, int spanY, int[] outXY) { return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied); } + + public boolean isRegionVacant(int x, int y, int spanX, int spanY) { + int x2 = x + spanX - 1; + int y2 = y + spanY - 1; + if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) { + return false; + } + for (int i = x; i <= x2; i++) { + for (int j = y; j <= y2; j++) { + if (mOccupied[i][j]) { + return false; + } + } + } + + return true; + } } diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java index 8a9a0508c..3992e6390 100644 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java @@ -1,6 +1,9 @@ package com.android.launcher3; import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.appwidget.AppWidgetProviderInfo; +import android.content.DialogInterface; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; @@ -28,6 +31,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; private static final int MOVE = R.id.action_move; private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; + private static final int RESIZE = R.id.action_resize; public enum DragType { ICON, @@ -62,6 +66,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { launcher.getText(R.string.action_move))); mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE, launcher.getText(R.string.action_move_to_workspace))); + mActions.put(RESIZE, new AccessibilityAction(RESIZE, + launcher.getText(R.string.action_resize))); } @Override @@ -87,6 +93,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { if (item.container >= 0) { info.addAction(mActions.get(MOVE_TO_WORKSPACE)); + } else if (item instanceof LauncherAppWidgetInfo) { + if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) { + info.addAction(mActions.get(RESIZE)); + } } } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { info.addAction(mActions.get(ADD_TO_WORKSPACE)); @@ -102,7 +112,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { return super.performAccessibilityAction(host, action, args); } - public boolean performAction(View host, final ItemInfo item, int action) { + public boolean performAction(final View host, final ItemInfo item, int action) { if (action == REMOVE) { if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) { announceConfirmation(R.string.item_removed); @@ -167,10 +177,97 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { announceConfirmation(R.string.item_moved); } }); + } else if (action == RESIZE) { + final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item; + final ArrayList actions = getSupportedResizeActions(host, info); + CharSequence[] labels = new CharSequence[actions.size()]; + for (int i = 0; i < actions.size(); i++) { + labels[i] = mLauncher.getText(actions.get(i)); + } + + new AlertDialog.Builder(mLauncher) + .setTitle(R.string.action_resize) + .setItems(labels, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + performResizeAction(actions.get(which), host, info); + dialog.dismiss(); + } + }) + .show(); } return false; } + private ArrayList getSupportedResizeActions(View host, LauncherAppWidgetInfo info) { + AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo(); + ArrayList actions = new ArrayList<>(); + + CellLayout layout = (CellLayout) host.getParent().getParent(); + if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) { + if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) || + layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) { + actions.add(R.string.action_increase_width); + } + + if (info.spanX > info.minSpanX && info.spanX > 1) { + actions.add(R.string.action_decrease_width); + } + } + + if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { + if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) || + layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) { + actions.add(R.string.action_increase_height); + } + + if (info.spanY > info.minSpanY && info.spanY > 1) { + actions.add(R.string.action_decrease_height); + } + } + return actions; + } + + private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams(); + CellLayout layout = (CellLayout) host.getParent().getParent(); + layout.markCellsAsUnoccupiedForView(host); + + if (action == R.string.action_increase_width) { + if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) + && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) + || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) { + lp.cellX --; + info.cellX --; + } + lp.cellHSpan ++; + info.spanX ++; + } else if (action == R.string.action_decrease_width) { + lp.cellHSpan --; + info.spanX --; + } else if (action == R.string.action_increase_height) { + if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) { + lp.cellY --; + info.cellY --; + } + lp.cellVSpan ++; + info.spanY ++; + } else if (action == R.string.action_decrease_height) { + lp.cellVSpan --; + info.spanY --; + } + + layout.markCellsAsOccupiedForView(host); + Rect sizeRange = new Rect(); + AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange); + ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null, + sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom); + host.requestLayout(); + LauncherModel.updateItemInDatabase(mLauncher, info); + announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY)); + } + @Thunk void announceConfirmation(int resId) { announceConfirmation(mLauncher.getResources().getString(resId)); } -- cgit v1.2.3 From 77919b93b98f5afa1b39546861197d6065847224 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 6 May 2015 16:53:21 -0700 Subject: Updating the icon cache after all apps has been bound. > Updating the cache DB version to reset existing cache. Bug: 20834835 Change-Id: I298ca9ddcc4dd270b25b767447ecde01ef41a916 --- src/com/android/launcher3/AllAppsList.java | 11 +++ src/com/android/launcher3/IconCache.java | 16 ++++- src/com/android/launcher3/LauncherModel.java | 102 +++++++++++++++++---------- 3 files changed, 89 insertions(+), 40 deletions(-) diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index dd646bb22..3b25dca34 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -24,6 +24,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; @@ -117,6 +118,16 @@ class AllAppsList { } } + public void updateIconsAndLabels(HashSet packages, UserHandleCompat user, + ArrayList outUpdates) { + for (AppInfo info : data) { + if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) { + mIconCache.updateTitleAndIcon(info); + outUpdates.add(info); + } + } + } + /** * Add and remove icons for this package which has been updated. */ diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index fd4571482..6c2aa397d 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -406,6 +406,20 @@ public class IconCache { application.usingLowResIcon = entry.isLowResIcon; } + /** + * Updates {@param application} only if a valid entry is found. + */ + public synchronized void updateTitleAndIcon(AppInfo application) { + CacheEntry entry = cacheLocked(application.componentName, null, application.user, + false, application.usingLowResIcon); + if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) { + application.title = entry.title; + application.iconBitmap = entry.icon; + application.contentDescription = entry.contentDescription; + application.usingLowResIcon = entry.isLowResIcon; + } + } + /** * Returns a high res icon for the given intent and user */ @@ -655,7 +669,7 @@ public class IconCache { } private static final class IconDB extends SQLiteOpenHelper { - private final static int DB_VERSION = 3; + private final static int DB_VERSION = 4; private final static String TABLE_NAME = "icons"; private final static String COLUMN_ROWID = "rowid"; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 0138a913a..ddbae3a18 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2767,6 +2767,7 @@ public class LauncherModel extends BroadcastReceiver } if (!mAllAppsLoaded) { loadAllApps(); + updateAllAppsIconsCache(); synchronized (LoaderTask.this) { if (mStopped) { return; @@ -2821,9 +2822,6 @@ public class LauncherModel extends BroadcastReceiver return; } - final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - final List profiles = mUserManager.getUserProfiles(); // Clear the list of apps @@ -2843,42 +2841,6 @@ public class LauncherModel extends BroadcastReceiver return; } - // Update icon cache - HashSet updatedPackages = mIconCache.updateDBIcons(user, apps); - - // If any package icon has changed (app was updated while launcher was dead), - // update the corresponding shortcuts. - if (!updatedPackages.isEmpty()) { - final ArrayList updates = new ArrayList(); - synchronized (sBgLock) { - for (ItemInfo info : sBgItemsIdMap) { - if (info instanceof ShortcutInfo && user.equals(info.user) - && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - ShortcutInfo si = (ShortcutInfo) info; - ComponentName cn = si.getTargetComponent(); - if (cn != null && updatedPackages.contains(cn.getPackageName())) { - si.updateIcon(mIconCache); - updates.add(si); - } - } - } - } - - if (!updates.isEmpty()) { - final UserHandleCompat userFinal = user; - mHandler.post(new Runnable() { - - public void run() { - Callbacks cb = getCallback(); - if (cb != null) { - cb.bindShortcutsChanged( - updates, new ArrayList(), userFinal); - } - } - }); - } - } - // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfoCompat app = apps.get(i); @@ -2929,6 +2891,68 @@ public class LauncherModel extends BroadcastReceiver } } + private void updateAllAppsIconsCache() { + final ArrayList updatedApps = new ArrayList<>(); + + for (UserHandleCompat user : mUserManager.getUserProfiles()) { + // Query for the set of apps + final List apps = mLauncherApps.getActivityList(null, user); + // Fail if we don't have any apps + // TODO: Fix this. Only fail for the current user. + if (apps == null || apps.isEmpty()) { + return; + } + + // Update icon cache + HashSet updatedPackages = mIconCache.updateDBIcons(user, apps); + + // If any package icon has changed (app was updated while launcher was dead), + // update the corresponding shortcuts. + if (!updatedPackages.isEmpty()) { + final ArrayList updatedShortcuts = new ArrayList<>(); + synchronized (sBgLock) { + for (ItemInfo info : sBgItemsIdMap) { + if (info instanceof ShortcutInfo && user.equals(info.user) + && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (cn != null && updatedPackages.contains(cn.getPackageName())) { + si.updateIcon(mIconCache); + updatedShortcuts.add(si); + } + } + } + mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps); + } + + if (!updatedShortcuts.isEmpty()) { + final UserHandleCompat userFinal = user; + mHandler.post(new Runnable() { + + public void run() { + Callbacks cb = getCallback(); + if (cb != null) { + cb.bindShortcutsChanged(updatedShortcuts, + new ArrayList(), userFinal); + } + } + }); + } + } + } + if (!updatedApps.isEmpty()) { + mHandler.post(new Runnable() { + + public void run() { + Callbacks cb = getCallback(); + if (cb != null) { + cb.bindAppsUpdated(updatedApps); + } + } + }); + } + } + public void dumpState() { synchronized (sBgLock) { Log.d(TAG, "mLoaderTask.mContext=" + mContext); -- cgit v1.2.3 From c905efc89cd8a391ae73f36d356c907b4aaf1bf0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 6 May 2015 09:54:53 -0700 Subject: Skipping package update tasks if loader has not yet run. Bug: 20754508 Change-Id: Id2ddcff42cd41e4f9875ad6ef95f648365502b75 --- src/com/android/launcher3/LauncherModel.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 0138a913a..048e9d91c 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3004,6 +3004,10 @@ public class LauncherModel extends BroadcastReceiver } public void run() { + if (!mHasLoaderCompletedOnce) { + // Loader has not yet run. + return; + } final Context context = mApp.getContext(); final String[] packages = mPackages; -- cgit v1.2.3 From ebe1734a67dff9ab46f3c6cce328a86b714ce620 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 6 May 2015 17:49:33 -0700 Subject: Make drag and drop also work when the widget tray is still in scroll mode. b/20698514 Change-Id: Ic075f0016d5e00bbdec193bbcd2e311da1957388 --- res/layout/widgets_view.xml | 2 +- .../launcher3/AppsContainerRecyclerView.java | 17 +++- .../widget/WidgetsContainerRecyclerView.java | 80 +++++++++++++++++++ .../android/launcher3/widget/WidgetsRowView.java | 90 ---------------------- 4 files changed, 97 insertions(+), 92 deletions(-) create mode 100644 src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java delete mode 100644 src/com/android/launcher3/widget/WidgetsRowView.java diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 0800f59aa..5cdf56048 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -40,7 +40,7 @@ android:clipChildren="false" android:orientation="vertical"> - + * Overwritten to NOT intercept a touch sequence that started when the {@link RecycleView} + * scrolling slowing down below the internally defined threshold. + */ +public class WidgetsContainerRecyclerView extends RecyclerView + implements RecyclerView.OnItemTouchListener { + + private static final int SCROLL_DELTA_THRESHOLD = 6; + + /** Keeps the last known scrolling delta/velocity along y-axis. */ + private int mDy = 0; + private float mDeltaThreshold; + + public WidgetsContainerRecyclerView(Context context) { + this(context, null); + } + + public WidgetsContainerRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + addOnItemTouchListener(this); + } + + @Override + public void onScrolled(int dx, int dy) { + mDy = dy; + } + + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if ((Math.abs(mDy) < mDeltaThreshold && + getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { + // now the touch events are being passed to the {@link WidgetCell} until the + // touch sequence goes over the touch slop. + stopScroll(); + } + } + return false; + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent ev) { + // Do nothing. + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsRowView.java b/src/com/android/launcher3/widget/WidgetsRowView.java deleted file mode 100644 index 54667384b..000000000 --- a/src/com/android/launcher3/widget/WidgetsRowView.java +++ /dev/null @@ -1,90 +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.widget; - -import android.content.Context; -import android.view.MotionEvent; -import android.widget.FrameLayout; -import android.widget.HorizontalScrollView; -import android.widget.TextView; - -import com.android.launcher3.R; - -/** - * Layout used for widget tray rows for each app. For performance, this view can be replaced with - * a {@link RecyclerView} in the future if we settle on scrollable single row for the widgets. - * If we decide on collapsable grid, then HorizontalScrollView can be replaced with a - * {@link GridLayout}. - */ -public class WidgetsRowView extends HorizontalScrollView { - static final String TAG = "WidgetsRow"; - - private Runnable mOnLayoutListener; - private String mAppName; - - public WidgetsRowView(Context context, String appName) { - super(context, null, 0); - mAppName = appName; - } - - /** - * Clears all the key listeners for the individual widgets. - */ - public void resetChildrenOnKeyListeners() { - int childCount = getChildCount(); - for (int j = 0; j < childCount; ++j) { - getChildAt(j).setOnKeyListener(null); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - TextView tv = (TextView) findViewById(R.id.widget_name); - tv.setText(mAppName); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mOnLayoutListener = null; - } - - public void setOnLayoutListener(Runnable r) { - mOnLayoutListener = r; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (mOnLayoutListener != null) { - mOnLayoutListener.run(); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean result = super.onTouchEvent(event); - return result; - } - - public static class LayoutParams extends FrameLayout.LayoutParams { - public LayoutParams(int width, int height) { - super(width, height); - } - } -} -- cgit v1.2.3 From 04ac4faab0336e30f0879311432a2377e2631df1 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 6 May 2015 18:58:31 -0700 Subject: Fix build in ub-launcher3-master -> Stop depending on new RecyclerView methods Change-Id: Ib2c58b24e2b796e6e7b3cd5e4bb927dc3f11faf2 --- .../launcher3/AppsContainerRecyclerView.java | 24 +++++++++++++++------- .../widget/WidgetsContainerRecyclerView.java | 24 +++++++++++++++------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index da81f58a4..bf478eddd 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -30,6 +30,8 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import com.android.launcher3.util.Thunk; + import java.util.List; /** @@ -40,10 +42,10 @@ public class AppsContainerRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - private static final int SCROLL_DELTA_THRESHOLD = 6; + private static final int SCROLL_DELTA_THRESHOLD = 4; /** Keeps the last known scrolling delta/velocity along y-axis. */ - private int mDy = 0; + @Thunk int mDy = 0; private float mDeltaThreshold; private AlphabeticalAppsList mApps; @@ -98,6 +100,19 @@ public class AppsContainerRecyclerView extends RecyclerView res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(getFastScrollerAlpha()); mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; + + ScrollListener listener = new ScrollListener(); + addOnScrollListener(listener); + } + + private class ScrollListener extends RecyclerView.OnScrollListener { + public ScrollListener() { + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mDy = dy; + } } /** @@ -149,11 +164,6 @@ public class AppsContainerRecyclerView extends RecyclerView drawFastScrollerPopup(canvas); } - @Override - public void onScrolled(int dx, int dy) { - mDy = dy; - } - /** * We intercept the touch handling only to support fast scrolling when initiated from the * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java index 56791c041..f70f170ed 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java @@ -21,6 +21,8 @@ import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; +import com.android.launcher3.util.Thunk; + /** * The widgets recycler view container. *

@@ -30,10 +32,10 @@ import android.view.MotionEvent; public class WidgetsContainerRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { - private static final int SCROLL_DELTA_THRESHOLD = 6; + private static final int SCROLL_DELTA_THRESHOLD = 4; /** Keeps the last known scrolling delta/velocity along y-axis. */ - private int mDy = 0; + @Thunk int mDy = 0; private float mDeltaThreshold; public WidgetsContainerRecyclerView(Context context) { @@ -47,6 +49,19 @@ public class WidgetsContainerRecyclerView extends RecyclerView public WidgetsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; + + ScrollListener listener = new ScrollListener(); + addOnScrollListener(listener); + } + + private class ScrollListener extends RecyclerView.OnScrollListener { + public ScrollListener() { + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mDy = dy; + } } @Override @@ -55,11 +70,6 @@ public class WidgetsContainerRecyclerView extends RecyclerView addOnItemTouchListener(this); } - @Override - public void onScrolled(int dx, int dy) { - mDy = dy; - } - @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { -- cgit v1.2.3 From 83f59abc9c566da5deb98afe7ea35cfb061f2920 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 5 May 2015 17:21:58 -0700 Subject: Explorations in dense grids for all apps. - Adds sticky section headers - Removing AppsListAdapter - Adding search bar field - Subtitle filtering Bug: 20222023 Change-Id: I1eaef701b5d68f475615f09d86561eacc91c937f --- res/drawable-hdpi/ic_arrow_back_grey.png | Bin 0 -> 190 bytes res/drawable-hdpi/ic_search_grey.png | Bin 0 -> 743 bytes res/drawable-mdpi/ic_arrow_back_grey.png | Bin 0 -> 151 bytes res/drawable-mdpi/ic_search_grey.png | Bin 0 -> 497 bytes res/drawable-xhdpi/ic_arrow_back_grey.png | Bin 0 -> 234 bytes res/drawable-xhdpi/ic_search_grey.png | Bin 0 -> 972 bytes res/drawable-xxhdpi/ic_arrow_back_grey.png | Bin 0 -> 308 bytes res/drawable-xxhdpi/ic_search_grey.png | Bin 0 -> 1473 bytes res/drawable-xxxhdpi/ic_arrow_back_grey.png | Bin 0 -> 359 bytes res/drawable-xxxhdpi/ic_search_grey.png | Bin 0 -> 1996 bytes res/layout/apps_list_view.xml | 63 ++++-- res/values/dimens.xml | 1 + .../android/launcher3/AlphabeticalAppsList.java | 91 ++++++++- .../launcher3/AppsContainerRecyclerView.java | 26 ++- src/com/android/launcher3/AppsContainerView.java | 225 +++++++++++++++------ src/com/android/launcher3/AppsGridAdapter.java | 149 +++++++++++--- src/com/android/launcher3/AppsListAdapter.java | 143 ------------- src/com/android/launcher3/DeviceProfile.java | 7 + src/com/android/launcher3/Launcher.java | 51 +++-- .../widget/WidgetsContainerRecyclerView.java | 2 +- 20 files changed, 479 insertions(+), 279 deletions(-) create mode 100755 res/drawable-hdpi/ic_arrow_back_grey.png create mode 100755 res/drawable-hdpi/ic_search_grey.png create mode 100755 res/drawable-mdpi/ic_arrow_back_grey.png create mode 100755 res/drawable-mdpi/ic_search_grey.png create mode 100755 res/drawable-xhdpi/ic_arrow_back_grey.png create mode 100755 res/drawable-xhdpi/ic_search_grey.png create mode 100755 res/drawable-xxhdpi/ic_arrow_back_grey.png create mode 100755 res/drawable-xxhdpi/ic_search_grey.png create mode 100755 res/drawable-xxxhdpi/ic_arrow_back_grey.png create mode 100755 res/drawable-xxxhdpi/ic_search_grey.png delete mode 100644 src/com/android/launcher3/AppsListAdapter.java diff --git a/res/drawable-hdpi/ic_arrow_back_grey.png b/res/drawable-hdpi/ic_arrow_back_grey.png new file mode 100755 index 000000000..ccd3900dd Binary files /dev/null and b/res/drawable-hdpi/ic_arrow_back_grey.png differ diff --git a/res/drawable-hdpi/ic_search_grey.png b/res/drawable-hdpi/ic_search_grey.png new file mode 100755 index 000000000..f4c5e27d2 Binary files /dev/null and b/res/drawable-hdpi/ic_search_grey.png differ diff --git a/res/drawable-mdpi/ic_arrow_back_grey.png b/res/drawable-mdpi/ic_arrow_back_grey.png new file mode 100755 index 000000000..11996efe3 Binary files /dev/null and b/res/drawable-mdpi/ic_arrow_back_grey.png differ diff --git a/res/drawable-mdpi/ic_search_grey.png b/res/drawable-mdpi/ic_search_grey.png new file mode 100755 index 000000000..e83891c11 Binary files /dev/null and b/res/drawable-mdpi/ic_search_grey.png differ diff --git a/res/drawable-xhdpi/ic_arrow_back_grey.png b/res/drawable-xhdpi/ic_arrow_back_grey.png new file mode 100755 index 000000000..79b9b486c Binary files /dev/null and b/res/drawable-xhdpi/ic_arrow_back_grey.png differ diff --git a/res/drawable-xhdpi/ic_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png new file mode 100755 index 000000000..bd5fdf444 Binary files /dev/null and b/res/drawable-xhdpi/ic_search_grey.png differ diff --git a/res/drawable-xxhdpi/ic_arrow_back_grey.png b/res/drawable-xxhdpi/ic_arrow_back_grey.png new file mode 100755 index 000000000..8e42e091d Binary files /dev/null and b/res/drawable-xxhdpi/ic_arrow_back_grey.png differ diff --git a/res/drawable-xxhdpi/ic_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png new file mode 100755 index 000000000..1d5c91361 Binary files /dev/null and b/res/drawable-xxhdpi/ic_search_grey.png differ diff --git a/res/drawable-xxxhdpi/ic_arrow_back_grey.png b/res/drawable-xxxhdpi/ic_arrow_back_grey.png new file mode 100755 index 000000000..854a9bd1a Binary files /dev/null and b/res/drawable-xxxhdpi/ic_arrow_back_grey.png differ diff --git a/res/drawable-xxxhdpi/ic_search_grey.png b/res/drawable-xxxhdpi/ic_search_grey.png new file mode 100755 index 000000000..28519fda6 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_search_grey.png differ diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index dfb7b588d..e29cac5e1 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -22,22 +22,55 @@ android:elevation="15dp" android:visibility="gone" android:focusableInTouchMode="true"> - + android:layout_height="52dp" + android:orientation="horizontal" + android:background="@drawable/apps_search_bg"> + + + + + + 8dp 52dp + 8dp 64dp 24sp 6dp diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index c7ee2e99a..477c00fe8 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -86,6 +86,8 @@ public class AlphabeticalAppsList { public String sectionName; // The number of applications in this section public int numAppsInSection; + // The section AdapterItem for this section + public AdapterItem sectionItem; // The first app AdapterItem for this section public AdapterItem firstAppItem; @@ -137,6 +139,9 @@ public class AlphabeticalAppsList { public boolean retainApp(AppInfo info, String sectionName); } + // The maximum number of rows allowed in a merged section before we stop merging + private static final int MAX_ROWS_IN_MERGED_SECTION = Integer.MAX_VALUE; + private List mApps = new ArrayList<>(); private List mFilteredApps = new ArrayList<>(); private List mSectionedFilteredApps = new ArrayList<>(); @@ -145,10 +150,23 @@ public class AlphabeticalAppsList { private Filter mFilter; private AlphabeticIndexCompat mIndexer; private AppNameComparator mAppNameComparator; + private int mNumAppsPerRow; + // The maximum number of section merges we allow at a given time before we stop merging + private int mMaxAllowableMerges = Integer.MAX_VALUE; - public AlphabeticalAppsList(Context context) { + public AlphabeticalAppsList(Context context, int numAppsPerRow) { mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppNameComparator(context); + setNumAppsPerRow(numAppsPerRow); + } + + /** + * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. + */ + public void setNumAppsPerRow(int numAppsPerRow) { + mNumAppsPerRow = numAppsPerRow; + mMaxAllowableMerges = (int) Math.ceil(numAppsPerRow / 2f); + onAppsUpdated(); } /** @@ -179,6 +197,13 @@ public class AlphabeticalAppsList { return mFilteredApps.size(); } + /** + * Returns whether there are is a filter set. + */ + public boolean hasFilter() { + return (mFilter != null); + } + /** * Returns whether there are no filtered results. */ @@ -190,9 +215,11 @@ public class AlphabeticalAppsList { * Sets the current filter for this list of apps. */ public void setFilter(Filter f) { - mFilter = f; - onAppsUpdated(); - mAdapter.notifyDataSetChanged(); + if (mFilter != f) { + mFilter = f; + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); + } } /** @@ -298,9 +325,13 @@ public class AlphabeticalAppsList { lastSectionInfo = new SectionInfo(sectionName); mSections.add(lastSectionInfo); - // Create a new section item + // Create a new section item, this item is used to break the flow of items in the + // list AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName); - mSectionedFilteredApps.add(sectionItem); + if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { + lastSectionInfo.sectionItem = sectionItem; + mSectionedFilteredApps.add(sectionItem); + } } // Create an app item @@ -312,5 +343,53 @@ public class AlphabeticalAppsList { mSectionedFilteredApps.add(appItem); mFilteredApps.add(info); } + + if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { + // Go through each section and try and merge some of the sections + int minNumAppsPerRow = (int) Math.ceil(mNumAppsPerRow / 2f); + int sectionAppCount = 0; + for (int i = 0; i < mSections.size(); i++) { + SectionInfo section = mSections.get(i); + String mergedSectionName = section.sectionName; + sectionAppCount = section.numAppsInSection; + int mergeCount = 1; + // Merge rows if the last app in this section is in a column that is greater than + // 0, but less than the min number of apps per row. In addition, apply the + // constraint to stop merging if the number of rows in the section is greater than + // some limit, and also if there are no lessons to merge. + while (0 < (sectionAppCount % mNumAppsPerRow) && + (sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow && + (int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION && + (i + 1) < mSections.size()) { + SectionInfo nextSection = mSections.remove(i + 1); + // Merge the section names + if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { + mergedSectionName += nextSection.sectionName; + } + // Remove the next section break + mSectionedFilteredApps.remove(nextSection.sectionItem); + if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { + // Update the section names for the two sections + int pos = mSectionedFilteredApps.indexOf(section.firstAppItem); + for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { + AdapterItem item = mSectionedFilteredApps.get(j); + item.sectionName = mergedSectionName; + } + } + // Update the following adapter items of the removed section + int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem); + for (int j = pos; j < mSectionedFilteredApps.size(); j++) { + AdapterItem item = mSectionedFilteredApps.get(j); + item.position--; + } + section.numAppsInSection += nextSection.numAppsInSection; + sectionAppCount += nextSection.numAppsInSection; + mergeCount++; + if (mergeCount >= mMaxAllowableMerges) { + break; + } + } + } + } } } diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index bf478eddd..d91bceac9 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -66,6 +66,7 @@ public class AppsContainerRecyclerView extends RecyclerView private int mScrollbarWidth; private int mScrollbarMinHeight; private int mScrollbarInset; + private RecyclerView.OnScrollListener mScrollListenerProxy; public AppsContainerRecyclerView(Context context) { this(context, null); @@ -102,7 +103,7 @@ public class AppsContainerRecyclerView extends RecyclerView mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; ScrollListener listener = new ScrollListener(); - addOnScrollListener(listener); + setOnScrollListener(listener); } private class ScrollListener extends RecyclerView.OnScrollListener { @@ -112,6 +113,7 @@ public class AppsContainerRecyclerView extends RecyclerView @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { mDy = dy; + mScrollListenerProxy.onScrolled(recyclerView, dx, dy); } } @@ -129,6 +131,13 @@ public class AppsContainerRecyclerView extends RecyclerView mNumAppsPerRow = rowSize; } + /** + * Sets an additional scroll listener, not necessary in master support lib. + */ + public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) { + mScrollListenerProxy = listener; + } + /** * Sets the fast scroller alpha. */ @@ -178,10 +187,6 @@ public class AppsContainerRecyclerView extends RecyclerView handleTouchEvent(ev); } - public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // Do nothing - } - /** * Handles the touch event and determines whether to show the fast scroller (or updates it if * it is already showing). @@ -322,6 +327,7 @@ public class AppsContainerRecyclerView extends RecyclerView // Find the position of the first application in the section that contains the row at the // current progress + List items = mApps.getAdapterItems(); int rowAtProgress = (int) (progress * getNumRows()); int rowCount = 0; AlphabeticalAppsList.SectionInfo lastSectionInfo = null; @@ -333,7 +339,7 @@ public class AppsContainerRecyclerView extends RecyclerView } rowCount += numRowsInSection; } - int position = mApps.getAdapterItems().indexOf(lastSectionInfo.firstAppItem); + int position = items.indexOf(lastSectionInfo.firstAppItem); // Scroll the position into view, anchored at the top of the screen if possible. We call the // scroll method on the LayoutManager directly since it is not exposed by RecyclerView. @@ -342,15 +348,17 @@ public class AppsContainerRecyclerView extends RecyclerView layoutManager.scrollToPositionWithOffset(position, 0); // Return the section name of the row - return mApps.getAdapterItems().get(position).sectionName; + return lastSectionInfo.sectionName; } /** * Returns the bounds for the scrollbar. */ private void updateVerticalScrollbarBounds() { + List items = mApps.getAdapterItems(); + // Skip early if there are no items - if (mApps.getAdapterItems().isEmpty()) { + if (items.isEmpty()) { mVerticalScrollbarBounds.setEmpty(); return; } @@ -369,7 +377,7 @@ public class AppsContainerRecyclerView extends RecyclerView View child = getChildAt(i); int position = getChildPosition(child); if (position != NO_POSITION) { - AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); + AlphabeticalAppsList.AdapterItem item = items.get(position); if (!item.isSectionHeader) { rowIndex = findRowForAppIndex(item.appIndex); rowTopOffset = getLayoutManager().getDecoratedTop(child); diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index c3cf629b8..9122427fd 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -34,7 +34,6 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; - import com.android.launcher3.util.Thunk; import java.util.List; @@ -45,24 +44,32 @@ import java.util.List; */ public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener, - View.OnLongClickListener { + View.OnClickListener, View.OnLongClickListener { - private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; + public static final boolean GRID_MERGE_SECTIONS = true; + public static final boolean GRID_MERGE_SECTION_HEADERS = false; + public static final boolean GRID_HIDE_SECTION_HEADERS = false; - private static final int GRID_LAYOUT = 0; - private static final int LIST_LAYOUT = 1; - private static final int USE_LAYOUT = GRID_LAYOUT; + private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; + private static final boolean DYNAMIC_HEADER_ELEVATION = false; + private static final float HEADER_ELEVATION_DP = 4; + private static final int FADE_IN_DURATION = 175; + private static final int FADE_OUT_DURATION = 125; @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; - private RecyclerView.Adapter mAdapter; + private AppsGridAdapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.ItemDecoration mItemDecoration; private LinearLayout mContentView; @Thunk AppsContainerRecyclerView mAppsRecyclerView; - private EditText mSearchBarView; - + private View mHeaderView; + private View mSearchBarContainerView; + private View mSearchButtonView; + private View mDismissSearchButtonView; + private EditText mSearchBarEditView; + private int mNumAppsPerRow; private Point mLastTouchDownPos = new Point(-1, -1); private Point mLastTouchPos = new Point(); @@ -73,6 +80,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private int mContainerInset; // Fixed bounds container insets private int mFixedBoundsContainerInset; + // RecyclerView scroll position + @Thunk int mRecyclerViewScrollY; public AppsContainerView(Context context) { this(context, null); @@ -93,23 +102,14 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize( R.dimen.apps_container_fixed_bounds_inset); mLauncher = (Launcher) context; - mApps = new AlphabeticalAppsList(context); - if (USE_LAYOUT == GRID_LAYOUT) { - mNumAppsPerRow = grid.appsViewNumCols; - AppsGridAdapter adapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, - mLauncher, this); - adapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); - mLayoutManager = adapter.getLayoutManager(context); - mItemDecoration = adapter.getItemDecoration(); - mAdapter = adapter; - mContentMarginStart = adapter.getContentMarginStart(); - } else if (USE_LAYOUT == LIST_LAYOUT) { - mNumAppsPerRow = 1; - AppsListAdapter adapter = new AppsListAdapter(context, mApps, this, mLauncher, this); - adapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); - mLayoutManager = adapter.getLayoutManager(context); - mAdapter = adapter; - } + mNumAppsPerRow = grid.appsViewNumCols; + mApps = new AlphabeticalAppsList(context, mNumAppsPerRow); + mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, mLauncher, this); + mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); + mAdapter.setNumAppsPerRow(mNumAppsPerRow); + mLayoutManager = mAdapter.getLayoutManager(); + mItemDecoration = mAdapter.getItemDecoration(); + mContentMarginStart = mAdapter.getContentMarginStart(); mApps.setAdapter(mAdapter); } @@ -142,10 +142,10 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett } /** - * Hides the search bar + * Hides the header bar */ - public void hideSearchBar() { - mSearchBarView.setVisibility(View.GONE); + public void hideHeaderBar() { + mHeaderView.setVisibility(View.GONE); updateBackgrounds(); updatePaddings(); } @@ -155,6 +155,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett */ public void scrollToTop() { mAppsRecyclerView.scrollToPosition(0); + mRecyclerViewScrollY = 0; } /** @@ -175,9 +176,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett protected void onFinishInflate() { boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL); - if (USE_LAYOUT == GRID_LAYOUT) { - ((AppsGridAdapter) mAdapter).setRtl(isRtl); - } + mAdapter.setRtl(isRtl); // Work around the search box getting first focus and showing the cursor by // proxying the focus from the content view to the recycler view directly @@ -190,10 +189,20 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett } } }); - mSearchBarView = (EditText) findViewById(R.id.app_search_box); - if (mSearchBarView != null) { - mSearchBarView.addTextChangedListener(this); - mSearchBarView.setOnEditorActionListener(this); + mHeaderView = findViewById(R.id.header); + mHeaderView.setOnClickListener(this); + if (Utilities.isLmpOrAbove() && !DYNAMIC_HEADER_ELEVATION) { + mHeaderView.setElevation(DynamicGrid.pxFromDp(HEADER_ELEVATION_DP, + getContext().getResources().getDisplayMetrics())); + } + mSearchButtonView = mHeaderView.findViewById(R.id.search_button); + mSearchBarContainerView = findViewById(R.id.app_search_container); + mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button); + mDismissSearchButtonView.setOnClickListener(this); + mSearchBarEditView = (EditText) findViewById(R.id.app_search_box); + if (mSearchBarEditView != null) { + mSearchBarEditView.addTextChangedListener(this); + mSearchBarEditView.setOnEditorActionListener(this); } mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view); mAppsRecyclerView.setApps(mApps); @@ -201,6 +210,18 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); mAppsRecyclerView.setHasFixedSize(true); + mAppsRecyclerView.setOnScrollListenerProxy(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + // Do nothing + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mRecyclerViewScrollY += dy; + onRecyclerViewScrolled(); + } + }); if (mItemDecoration != null) { mAppsRecyclerView.addItemDecoration(mItemDecoration); } @@ -225,12 +246,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) { mNumAppsPerRow = grid.appsViewNumCols; mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow); - if (USE_LAYOUT == GRID_LAYOUT) { - ((AppsGridAdapter) mAdapter).setNumAppsPerRow(mNumAppsPerRow); - } + mAdapter.setNumAppsPerRow(mNumAppsPerRow); + mApps.setNumAppsPerRow(mNumAppsPerRow); } mFixedBounds.set(fixedBounds); + if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + mFixedBounds.top = mInsets.top; + mFixedBounds.bottom = getMeasuredHeight(); + } } // Post the updates since they can trigger a relayout, and this call can be triggered from // a layout pass itself. @@ -264,6 +288,15 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett return false; } + @Override + public void onClick(View v) { + if (v == mHeaderView) { + showSearchField(); + } else if (v == mDismissSearchButtonView) { + hideSearchField(true, true); + } + } + @Override public boolean onLongClick(View v) { // Return early if this is not initiated from a touch @@ -363,24 +396,27 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mApps.setFilter(null); } else { String formatStr = getResources().getString(R.string.apps_view_no_search_results); - if (USE_LAYOUT == GRID_LAYOUT) { - ((AppsGridAdapter) mAdapter).setEmptySearchText(String.format(formatStr, - s.toString())); - } else { - ((AppsListAdapter) mAdapter).setEmptySearchText(String.format(formatStr, - s.toString())); - } + mAdapter.setEmptySearchText(String.format(formatStr, s.toString())); final String filterText = s.toString().toLowerCase().replaceAll("\\s+", ""); mApps.setFilter(new AlphabeticalAppsList.Filter() { @Override public boolean retainApp(AppInfo info, String sectionName) { String title = info.title.toString(); - return sectionName.toLowerCase().contains(filterText) || - title.toLowerCase().replaceAll("\\s+", "").contains(filterText); + if (sectionName.toLowerCase().contains(filterText)) { + return true; + } + String[] words = title.toLowerCase().split("\\s+"); + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(filterText)) { + return true; + } + } + return false; } }); } + scrollToTop(); } @Override @@ -396,9 +432,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett AlphabeticalAppsList.AdapterItem item = items.get(i); if (!item.isSectionHeader) { mAppsRecyclerView.getChildAt(i).performClick(); - InputMethodManager imm = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(getWindowToken(), 0); + getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); return true; } } @@ -428,10 +462,22 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - if (mSearchBarView != null) { + if (mSearchBarEditView != null) { if (toWorkspace) { - // Clear the search bar - mSearchBarView.setText(""); + hideSearchField(false, false); + } + } + } + + /** + * Updates the container when the recycler view is scrolled. + */ + private void onRecyclerViewScrolled() { + if (DYNAMIC_HEADER_ELEVATION) { + int elevation = Math.min(mRecyclerViewScrollY, DynamicGrid.pxFromDp(HEADER_ELEVATION_DP, + getContext().getResources().getDisplayMetrics())); + if (Float.compare(mHeaderView.getElevation(), elevation) != 0) { + mHeaderView.setElevation(elevation); } } } @@ -494,8 +540,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private void updatePaddings() { boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL); - boolean hasSearchBar = (mSearchBarView != null) && - (mSearchBarView.getVisibility() == View.VISIBLE); + boolean hasSearchBar = (mSearchBarEditView != null) && + (mSearchBarEditView.getVisibility() == View.VISIBLE); if (mFixedBounds.isEmpty()) { // If there are no fixed bounds, then use the default padding and insets @@ -516,10 +562,10 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset); } - // Update the search bar + // Update the header if (hasSearchBar) { LinearLayout.LayoutParams lp = - (LinearLayout.LayoutParams) mSearchBarView.getLayoutParams(); + (LinearLayout.LayoutParams) mHeaderView.getLayoutParams(); lp.leftMargin = lp.rightMargin = inset; } } @@ -529,8 +575,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett */ private void updateBackgrounds() { int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; - boolean hasSearchBar = (mSearchBarView != null) && - (mSearchBarView.getVisibility() == View.VISIBLE); + boolean hasSearchBar = (mSearchBarEditView != null) && + (mSearchBarEditView.getVisibility() == View.VISIBLE); // Update the background of the reveal view and list to be inset with the fixed bound // insets instead of the default insets @@ -542,4 +588,63 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett getContext().getResources().getDrawable(R.drawable.apps_reveal_bg), inset, 0, inset, 0)); } + + /** + * Shows the search field. + */ + private void showSearchField() { + // Show the search bar and focus the search + mSearchBarContainerView.setVisibility(View.VISIBLE); + mSearchBarContainerView.setAlpha(0f); + mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mSearchBarEditView.requestFocus(); + getInputMethodManager().showSoftInput(mSearchBarEditView, + InputMethodManager.SHOW_IMPLICIT); + } + }); + mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer(); + } + + /** + * Hides the search field. + */ + private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + if (animated) { + // Hide the search bar and focus the recycler view + mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mSearchBarContainerView.setVisibility(View.INVISIBLE); + mSearchBarEditView.setText(""); + mApps.setFilter(null); + if (returnFocusToRecyclerView) { + mAppsRecyclerView.requestFocus(); + } + scrollToTop(); + } + }); + mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer(); + } else { + mSearchBarContainerView.setVisibility(View.INVISIBLE); + mSearchBarEditView.setText(""); + mApps.setFilter(null); + mSearchButtonView.setAlpha(1f); + if (returnFocusToRecyclerView) { + mAppsRecyclerView.requestFocus(); + } + scrollToTop(); + } + getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); + } + + /** + * Returns an input method manager. + */ + private InputMethodManager getInputMethodManager() { + return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + } } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 5bc3981df..62d9129c9 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -4,16 +4,18 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.Rect; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; - import com.android.launcher3.util.Thunk; +import java.util.HashMap; import java.util.List; @@ -23,6 +25,7 @@ import java.util.List; class AppsGridAdapter extends RecyclerView.Adapter { public static final String TAG = "AppsGridAdapter"; + private static final boolean DEBUG = false; private static final int SECTION_BREAK_VIEW_TYPE = 0; private static final int ICON_VIEW_TYPE = 1; @@ -48,6 +51,12 @@ class AppsGridAdapter extends RecyclerView.Adapter { * Helper class to size the grid items. */ public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup { + + public GridSpanSizer() { + super(); + setSpanIndexCacheEnabled(true); + } + @Override public int getSpanSize(int position) { if (mApps.hasNoFilteredResults()) { @@ -57,7 +66,11 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (mApps.getAdapterItems().get(position).isSectionHeader) { // Section break spans full width - return mAppsPerRow; + if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) { + return 0; + } else { + return mAppsPerRow; + } } else { return 1; } @@ -69,31 +82,88 @@ class AppsGridAdapter extends RecyclerView.Adapter { */ public class GridItemDecoration extends RecyclerView.ItemDecoration { + private static final boolean FADE_OUT_SECTIONS = false; + + private HashMap mCachedSectionBounds = new HashMap<>(); + private Rect mTmpBounds = new Rect(); + @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + if (mApps.hasFilter()) { + return; + } + List items = mApps.getAdapterItems(); + String lastSectionName = null; + int appIndexInSection = 0; + int lastSectionTop = 0; + int lastSectionHeight = 0; for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); - if (shouldDrawItemSection(holder, child, items)) { - // Draw at the parent - AlphabeticalAppsList.AdapterItem item = - items.get(holder.getPosition()); - String section = item.sectionName; - mSectionTextPaint.getTextBounds(section, 0, section.length(), - mTmpBounds); - if (mIsRtl) { - int left = parent.getWidth() - mPaddingStart - mStartMargin; - c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, - child.getTop() + (2 * child.getPaddingTop()) + - mTmpBounds.height(), mSectionTextPaint); - } else { - int left = mPaddingStart; - c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2, - child.getTop() + (2 * child.getPaddingTop()) + - mTmpBounds.height(), mSectionTextPaint); + if (shouldDrawItemSection(holder, child, i, items)) { + int cellTopOffset = (2 * child.getPaddingTop()); + int pos = holder.getPosition(); + AlphabeticalAppsList.AdapterItem item = items.get(pos); + if (!item.sectionName.equals(lastSectionName)) { + lastSectionName = item.sectionName; + + // Find the section code points + String sectionBegin = null; + String sectionEnd = null; + int charOffset = 0; + while (charOffset < item.sectionName.length()) { + int codePoint = item.sectionName.codePointAt(charOffset); + int codePointSize = Character.charCount(codePoint); + if (charOffset == 0) { + // The first code point + sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize); + } else if ((charOffset + codePointSize) >= item.sectionName.length()) { + // The last code point + sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize); + } + charOffset += codePointSize; + } + + Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin); + int minTop = cellTopOffset + sectionBeginBounds.y; + int top = child.getTop() + cellTopOffset + sectionBeginBounds.y; + int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : + mPaddingStart; + int col = appIndexInSection % mAppsPerRow; + int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1); + int alpha = 255; + boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName); + if (fixedToRow) { + alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop))); + } else { + // If we aren't fixed to the current row, then bound into the viewport + top = Math.max(minTop, top); + } + if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) { + top += lastSectionTop - top + lastSectionHeight; + } + if (FADE_OUT_SECTIONS) { + mSectionTextPaint.setAlpha(alpha); + } + if (sectionEnd != null) { + Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd); + c.drawText(sectionBegin + "/" + sectionEnd, + left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top, + mSectionTextPaint); + } else { + c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top, + mSectionTextPaint); + } + lastSectionTop = top; + lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset; } } + if (holder.mIsSectionHeader) { + appIndexInSection = 0; + } else { + appIndexInSection++; + } } } @@ -103,7 +173,17 @@ class AppsGridAdapter extends RecyclerView.Adapter { // Do nothing } - private boolean shouldDrawItemSection(ViewHolder holder, View child, + private Point getAndCacheSectionBounds(String sectionName) { + Point bounds = mCachedSectionBounds.get(sectionName); + if (bounds == null) { + mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds); + bounds = new Point(mTmpBounds.width(), mTmpBounds.height()); + mCachedSectionBounds.put(sectionName, bounds); + } + return bounds; + } + + private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex, List items) { // Ensure item is not already removed GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) @@ -121,11 +201,19 @@ class AppsGridAdapter extends RecyclerView.Adapter { } // Ensure we have a holder position int pos = holder.getPosition(); - if (pos <= 0 || pos >= items.size()) { + if (pos < 0 || pos >= items.size()) { + return false; + } + // Ensure this is not a section header + if (items.get(pos).isSectionHeader) { return false; } - // Only draw the first item in the section (the first one after the section header) - return items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader; + // Only draw the header for the first item in a section, or whenever the sub-sections + // changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but + // AppsContainerView.GRID_MERGE_SECTION_HEADERS is false) + return (childIndex == 0) || + items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader || + (!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName)); } } @@ -144,8 +232,8 @@ class AppsGridAdapter extends RecyclerView.Adapter { // Section drawing @Thunk int mPaddingStart; @Thunk int mStartMargin; + @Thunk int mSectionHeaderOffset; @Thunk Paint mSectionTextPaint; - @Thunk Rect mTmpBounds = new Rect(); public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, @@ -163,7 +251,10 @@ class AppsGridAdapter extends RecyclerView.Adapter { mTouchListener = touchListener; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; - mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); + if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) { + mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); + mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset); + } mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset); mSectionTextPaint = new Paint(); mSectionTextPaint.setTextSize(res.getDimensionPixelSize( @@ -197,7 +288,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { /** * Returns the grid layout manager. */ - public GridLayoutManager getLayoutManager(Context context) { + public GridLayoutManager getLayoutManager() { return mGridLayoutMgr; } @@ -205,7 +296,11 @@ class AppsGridAdapter extends RecyclerView.Adapter { * Returns the item decoration for the recycler view. */ public RecyclerView.ItemDecoration getItemDecoration() { - return mItemDecoration; + // We don't draw any headers when we are uncomfortably dense + if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) { + return mItemDecoration; + } + return null; } /** diff --git a/src/com/android/launcher3/AppsListAdapter.java b/src/com/android/launcher3/AppsListAdapter.java deleted file mode 100644 index ffd309261..000000000 --- a/src/com/android/launcher3/AppsListAdapter.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.android.launcher3; - -import android.content.Context; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; - -/** - * The linear list view adapter for all the apps. - */ -class AppsListAdapter extends RecyclerView.Adapter { - - /** - * ViewHolder for each row. - */ - public static class ViewHolder extends RecyclerView.ViewHolder { - public View mContent; - - public ViewHolder(View v) { - super(v); - mContent = v; - } - } - - private static final int SECTION_BREAK_VIEW_TYPE = 0; - private static final int ICON_VIEW_TYPE = 1; - private static final int EMPTY_VIEW_TYPE = 2; - - private LayoutInflater mLayoutInflater; - private AlphabeticalAppsList mApps; - private View.OnTouchListener mTouchListener; - private View.OnClickListener mIconClickListener; - private View.OnLongClickListener mIconLongClickListener; - private String mEmptySearchText; - - public AppsListAdapter(Context context, AlphabeticalAppsList apps, - View.OnTouchListener touchListener, View.OnClickListener iconClickListener, - View.OnLongClickListener iconLongClickListener) { - mApps = apps; - mLayoutInflater = LayoutInflater.from(context); - mTouchListener = touchListener; - mIconClickListener = iconClickListener; - mIconLongClickListener = iconLongClickListener; - } - - public RecyclerView.LayoutManager getLayoutManager(Context context) { - return new LinearLayoutManager(context); - } - - /** - * Sets the text to show when there are no apps. - */ - public void setEmptySearchText(String query) { - mEmptySearchText = query; - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - switch (viewType) { - case EMPTY_VIEW_TYPE: - return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent, - false)); - case SECTION_BREAK_VIEW_TYPE: - return new ViewHolder(new View(parent.getContext())); - case ICON_VIEW_TYPE: - // Inflate the row and all the icon children necessary - ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.apps_list_row_view, - parent, false); - BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - R.layout.apps_list_row_icon_view, row, false); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, 1); - lp.gravity = Gravity.CENTER_VERTICAL; - icon.setLayoutParams(lp); - icon.setOnTouchListener(mTouchListener); - icon.setOnClickListener(mIconClickListener); - icon.setOnLongClickListener(mIconLongClickListener); - icon.setFocusable(true); - row.addView(icon); - return new ViewHolder(row); - default: - throw new RuntimeException("Unexpected view type"); - } - } - - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - switch (holder.getItemViewType()) { - case ICON_VIEW_TYPE: - AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); - ViewGroup content = (ViewGroup) holder.mContent; - String sectionDescription = item.sectionName; - - // Bind the section header - boolean showSectionHeader = true; - if (position > 0) { - AlphabeticalAppsList.AdapterItem prevItem = - mApps.getAdapterItems().get(position - 1); - showSectionHeader = prevItem.isSectionHeader; - } - TextView tv = (TextView) content.findViewById(R.id.section); - if (showSectionHeader) { - tv.setText(sectionDescription); - tv.setVisibility(View.VISIBLE); - } else { - tv.setVisibility(View.INVISIBLE); - } - - // Bind the icon - BubbleTextView icon = (BubbleTextView) content.getChildAt(1); - icon.applyFromApplicationInfo(item.appInfo); - break; - case EMPTY_VIEW_TYPE: - TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text); - emptyViewText.setText(mEmptySearchText); - break; - } - } - - @Override - public int getItemCount() { - if (mApps.hasNoFilteredResults()) { - // For the empty view - return 1; - } - return mApps.getAdapterItems().size(); - } - - @Override - public int getItemViewType(int position) { - if (mApps.hasNoFilteredResults()) { - return EMPTY_VIEW_TYPE; - } else if (mApps.getAdapterItems().get(position).isSectionHeader) { - return SECTION_BREAK_VIEW_TYPE; - } - return ICON_VIEW_TYPE; - } -} diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index deb807501..918517ebd 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -428,6 +428,13 @@ public class DeviceProfile { } public boolean updateAppsViewNumCols(Resources res, int containerWidth) { + if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) { + if (appsViewNumCols != allAppsNumCols) { + appsViewNumCols = allAppsNumCols; + return true; + } + return false; + } int appsViewLeftMarginPx = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 339b4e498..c0f09f486 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -135,6 +135,9 @@ public class Launcher extends Activity static final String TAG = "Launcher"; static final boolean LOGD = true; + // Temporary flag + static final boolean DISABLE_ALL_APPS_SEARCH_INTEGRATION = true; + static final boolean PROFILE_STARTUP = false; static final boolean DEBUG_WIDGETS = true; static final boolean DEBUG_STRICT_MODE = false; @@ -530,10 +533,12 @@ public class Launcher extends Activity @Override public void dismissAllApps() { - // Dismiss All Apps if we aren't already paused/invisible - if (!mPaused) { - showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, - null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */); + if (!DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + // Dismiss All Apps if we aren't already paused/invisible + if (!mPaused) { + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, + null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */); + } } } }); @@ -1019,7 +1024,7 @@ public class Launcher extends Activity mOnResumeState = State.NONE; // Restore the apps state if we are in all apps - if (mState == State.APPS) { + if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mState == State.APPS) { if (mLauncherCallbacks != null) { mLauncherCallbacks.onAllAppsShown(); } @@ -1453,8 +1458,8 @@ public class Launcher extends Activity // Setup Apps mAppsView = (AppsContainerView) findViewById(R.id.apps_view); - if (mLauncherCallbacks != null && mLauncherCallbacks.overrideAllAppsSearch()) { - mAppsView.hideSearchBar(); + if (isAllAppsSearchOverridden()) { + mAppsView.hideHeaderBar(); } // Setup AppsCustomize @@ -2877,15 +2882,22 @@ public class Launcher extends Activity /** Updates the interaction state. */ public void updateInteraction(Workspace.State fromState, Workspace.State toState) { - // Only update the interacting state if we are transitioning to/from a view without an + // Only update the interacting state if we are transitioning to/from a view with an // overlay - boolean fromStateWithoutOverlay = fromState != Workspace.State.NORMAL && - fromState != Workspace.State.NORMAL_HIDDEN; - boolean toStateWithoutOverlay = toState != Workspace.State.NORMAL && - toState != Workspace.State.NORMAL_HIDDEN; - if (toStateWithoutOverlay) { + boolean fromStateWithOverlay; + boolean toStateWithOverlay; + if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + fromStateWithOverlay = fromState != Workspace.State.NORMAL; + toStateWithOverlay = toState != Workspace.State.NORMAL; + } else { + fromStateWithOverlay = fromState != Workspace.State.NORMAL && + fromState != Workspace.State.NORMAL_HIDDEN; + toStateWithOverlay = toState != Workspace.State.NORMAL && + toState != Workspace.State.NORMAL_HIDDEN; + } + if (toStateWithOverlay) { onInteractionBegin(); - } else if (fromStateWithoutOverlay) { + } else if (fromStateWithOverlay) { onInteractionEnd(); } } @@ -3367,7 +3379,7 @@ public class Launcher extends Activity .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); if (notifyLauncherCallbacks) { // Dismiss all apps when the workspace is shown - if (mLauncherCallbacks != null) { + if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) { mLauncherCallbacks.onAllAppsHidden(); } } @@ -3419,7 +3431,7 @@ public class Launcher extends Activity if (toState == State.APPS) { mStateTransitionAnimation.startAnimationToAllApps(animated); - if (mLauncherCallbacks != null) { + if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) { mLauncherCallbacks.onAllAppsShown(); } } else { @@ -3472,7 +3484,7 @@ public class Launcher extends Activity if (successfulDrop) { // We need to trigger all apps hidden to notify search to update itself before the // delayed call to showWorkspace below - if (mLauncherCallbacks != null) { + if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) { mLauncherCallbacks.onAllAppsHidden(); } } @@ -4454,9 +4466,12 @@ public class Launcher extends Activity /** * Returns whether the launcher callbacks overrides search in all apps. - * @return */ @Thunk boolean isAllAppsSearchOverridden() { + if (DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + return false; + } + if (mLauncherCallbacks != null) { return mLauncherCallbacks.overrideAllAppsSearch(); } diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java index f70f170ed..80e13bcf2 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java @@ -51,7 +51,7 @@ public class WidgetsContainerRecyclerView extends RecyclerView mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; ScrollListener listener = new ScrollListener(); - addOnScrollListener(listener); + setOnScrollListener(listener); } private class ScrollListener extends RecyclerView.OnScrollListener { -- cgit v1.2.3 From 0a71b9d83fc425421d7e2105f429b01ae8fe3428 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 7 May 2015 11:14:42 -0700 Subject: Adding abstract methods for new RecyclerView lib. Change-Id: I50c3dfaecd79a87340ed4eeaa3195a26311062e0 --- src/com/android/launcher3/AppsContainerRecyclerView.java | 4 ++++ src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index d91bceac9..7f64be2f5 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -187,6 +187,10 @@ public class AppsContainerRecyclerView extends RecyclerView handleTouchEvent(ev); } + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS + } + /** * Handles the touch event and determines whether to show the fast scroller (or updates it if * it is already showing). diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java index 80e13bcf2..65694bfaa 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java @@ -87,4 +87,8 @@ public class WidgetsContainerRecyclerView extends RecyclerView public void onTouchEvent(RecyclerView rv, MotionEvent ev) { // Do nothing. } + + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS + } } \ No newline at end of file -- cgit v1.2.3 From 219d048711a2c26b7b00f3a0b1d66703d135519a Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 7 May 2015 12:51:42 -0700 Subject: Widgets should also be bound when binding all apps. b/20915830 Change-Id: I772156f8a8d361f485fb4d7edc447c2cee2edf23 --- src/com/android/launcher3/LauncherModel.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7efdf3284..e81c8c285 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2810,6 +2810,8 @@ public class LauncherModel extends BroadcastReceiver } else { mHandler.post(r); } + loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks), + false /* refresh */); } private void loadAllApps() { @@ -2871,8 +2873,6 @@ public class LauncherModel extends BroadcastReceiver final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAllApplications(added); - loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, - true /* refresh */); if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - bindTime) + "ms"); @@ -2885,6 +2885,8 @@ public class LauncherModel extends BroadcastReceiver // Cleanup any data stored for a deleted user. ManagedProfileHeuristic.processAllUsers(profiles, mContext); + loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks), + true /* refresh */); if (DEBUG_LOADERS) { Log.d(TAG, "Icons processed in " + (SystemClock.uptimeMillis() - loadTime) + "ms"); -- cgit v1.2.3 From f50c12788c1ebbd28e9bc45575601540983c6562 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 7 May 2015 13:26:17 -0700 Subject: Fixing issue where apps were not re-sorted after updating. Bug: 20163738 Change-Id: I2fbefc6f451d8eef3d17f727be450a04204a9ca3 --- src/com/android/launcher3/AlphabeticalAppsList.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 477c00fe8..e9a52d5a5 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -226,7 +226,6 @@ public class AlphabeticalAppsList { * Sets the current set of apps. */ public void setApps(List apps) { - Collections.sort(apps, mAppNameComparator.getComparator()); mApps.clear(); mApps.addAll(apps); onAppsUpdated(); @@ -241,6 +240,8 @@ public class AlphabeticalAppsList { for (AppInfo info : apps) { addApp(info); } + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); } /** @@ -251,12 +252,12 @@ public class AlphabeticalAppsList { int index = mApps.indexOf(info); if (index != -1) { mApps.set(index, info); - onAppsUpdated(); - mAdapter.notifyItemChanged(index); } else { addApp(info); } } + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); } /** @@ -267,10 +268,10 @@ public class AlphabeticalAppsList { int removeIndex = findAppByComponent(mApps, info); if (removeIndex != -1) { mApps.remove(removeIndex); - onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } } + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); } /** @@ -290,14 +291,12 @@ public class AlphabeticalAppsList { } /** - * Implementation to actually add an app to the alphabetic list + * Implementation to actually add an app to the alphabetic list, but does not notify. */ private void addApp(AppInfo info) { int index = Collections.binarySearch(mApps, info, mAppNameComparator.getComparator()); if (index < 0) { mApps.add(-(index + 1), info); - onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } } @@ -305,6 +304,9 @@ public class AlphabeticalAppsList { * Updates internals when the set of apps are updated. */ private void onAppsUpdated() { + // Sort the list of apps + Collections.sort(mApps, mAppNameComparator.getComparator()); + // Recreate the filtered and sectioned apps (for convenience for the grid layout) mFilteredApps.clear(); mSections.clear(); -- cgit v1.2.3 From 75deaf3d8f369bd13ea59ec3740324a5cfeb3099 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 7 May 2015 16:13:12 -0700 Subject: Using xml-drawable and elevation as folder background Change-Id: I21376bd3de8cd58c646d0898d5a8303af9057218 --- res/layout/user_folder.xml | 2 +- res/values/dimens.xml | 9 +++++++-- src/com/android/launcher3/DragView.java | 4 ++++ src/com/android/launcher3/Folder.java | 22 ++++++++++++++++++---- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index cd3a051ad..5bacc9605 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -19,7 +19,7 @@ xmlns:launcher="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@drawable/quantum_panel" + android:elevation="5dp" android:orientation="vertical" > 20dp - + 12dp + + 30dp + 8dp 120dp @@ -107,6 +111,7 @@ 4dp 10dp + 8dp 24dp diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index a4b6704ac..3eec3d9ee 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -129,6 +129,10 @@ public class DragView extends View { int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); measure(ms, ms); mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + + if (Utilities.isLmpOrAbove()) { + setElevation(getResources().getDimension(R.dimen.drag_elevation)); + } } /** Sets the scale of the view over the normal workspace icon size. */ diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index a2828054c..d98b0eb6a 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -27,6 +27,7 @@ import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.drawable.InsetDrawable; import android.os.Build; import android.text.InputType; import android.text.Selection; @@ -180,6 +181,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // name is complete, we have something to focus on, thus hiding the cursor and giving // reliable behavior when clicking the text field (since it will always gain focus on click). setFocusableInTouchMode(true); + + if (Utilities.isLmpOrAbove()) { + int padding = getResources().getDimensionPixelSize(R.dimen.folder_shadow_padding); + setBackground(new InsetDrawable( + getResources().getDrawable(R.drawable.apps_list_bg), + padding, padding, padding, padding)); + } else { + setBackgroundResource(R.drawable.quantum_panel); + } } @Override @@ -470,9 +480,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0); PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0); + Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty); + drift.setDuration(mMaterialExpandDuration); + drift.setStartDelay(mMaterialExpandStagger); + drift.setInterpolator(new LogDecelerateInterpolator(100, 0)); + int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX()); int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY()); float radius = (float) Math.hypot(rx, ry); + AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); Animator reveal = LauncherAnimUtils.createCircularReveal(this, (int) getPivotX(), (int) getPivotY(), 0, radius); @@ -491,10 +507,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList textAlpha.setStartDelay(mMaterialExpandStagger); textAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty); - drift.setDuration(mMaterialExpandDuration); - drift.setStartDelay(mMaterialExpandStagger); - drift.setInterpolator(new LogDecelerateInterpolator(60, 0)); anim.play(drift); anim.play(iconsAlpha); @@ -504,10 +516,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList openFolderAnim = anim; mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null); + mFooter.setLayerType(LAYER_TYPE_HARDWARE, null); onCompleteRunnable = new Runnable() { @Override public void run() { mContentWrapper.setLayerType(LAYER_TYPE_NONE, null); + mContentWrapper.setLayerType(LAYER_TYPE_NONE, null); } }; } -- cgit v1.2.3 From de34aa401e08abe10027af208d5d6b339f4c4895 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 7 May 2015 18:21:28 -0700 Subject: Updating sticky headers. - The whole section's headers are drawn together, moving as a group until it reaches the individual bounds for each letter in the section - Adding animation to search button -> field transition - Fixing section header text measuring causing sections not to be centered - Forcing the merge to stop if an app has > 3 full rows; on both phone and tablet, merging a large section with anything else seems to be less useful --- res/layout/apps_list_view.xml | 3 +- .../android/launcher3/AlphabeticalAppsList.java | 43 ++++-- .../launcher3/AppsContainerSearchEditTextView.java | 65 +++++++++ src/com/android/launcher3/AppsContainerView.java | 59 ++++++-- src/com/android/launcher3/AppsGridAdapter.java | 161 +++++++++++++-------- 5 files changed, 246 insertions(+), 85 deletions(-) create mode 100644 src/com/android/launcher3/AppsContainerSearchEditTextView.java diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index e29cac5e1..a726cd8fe 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -43,7 +43,7 @@ android:paddingBottom="12dp" android:contentDescription="@string/all_apps_button_label" android:src="@drawable/ic_arrow_back_grey" /> - mApps = new ArrayList<>(); private List mFilteredApps = new ArrayList<>(); @@ -314,6 +324,7 @@ public class AlphabeticalAppsList { SectionInfo lastSectionInfo = null; int position = 0; int appIndex = 0; + int sectionAppIndex = 0; for (AppInfo info : mApps) { String sectionName = mIndexer.computeSectionName(info.title.toString().trim()); @@ -325,11 +336,12 @@ public class AlphabeticalAppsList { // Create a new section if necessary if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) { lastSectionInfo = new SectionInfo(sectionName); + sectionAppIndex = 0; mSections.add(lastSectionInfo); // Create a new section item, this item is used to break the flow of items in the // list - AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName); + AdapterItem sectionItem = AdapterItem.asSection(position++, lastSectionInfo); if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { lastSectionInfo.sectionItem = sectionItem; mSectionedFilteredApps.add(sectionItem); @@ -337,7 +349,8 @@ public class AlphabeticalAppsList { } // Create an app item - AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++); + AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, + sectionAppIndex++, info, appIndex++); lastSectionInfo.numAppsInSection++; if (lastSectionInfo.firstAppItem == null) { lastSectionInfo.firstAppItem = appItem; @@ -361,25 +374,33 @@ public class AlphabeticalAppsList { // some limit, and also if there are no lessons to merge. while (0 < (sectionAppCount % mNumAppsPerRow) && (sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow && - (int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION && + (sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION && (i + 1) < mSections.size()) { SectionInfo nextSection = mSections.remove(i + 1); + // Merge the section names if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { mergedSectionName += nextSection.sectionName; } // Remove the next section break mSectionedFilteredApps.remove(nextSection.sectionItem); + int pos = mSectionedFilteredApps.indexOf(section.firstAppItem); if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { // Update the section names for the two sections - int pos = mSectionedFilteredApps.indexOf(section.firstAppItem); for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { AdapterItem item = mSectionedFilteredApps.get(j); item.sectionName = mergedSectionName; + item.sectionInfo = section; } } - // Update the following adapter items of the removed section - int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem); + // Point the section for these new apps to the merged section + for (int j = pos + section.numAppsInSection; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { + AdapterItem item = mSectionedFilteredApps.get(j); + item.sectionInfo = section; + item.sectionAppIndex += section.numAppsInSection; + } + // Update the following adapter items of the removed section item + pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem); for (int j = pos; j < mSectionedFilteredApps.size(); j++) { AdapterItem item = mSectionedFilteredApps.get(j); item.position--; diff --git a/src/com/android/launcher3/AppsContainerSearchEditTextView.java b/src/com/android/launcher3/AppsContainerSearchEditTextView.java new file mode 100644 index 000000000..c688237b2 --- /dev/null +++ b/src/com/android/launcher3/AppsContainerSearchEditTextView.java @@ -0,0 +1,65 @@ +/* + * 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.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.widget.EditText; + + +/** + * The edit text for the search container + */ +public class AppsContainerSearchEditTextView extends EditText { + + /** + * Implemented by listeners of the back key. + */ + public interface OnBackKeyListener { + public void onBackKey(); + } + + private OnBackKeyListener mBackKeyListener; + + public AppsContainerSearchEditTextView(Context context) { + this(context, null); + } + + public AppsContainerSearchEditTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AppsContainerSearchEditTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setOnBackKeyListener(OnBackKeyListener listener) { + mBackKeyListener = listener; + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + // If this is a back key, propagate the key back to the listener + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + if (mBackKeyListener != null) { + mBackKeyListener.onBackKey(); + } + return false; + } + return super.onKeyPreIme(keyCode, event); + } +} diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 9122427fd..0aa1e67d1 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -52,9 +52,11 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; private static final boolean DYNAMIC_HEADER_ELEVATION = false; + private static final boolean DISMISS_SEARCH_ON_BACK = true; private static final float HEADER_ELEVATION_DP = 4; private static final int FADE_IN_DURATION = 175; - private static final int FADE_OUT_DURATION = 125; + private static final int FADE_OUT_DURATION = 100; + private static final int SEARCH_TRANSLATION_X_DP = 18; @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; @@ -68,7 +70,7 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private View mSearchBarContainerView; private View mSearchButtonView; private View mDismissSearchButtonView; - private EditText mSearchBarEditView; + private AppsContainerSearchEditTextView mSearchBarEditView; private int mNumAppsPerRow; private Point mLastTouchDownPos = new Point(-1, -1); @@ -199,10 +201,19 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mSearchBarContainerView = findViewById(R.id.app_search_container); mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button); mDismissSearchButtonView.setOnClickListener(this); - mSearchBarEditView = (EditText) findViewById(R.id.app_search_box); + mSearchBarEditView = (AppsContainerSearchEditTextView) findViewById(R.id.app_search_box); if (mSearchBarEditView != null) { mSearchBarEditView.addTextChangedListener(this); mSearchBarEditView.setOnEditorActionListener(this); + if (DISMISS_SEARCH_ON_BACK) { + mSearchBarEditView.setOnBackKeyListener( + new AppsContainerSearchEditTextView.OnBackKeyListener() { + @Override + public void onBackKey() { + hideSearchField(true, true); + } + }); + } } mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view); mAppsRecyclerView.setApps(mApps); @@ -594,9 +605,16 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett */ private void showSearchField() { // Show the search bar and focus the search + final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP, + getContext().getResources().getDisplayMetrics()); mSearchBarContainerView.setVisibility(View.VISIBLE); mSearchBarContainerView.setAlpha(0f); - mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer() + mSearchBarContainerView.setTranslationX(translationX); + mSearchBarContainerView.animate() + .alpha(1f) + .translationX(0) + .setDuration(FADE_IN_DURATION) + .withLayer() .withEndAction(new Runnable() { @Override public void run() { @@ -605,38 +623,57 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett InputMethodManager.SHOW_IMPLICIT); } }); - mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer(); + mSearchButtonView.animate() + .alpha(0f) + .translationX(-translationX) + .setDuration(FADE_OUT_DURATION) + .withLayer(); } /** * Hides the search field. */ private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; + final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP, + getContext().getResources().getDisplayMetrics()); if (animated) { // Hide the search bar and focus the recycler view - mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer() + mSearchBarContainerView.animate() + .alpha(0f) + .translationX(0) + .setDuration(FADE_IN_DURATION) + .withLayer() .withEndAction(new Runnable() { @Override public void run() { mSearchBarContainerView.setVisibility(View.INVISIBLE); - mSearchBarEditView.setText(""); + if (resetTextField) { + mSearchBarEditView.setText(""); + } mApps.setFilter(null); if (returnFocusToRecyclerView) { mAppsRecyclerView.requestFocus(); } - scrollToTop(); } }); - mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer(); + mSearchButtonView.setTranslationX(-translationX); + mSearchButtonView.animate() + .alpha(1f) + .translationX(0) + .setDuration(FADE_OUT_DURATION) + .withLayer(); } else { mSearchBarContainerView.setVisibility(View.INVISIBLE); - mSearchBarEditView.setText(""); + if (resetTextField) { + mSearchBarEditView.setText(""); + } mApps.setFilter(null); mSearchButtonView.setAlpha(1f); + mSearchButtonView.setTranslationX(0f); if (returnFocusToRecyclerView) { mAppsRecyclerView.requestFocus(); } - scrollToTop(); } getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 62d9129c9..d83d6c97c 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -5,10 +5,10 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -84,8 +84,10 @@ class AppsGridAdapter extends RecyclerView.Adapter { private static final boolean FADE_OUT_SECTIONS = false; - private HashMap mCachedSectionBounds = new HashMap<>(); + private HashMap mCachedSectionBounds = new HashMap<>(); private Rect mTmpBounds = new Rect(); + private String[] mTmpSections = new String[2]; + private PointF[] mTmpSectionBounds = new PointF[2]; @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { @@ -94,75 +96,83 @@ class AppsGridAdapter extends RecyclerView.Adapter { } List items = mApps.getAdapterItems(); - String lastSectionName = null; - int appIndexInSection = 0; + int childCount = parent.getChildCount(); int lastSectionTop = 0; int lastSectionHeight = 0; - for (int i = 0; i < parent.getChildCount(); i++) { + for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); if (shouldDrawItemSection(holder, child, i, items)) { - int cellTopOffset = (2 * child.getPaddingTop()); + // At this point, we only draw sections for each section break; + int viewTopOffset = (2 * child.getPaddingTop()); int pos = holder.getPosition(); AlphabeticalAppsList.AdapterItem item = items.get(pos); - if (!item.sectionName.equals(lastSectionName)) { - lastSectionName = item.sectionName; - - // Find the section code points - String sectionBegin = null; - String sectionEnd = null; - int charOffset = 0; - while (charOffset < item.sectionName.length()) { - int codePoint = item.sectionName.codePointAt(charOffset); - int codePointSize = Character.charCount(codePoint); - if (charOffset == 0) { - // The first code point - sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize); - } else if ((charOffset + codePointSize) >= item.sectionName.length()) { - // The last code point - sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize); - } - charOffset += codePointSize; + AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo; + + // Draw all the sections for this index + String lastSectionName = item.sectionName; + for (int j = item.sectionAppIndex; j < sectionInfo.numAppsInSection;j++, pos++) { + AlphabeticalAppsList.AdapterItem nextItem = items.get(pos); + if (nextItem.sectionInfo != sectionInfo) { + break; + } + if (j > item.sectionAppIndex && nextItem.sectionName.equals(lastSectionName)) { + continue; } - Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin); - int minTop = cellTopOffset + sectionBeginBounds.y; - int top = child.getTop() + cellTopOffset + sectionBeginBounds.y; - int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : + // Find the section code points + getSectionLetters(nextItem.sectionName, mTmpSections, mTmpSectionBounds); + String sectionBegin = mTmpSections[0]; + String sectionEnd = mTmpSections[1]; + PointF sectionBeginBounds = mTmpSectionBounds[0]; + PointF sectionEndBounds = mTmpSectionBounds[1]; + + // Calculate where to draw the section + int sectionBaseline = (int) (viewTopOffset + sectionBeginBounds.y); + int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : mPaddingStart; - int col = appIndexInSection % mAppsPerRow; - int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1); - int alpha = 255; - boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName); - if (fixedToRow) { - alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop))); - } else { - // If we aren't fixed to the current row, then bound into the viewport - top = Math.max(minTop, top); + int y = child.getTop() + sectionBaseline; + + // Determine whether this is the last row with apps in that section, if + // so, then fix the section to the row allowing it to scroll past the + // baseline, otherwise, bound it to the baseline so it's in the viewport + int appIndexInSection = items.get(pos).sectionAppIndex; + int nextRowPos = Math.min(items.size() - 1, + pos + mAppsPerRow - (appIndexInSection % mAppsPerRow)); + boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(nextItem.sectionName); + if (!fixedToRow) { + y = Math.max(sectionBaseline, y); } - if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) { - top += lastSectionTop - top + lastSectionHeight; + + // In addition, if it overlaps with the last section that was drawn, then + // offset it so that it does not overlap + if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) { + y += lastSectionTop - y + lastSectionHeight; } + + // Draw the section header if (FADE_OUT_SECTIONS) { + int alpha = 255; + if (fixedToRow) { + alpha = Math.min(255, (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); + } mSectionTextPaint.setAlpha(alpha); } if (sectionEnd != null) { - Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd); + // If there is a range, draw the range c.drawText(sectionBegin + "/" + sectionEnd, - left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top, + x + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, y, mSectionTextPaint); } else { - c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top, + c.drawText(sectionBegin, (int) (x + (mStartMargin / 2f) - (sectionBeginBounds.x / 2f)), y, mSectionTextPaint); } - lastSectionTop = top; - lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset; + + lastSectionTop = y; + lastSectionHeight = (int) (sectionBeginBounds.y + mSectionHeaderOffset); + lastSectionName = nextItem.sectionName; } - } - if (holder.mIsSectionHeader) { - appIndexInSection = 0; - } else { - appIndexInSection++; + i += (sectionInfo.numAppsInSection - item.sectionAppIndex); } } } @@ -173,16 +183,50 @@ class AppsGridAdapter extends RecyclerView.Adapter { // Do nothing } - private Point getAndCacheSectionBounds(String sectionName) { - Point bounds = mCachedSectionBounds.get(sectionName); + /** + * Given a section name, return the first and last section letters. + */ + private void getSectionLetters(String sectionName, String[] lettersOut, PointF[] boundsOut) { + lettersOut[0] = lettersOut[1] = null; + boundsOut[0] = boundsOut[1] = null; + if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { + int charOffset = 0; + while (charOffset < sectionName.length()) { + int codePoint = sectionName.codePointAt(charOffset); + int codePointSize = Character.charCount(codePoint); + if (charOffset == 0) { + // The first code point + lettersOut[0] = sectionName.substring(charOffset, charOffset + codePointSize); + boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]); + } else if ((charOffset + codePointSize) >= sectionName.length()) { + // The last code point + lettersOut[1] = sectionName.substring(charOffset, charOffset + codePointSize); + boundsOut[0] = getAndCacheSectionBounds(lettersOut[1]); + } + charOffset += codePointSize; + } + } else { + lettersOut[0] = sectionName; + boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]); + } + } + + /** + * Given a section name, return the first and last section letters. + */ + private PointF getAndCacheSectionBounds(String sectionName) { + PointF bounds = mCachedSectionBounds.get(sectionName); if (bounds == null) { mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds); - bounds = new Point(mTmpBounds.width(), mTmpBounds.height()); + bounds = new PointF(mSectionTextPaint.measureText(sectionName), mTmpBounds.height()); mCachedSectionBounds.put(sectionName, bounds); } return bounds; } + /** + * Returns whether to draw the section for the given child. + */ private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex, List items) { // Ensure item is not already removed @@ -201,19 +245,12 @@ class AppsGridAdapter extends RecyclerView.Adapter { } // Ensure we have a holder position int pos = holder.getPosition(); - if (pos < 0 || pos >= items.size()) { - return false; - } - // Ensure this is not a section header - if (items.get(pos).isSectionHeader) { + if (pos <= 0 || pos >= items.size()) { return false; } - // Only draw the header for the first item in a section, or whenever the sub-sections - // changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but - // AppsContainerView.GRID_MERGE_SECTION_HEADERS is false) + // Draw the section header for the first item in each section return (childIndex == 0) || - items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader || - (!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName)); + (items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader); } } -- cgit v1.2.3 From b7e15adc7b67cb1d70ca72014b200dd6cbbf166a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 7 May 2015 14:51:31 -0700 Subject: Animating the folder title & page indicator when a multi-page folder is opened for the first time Change-Id: I70f5fd942724251a5e863fbb78a0c24f440b0283 --- res/layout/user_folder.xml | 21 ++++++----- src/com/android/launcher3/Folder.java | 49 ++++++++++++++++++++++++-- src/com/android/launcher3/FolderInfo.java | 5 +++ src/com/android/launcher3/FolderPagedView.java | 38 ++++++++++++++++---- 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index cd3a051ad..099da32e6 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -45,20 +45,16 @@ android:id="@+id/folder_footer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" > - - + android:orientation="horizontal" + android:paddingLeft="8dp" + android:paddingRight="8dp" > + + \ No newline at end of file diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index a2828054c..f2d7a6991 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -89,6 +89,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList */ private static final float ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f; + public static final int FOOTER_ANIMATION_DURATION = 200; + private static final int REORDER_DELAY = 250; private static final int ON_EXIT_CLOSE_DELAY = 400; private static final Rect sTempRect = new Rect(); @@ -201,10 +203,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); mFooter = findViewById(R.id.folder_footer); - updateFooterHeight(); - } - public void updateFooterHeight() { // We find out how tall footer wants to be (it is set to wrap_content), so that // we can allocate the appropriate amount of space for it. int measureSpec = MeasureSpec.UNSPECIFIED; @@ -529,6 +528,36 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setFocusOnFirstChild(); } }); + + // Footer animation + if (mContent.getPageCount() > 1 && !mInfo.hasOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION)) { + int footerWidth = mContent.getDesiredWidth() + - mFooter.getPaddingLeft() - mFooter.getPaddingRight(); + + float textWidth = mFolderName.getPaint().measureText(mFolderName.getText().toString()); + mFolderName.setTranslationX((footerWidth - textWidth) / 2); + mContent.setMarkerScale(0); + + // Do not update the flag if we are in drag mode. The flag will be updated, when we + // actually drop the icon. + final boolean updateAnimationFlag = !mDragInProgress; + openFolderAnim.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationEnd(Animator animation) { + mFolderName.animate().setDuration(FOOTER_ANIMATION_DURATION).translationX(0); + mContent.animateMarkers(); + + if (updateAnimationFlag) { + mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher); + } + } + }); + } else { + mFolderName.setTranslationX(0); + mContent.setMarkerScale(1); + } + openFolderAnim.start(); // Make sure the folder picks up the last drag move even if the finger doesn't move. @@ -805,6 +834,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Reordering may have occured, and we need to save the new item locations. We do this once // at the end to prevent unnecessary database operations. updateItemLocationsInDatabaseBatch(); + + // Use the item count to check for multi-page as the folder UI may not have + // been refreshed yet. + if (getItemCount() <= mContent.itemsPerPage()) { + // Show the animation, next time something is added to the folder. + mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher); + } + } @Override @@ -1183,6 +1220,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Clear the drag info, as it is no longer being dragged. mCurrentDragInfo = null; mDragInProgress = false; + + if (mContent.getPageCount() > 1) { + // The animation has already been shown while opening the folder. + mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher); + } } // This is used so the item doesn't immediately appear in the folder when added. In one case @@ -1197,6 +1239,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList v.setVisibility(VISIBLE); } + @Override public void onAdd(ShortcutInfo item) { // If the item was dropped onto this open folder, we have done the work associated // with adding the item to the folder, as indicated by mSuppressOnAdd being set diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 80b156413..aea21c95b 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -41,6 +41,11 @@ public class FolderInfo extends ItemInfo { */ public static final int FLAG_WORK_FOLDER = 0x00000002; + /** + * The multi-page animation has run for this folder + */ + public static final int FLAG_MULTI_PAGE_ANIMATION = 0x00000004; + /** * Whether this folder has been opened */ diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index a1c909a1f..f070a6bba 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -20,9 +20,11 @@ import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.animation.DecelerateInterpolator; +import android.view.animation.OvershootInterpolator; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; import com.android.launcher3.PageIndicator.PageMarkerResources; @@ -44,6 +46,8 @@ public class FolderPagedView extends PagedView { private static final int START_VIEW_REORDER_DELAY = 30; private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; + private static final int PAGE_INDICATOR_ANIMATION_DELAY = 150; + /** * Fraction of the width to scroll when showing the next page hint. */ @@ -73,7 +77,7 @@ public class FolderPagedView extends PagedView { private FocusIndicatorView mFocusIndicatorView; private PagedFolderKeyEventListener mKeyListener; - private View mPageIndicator; + private PageIndicator mPageIndicator; public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); @@ -101,7 +105,7 @@ public class FolderPagedView extends PagedView { mFolder = folder; mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); mKeyListener = new PagedFolderKeyEventListener(folder); - mPageIndicator = folder.findViewById(R.id.folder_page_indicator); + mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator); } /** @@ -338,11 +342,8 @@ public class FolderPagedView extends PagedView { setEnableOverscroll(getPageCount() > 1); // Update footer - int indicatorVisibility = mPageIndicator.getVisibility(); mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE); - if (indicatorVisibility != mPageIndicator.getVisibility()) { - mFolder.updateFooterHeight(); - } + mFolder.mFolderName.setGravity(getPageCount() > 1 ? Gravity.START : Gravity.CENTER_HORIZONTAL); } public int getDesiredWidth() { @@ -628,4 +629,29 @@ public class FolderPagedView extends PagedView { } } } + + public void setMarkerScale(float scale) { + int count = mPageIndicator.getChildCount(); + for (int i = 0; i < count; i++) { + View marker = mPageIndicator.getChildAt(i); + marker.animate().cancel(); + marker.setScaleX(scale); + marker.setScaleY(scale); + } + } + + public void animateMarkers() { + int count = mPageIndicator.getChildCount(); + OvershootInterpolator interpolator = new OvershootInterpolator(4); + for (int i = 0; i < count; i++) { + mPageIndicator.getChildAt(i).animate().scaleX(1).scaleY(1) + .setInterpolator(interpolator) + .setDuration(Folder.FOOTER_ANIMATION_DURATION) + .setStartDelay(PAGE_INDICATOR_ANIMATION_DELAY * i); + } + } + + public int itemsPerPage() { + return mMaxItemsPerPage; + } } -- cgit v1.2.3 From 9480415d9baf00ecfaac8ab3f608b16a1faa6518 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 8 May 2015 13:06:44 -0700 Subject: Make common base class to update container bounds and to handle scroll logic. Bug: 20763871 Change-Id: I8c2f45cfb10964e4be7e9c07c89dd336585c9989 --- res/values/dimens.xml | 5 +- .../launcher3/AppsContainerRecyclerView.java | 40 +---- src/com/android/launcher3/AppsContainerView.java | 173 +++++++++------------ .../launcher3/BaseContainerRecyclerView.java | 113 ++++++++++++++ src/com/android/launcher3/BaseContainerView.java | 100 ++++++++++++ src/com/android/launcher3/Launcher.java | 3 +- .../widget/WidgetsContainerRecyclerView.java | 58 +------ .../launcher3/widget/WidgetsContainerView.java | 24 +-- 8 files changed, 307 insertions(+), 209 deletions(-) create mode 100644 src/com/android/launcher3/BaseContainerRecyclerView.java create mode 100644 src/com/android/launcher3/BaseContainerView.java diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4fbe87e09..46830d6e5 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -50,8 +50,6 @@ 0dp 0dp 8dp - - 8dp 52dp 8dp 64dp @@ -62,6 +60,9 @@ 64dp 40dp + + 8dp + 48dp 0dp diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 7f64be2f5..fb5f6d436 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -30,23 +30,15 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; -import com.android.launcher3.util.Thunk; - import java.util.List; /** * A RecyclerView with custom fastscroll support. This is the main container for the all apps * icons. */ -public class AppsContainerRecyclerView extends RecyclerView - implements RecyclerView.OnItemTouchListener { +public class AppsContainerRecyclerView extends BaseContainerRecyclerView { private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - private static final int SCROLL_DELTA_THRESHOLD = 4; - - /** Keeps the last known scrolling delta/velocity along y-axis. */ - @Thunk int mDy = 0; - private float mDeltaThreshold; private AlphabeticalAppsList mApps; private int mNumAppsPerRow; @@ -66,7 +58,6 @@ public class AppsContainerRecyclerView extends RecyclerView private int mScrollbarWidth; private int mScrollbarMinHeight; private int mScrollbarInset; - private RecyclerView.OnScrollListener mScrollListenerProxy; public AppsContainerRecyclerView(Context context) { this(context, null); @@ -100,21 +91,6 @@ public class AppsContainerRecyclerView extends RecyclerView mScrollbarInset = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(getFastScrollerAlpha()); - mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; - - ScrollListener listener = new ScrollListener(); - setOnScrollListener(listener); - } - - private class ScrollListener extends RecyclerView.OnScrollListener { - public ScrollListener() { - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mDy = dy; - mScrollListenerProxy.onScrolled(recyclerView, dx, dy); - } } /** @@ -131,13 +107,6 @@ public class AppsContainerRecyclerView extends RecyclerView mNumAppsPerRow = rowSize; } - /** - * Sets an additional scroll listener, not necessary in master support lib. - */ - public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) { - mScrollListenerProxy = listener; - } - /** * Sets the fast scroller alpha. */ @@ -187,10 +156,6 @@ public class AppsContainerRecyclerView extends RecyclerView handleTouchEvent(ev); } - public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS - } - /** * Handles the touch event and determines whether to show the fast scroller (or updates it if * it is already showing). @@ -206,8 +171,7 @@ public class AppsContainerRecyclerView extends RecyclerView // Keep track of the down positions mDownX = mLastX = x; mDownY = mLastY = y; - if ((Math.abs(mDy) < mDeltaThreshold && - getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { + if (shouldStopScroll(ev)) { stopScroll(); } break; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 9122427fd..024e53939 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -31,7 +31,6 @@ import android.view.ViewConfiguration; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.util.Thunk; @@ -40,10 +39,10 @@ import java.util.List; /** - * The all apps list view container. + * The all apps view container. */ -public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher, - TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener, +public class AppsContainerView extends BaseContainerView implements DragSource, Insettable, + TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener, View.OnClickListener, View.OnLongClickListener { public static final boolean GRID_MERGE_SECTIONS = true; @@ -73,13 +72,9 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett private int mNumAppsPerRow; private Point mLastTouchDownPos = new Point(-1, -1); private Point mLastTouchPos = new Point(); - private Rect mInsets = new Rect(); - private Rect mFixedBounds = new Rect(); private int mContentMarginStart; // Normal container insets private int mContainerInset; - // Fixed bounds container insets - private int mFixedBoundsContainerInset; // RecyclerView scroll position @Thunk int mRecyclerViewScrollY; @@ -99,8 +94,6 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett mContainerInset = context.getResources().getDimensionPixelSize( R.dimen.apps_container_inset); - mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize( - R.dimen.apps_container_fixed_bounds_inset); mLauncher = (Launcher) context; mNumAppsPerRow = grid.appsViewNumCols; mApps = new AlphabeticalAppsList(context, mNumAppsPerRow); @@ -146,8 +139,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett */ public void hideHeaderBar() { mHeaderView.setVisibility(View.GONE); - updateBackgrounds(); - updatePaddings(); + onUpdateBackgrounds(); + onUpdatePaddings(); } /** @@ -225,46 +218,81 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett if (mItemDecoration != null) { mAppsRecyclerView.addItemDecoration(mItemDecoration); } - updateBackgrounds(); - updatePaddings(); + onUpdateBackgrounds(); + onUpdatePaddings(); } @Override - public void setInsets(Rect insets) { - mInsets.set(insets); - updatePaddings(); + protected void onFixedBoundsUpdated() { + // Update the number of items in the grid + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) { + mNumAppsPerRow = grid.appsViewNumCols; + mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow); + mAdapter.setNumAppsPerRow(mNumAppsPerRow); + mApps.setNumAppsPerRow(mNumAppsPerRow); + } } /** - * Sets the fixed bounds for this Apps view. + * Update the padding of the Apps view and children. To ensure that the RecyclerView has the + * full width to handle touches right to the edge of the screen, we only apply the top and + * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView + * itself. In particular, the left/right padding is applied to the background of the view, + * and then additionally inset by the start margin. */ - public void setFixedBounds(Context context, Rect fixedBounds) { - if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) { - // Update the number of items in the grid - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) { - mNumAppsPerRow = grid.appsViewNumCols; - mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow); - mAdapter.setNumAppsPerRow(mNumAppsPerRow); - mApps.setNumAppsPerRow(mNumAppsPerRow); - } + @Override + protected void onUpdatePaddings() { + boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == + LAYOUT_DIRECTION_RTL); + boolean hasSearchBar = (mSearchBarEditView != null) && + (mSearchBarEditView.getVisibility() == View.VISIBLE); - mFixedBounds.set(fixedBounds); - if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { - mFixedBounds.top = mInsets.top; - mFixedBounds.bottom = getMeasuredHeight(); - } + if (mFixedBounds.isEmpty()) { + // If there are no fixed bounds, then use the default padding and insets + setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right, + mContainerInset + mInsets.bottom); + } else { + // If there are fixed bounds, then we update the padding to reflect the fixed bounds. + setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, + mInsets.bottom); } - // Post the updates since they can trigger a relayout, and this call can be triggered from - // a layout pass itself. - post(new Runnable() { - @Override - public void run() { - updateBackgrounds(); - updatePaddings(); - } - }); + + // Update the apps recycler view, inset it by the container inset as well + int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + if (isRtl) { + mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset); + } else { + mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset); + } + + // Update the header bar + if (hasSearchBar) { + LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) mHeaderView.getLayoutParams(); + lp.leftMargin = lp.rightMargin = inset; + } + } + + /** + * Update the background of the Apps view and children. + */ + @Override + protected void onUpdateBackgrounds() { + int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + boolean hasSearchBar = (mSearchBarEditView != null) && + (mSearchBarEditView.getVisibility() == View.VISIBLE); + + // Update the background of the reveal view and list to be inset with the fixed bound + // insets instead of the default insets + mAppsRecyclerView.setBackground(new InsetDrawable( + getContext().getResources().getDrawable( + hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg), + inset, 0, inset, 0)); + getRevealView().setBackground(new InsetDrawable( + getContext().getResources().getDrawable(R.drawable.apps_reveal_bg), + inset, 0, inset, 0)); } @Override @@ -530,65 +558,6 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett return false; } - /** - * Update the padding of the Apps view and children. To ensure that the RecyclerView has the - * full width to handle touches right to the edge of the screen, we only apply the top and - * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView - * itself. In particular, the left/right padding is applied to the background of the view, - * and then additionally inset by the start margin. - */ - private void updatePaddings() { - boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == - LAYOUT_DIRECTION_RTL); - boolean hasSearchBar = (mSearchBarEditView != null) && - (mSearchBarEditView.getVisibility() == View.VISIBLE); - - if (mFixedBounds.isEmpty()) { - // If there are no fixed bounds, then use the default padding and insets - setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right, - mContainerInset + mInsets.bottom); - } else { - // If there are fixed bounds, then we update the padding to reflect the fixed bounds. - setPadding(mFixedBounds.left, mFixedBounds.top + mFixedBoundsContainerInset, - getMeasuredWidth() - mFixedBounds.right, - mInsets.bottom + mFixedBoundsContainerInset); - } - - // Update the apps recycler view - int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; - if (isRtl) { - mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset); - } else { - mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset); - } - - // Update the header - if (hasSearchBar) { - LinearLayout.LayoutParams lp = - (LinearLayout.LayoutParams) mHeaderView.getLayoutParams(); - lp.leftMargin = lp.rightMargin = inset; - } - } - - /** - * Update the background of the Apps view and children. - */ - private void updateBackgrounds() { - int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; - boolean hasSearchBar = (mSearchBarEditView != null) && - (mSearchBarEditView.getVisibility() == View.VISIBLE); - - // Update the background of the reveal view and list to be inset with the fixed bound - // insets instead of the default insets - mAppsRecyclerView.setBackground(new InsetDrawable( - getContext().getResources().getDrawable( - hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg), - inset, 0, inset, 0)); - getRevealView().setBackground(new InsetDrawable( - getContext().getResources().getDrawable(R.drawable.apps_reveal_bg), - inset, 0, inset, 0)); - } - /** * Shows the search field. */ diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java new file mode 100644 index 000000000..5b30e3df6 --- /dev/null +++ b/src/com/android/launcher3/BaseContainerRecyclerView.java @@ -0,0 +1,113 @@ +/* + * 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.Context; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.MotionEvent; +import com.android.launcher3.util.Thunk; + +/** + * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling + * velocity is below a predefined threshold. + */ +public class BaseContainerRecyclerView extends RecyclerView + implements RecyclerView.OnItemTouchListener { + + private static final int SCROLL_DELTA_THRESHOLD_DP = 4; + + /** Keeps the last known scrolling delta/velocity along y-axis. */ + @Thunk int mDy = 0; + private float mDeltaThreshold; + private RecyclerView.OnScrollListener mScrollListenerProxy; + + public BaseContainerRecyclerView(Context context) { + this(context, null); + } + + public BaseContainerRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BaseContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP; + + ScrollListener listener = new ScrollListener(); + setOnScrollListener(listener); + } + + private class ScrollListener extends OnScrollListener { + public ScrollListener() { + // Do nothing + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mDy = dy; + if (mScrollListenerProxy != null) { + mScrollListenerProxy.onScrolled(recyclerView, dx, dy); + } + } + } + + /** + * Sets an additional scroll listener, only needed for LMR1 version of the support lib. + */ + public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) { + mScrollListenerProxy = listener; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + addOnItemTouchListener(this); + } + + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { + if (shouldStopScroll(ev)) { + stopScroll(); + } + return false; + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent ev) { + // Do nothing. + } + + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS + } + + /** + * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. + */ + protected boolean shouldStopScroll(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if ((Math.abs(mDy) < mDeltaThreshold && + getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { + // now the touch events are being passed to the {@link WidgetCell} until the + // touch sequence goes over the touch slop. + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java new file mode 100644 index 000000000..2a8443221 --- /dev/null +++ b/src/com/android/launcher3/BaseContainerView.java @@ -0,0 +1,100 @@ +/* + * 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.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * A base container view, which supports resizing. + */ +public class BaseContainerView extends FrameLayout implements Insettable { + + protected Rect mInsets = new Rect(); + protected Rect mFixedBounds = new Rect(); + protected int mFixedBoundsContainerInset; + + public BaseContainerView(Context context) { + this(context, null); + } + + public BaseContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize( + R.dimen.container_fixed_bounds_inset); + } + + @Override + final public void setInsets(Rect insets) { + mInsets.set(insets); + onUpdateBackgrounds(); + onUpdatePaddings(); + } + + /** + * Sets the fixed bounds for this container view. + */ + final public void setFixedBounds(Rect fixedBounds) { + if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) { + mFixedBounds.set(fixedBounds); + if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + mFixedBounds.top = mInsets.top; + mFixedBounds.bottom = getMeasuredHeight(); + } + // To ensure that the child RecyclerView has the full width to handle touches right to + // the edge of the screen, we only apply the top and bottom padding to the bounds + mFixedBounds.inset(0, mFixedBoundsContainerInset); + onFixedBoundsUpdated(); + } + // Post the updates since they can trigger a relayout, and this call can be triggered from + // a layout pass itself. + post(new Runnable() { + @Override + public void run() { + onUpdateBackgrounds(); + onUpdatePaddings(); + } + }); + } + + /** + * Update the UI in response to a change in the fixed bounds. + */ + protected void onFixedBoundsUpdated() { + // Do nothing + } + + /** + * Update the paddings in response to a change in the bounds or insets. + */ + protected void onUpdatePaddings() { + // Do nothing + } + + /** + * Update the backgrounds in response to a change in the bounds or insets. + */ + protected void onUpdateBackgrounds() { + // Do nothing + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1de383c65..564575904 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -528,7 +528,8 @@ public class Launcher extends Activity if (LOGD) { Log.d(TAG, "onAllAppsBoundsChanged(Rect): " + bounds); } - mAppsView.setFixedBounds(Launcher.this, bounds); + mAppsView.setFixedBounds(bounds); + mWidgetsView.setFixedBounds(bounds); } @Override diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java index 65694bfaa..6f15324c1 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java @@ -17,26 +17,13 @@ package com.android.launcher3.widget; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; -import android.view.MotionEvent; - -import com.android.launcher3.util.Thunk; +import com.android.launcher3.BaseContainerRecyclerView; /** * The widgets recycler view container. - *

- * Overwritten to NOT intercept a touch sequence that started when the {@link RecycleView} - * scrolling slowing down below the internally defined threshold. */ -public class WidgetsContainerRecyclerView extends RecyclerView - implements RecyclerView.OnItemTouchListener { - - private static final int SCROLL_DELTA_THRESHOLD = 4; - - /** Keeps the last known scrolling delta/velocity along y-axis. */ - @Thunk int mDy = 0; - private float mDeltaThreshold; +public class WidgetsContainerRecyclerView extends BaseContainerRecyclerView { public WidgetsContainerRecyclerView(Context context) { this(context, null); @@ -48,47 +35,6 @@ public class WidgetsContainerRecyclerView extends RecyclerView public WidgetsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD; - - ScrollListener listener = new ScrollListener(); - setOnScrollListener(listener); - } - - private class ScrollListener extends RecyclerView.OnScrollListener { - public ScrollListener() { - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mDy = dy; - } } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - addOnItemTouchListener(this); - } - - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if ((Math.abs(mDy) < mDeltaThreshold && - getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { - // now the touch events are being passed to the {@link WidgetCell} until the - // touch sequence goes over the touch slop. - stopScroll(); - } - } - return false; - } - - @Override - public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - // Do nothing. - } - - public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS - } } \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 22e29f304..439227f52 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -28,10 +28,9 @@ import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; import android.util.Log; import android.view.View; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Toast; - +import com.android.launcher3.BaseContainerView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DragController; @@ -55,8 +54,8 @@ import java.util.ArrayList; /** * The widgets list view container. */ -public class WidgetsContainerView extends FrameLayout implements Insettable, - View.OnLongClickListener, View.OnClickListener, DragSource{ +public class WidgetsContainerView extends BaseContainerView + implements View.OnLongClickListener, View.OnClickListener, DragSource{ private static final String TAG = "WidgetsContainerView"; private static final boolean DEBUG = false; @@ -129,6 +128,7 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, }); mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); + onUpdatePaddings(); } // @@ -364,13 +364,17 @@ public class WidgetsContainerView extends FrameLayout implements Insettable, // Container rendering related. // - /* - * @see Insettable#setInsets(Rect) - */ @Override - public void setInsets(Rect insets) { - setPadding(mPadding.left + insets.left, mPadding.top + insets.top, - mPadding.right + insets.right, mPadding.bottom + insets.bottom); + protected void onUpdatePaddings() { + if (mFixedBounds.isEmpty()) { + // If there are no fixed bounds, then use the default padding and insets + setPadding(mPadding.left + mInsets.left, mPadding.top + mInsets.top, + mPadding.right + mInsets.right, mPadding.bottom + mInsets.bottom); + } else { + // If there are fixed bounds, then we update the padding to reflect the fixed bounds. + setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, + mInsets.bottom); + } } /** -- cgit v1.2.3 From 99d96ba6c8e3258f7d99a33d49da2aeb0da5d862 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 8 May 2015 12:04:45 -0700 Subject: Fixing issue with fast scroller not showing sub-section headers. - Removed some old logic to draw merged section headers Change-Id: I2a7ff9948a3dce253d6bdcda89cc9f222faab309 --- .../android/launcher3/AlphabeticalAppsList.java | 112 ++++++++++++--------- .../launcher3/AppsContainerRecyclerView.java | 44 ++++---- src/com/android/launcher3/AppsContainerView.java | 1 - src/com/android/launcher3/AppsGridAdapter.java | 69 +++---------- 4 files changed, 99 insertions(+), 127 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 824ccde64..70e36a744 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -82,17 +82,30 @@ public class AlphabeticalAppsList { * Info about a section in the alphabetic list */ public static class SectionInfo { - // The name of this section - public String sectionName; // The number of applications in this section - public int numAppsInSection; - // The section AdapterItem for this section - public AdapterItem sectionItem; + public int numApps; + // The section break AdapterItem for this section + public AdapterItem sectionBreakItem; // The first app AdapterItem for this section public AdapterItem firstAppItem; + } - public SectionInfo(String name) { - sectionName = name; + /** + * Info about a fast scroller section, depending if sections are merged, the fast scroller + * sections will not be the same set as the section headers. + */ + public static class FastScrollSectionInfo { + // The section name + public String sectionName; + // To map the touch (from 0..1) to the index in the app list to jump to in the fast + // scroller, we use the fraction in range (0..1) of the app index / total app count. + public float appRangeFraction; + // The AdapterItem to scroll to for this section + public AdapterItem appItem; + + public FastScrollSectionInfo(String sectionName, float appRangeFraction) { + this.sectionName = sectionName; + this.appRangeFraction = appRangeFraction; } } @@ -100,31 +113,30 @@ public class AlphabeticalAppsList { * Info about a particular adapter item (can be either section or app) */ public static class AdapterItem { + /** Section & App properties */ // The index of this adapter item in the list public int position; // Whether or not the item at this adapter position is a section or not public boolean isSectionHeader; - // The name of this section, or the section section name of the app. Note that if this - // app was merged into another section, then this may be a different name than the - // sectionInfo's sectionName - public String sectionName; - // The section to which this app belongs + // The section for this item public SectionInfo sectionInfo; + + /** App-only properties */ + // The section name of this app. Note that there can be multiple items with different + // sectionNames in the same section + public String sectionName = null; // The index of this app in the section - public int sectionAppIndex; - // The associated AppInfo, or null if this adapter item is a section - public AppInfo appInfo; - // The index of this app (not including sections), or -1 if this adapter item is a section - public int appIndex; + public int sectionAppIndex = -1; + // The associated AppInfo for the app + public AppInfo appInfo = null; + // The index of this app not including sections + public int appIndex = -1; - public static AdapterItem asSection(int pos, SectionInfo section) { + public static AdapterItem asSectionBreak(int pos, SectionInfo section) { AdapterItem item = new AdapterItem(); item.position = pos; item.isSectionHeader = true; item.sectionInfo = section; - item.sectionName = section.sectionName; - item.appInfo = null; - item.appIndex = -1; return item; } @@ -156,6 +168,7 @@ public class AlphabeticalAppsList { private List mFilteredApps = new ArrayList<>(); private List mSectionedFilteredApps = new ArrayList<>(); private List mSections = new ArrayList<>(); + private List mFastScrollerSections = new ArrayList<>(); private RecyclerView.Adapter mAdapter; private Filter mFilter; private AlphabeticIndexCompat mIndexer; @@ -193,6 +206,13 @@ public class AlphabeticalAppsList { return mSections; } + /** + * Returns fast scroller sections of all the current filtered applications. + */ + public List getFastScrollerSections() { + return mFastScrollerSections; + } + /** * Returns the current filtered list of applications broken down into their sections. */ @@ -321,10 +341,13 @@ public class AlphabeticalAppsList { mFilteredApps.clear(); mSections.clear(); mSectionedFilteredApps.clear(); + mFastScrollerSections.clear(); SectionInfo lastSectionInfo = null; + String lastSectionName = null; + FastScrollSectionInfo lastFastScrollerSectionInfo = null; int position = 0; int appIndex = 0; - int sectionAppIndex = 0; + int numApps = mApps.size(); for (AppInfo info : mApps) { String sectionName = mIndexer.computeSectionName(info.title.toString().trim()); @@ -334,26 +357,29 @@ public class AlphabeticalAppsList { } // Create a new section if necessary - if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) { - lastSectionInfo = new SectionInfo(sectionName); - sectionAppIndex = 0; + if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { + lastSectionName = sectionName; + lastSectionInfo = new SectionInfo(); mSections.add(lastSectionInfo); + lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, + (float) appIndex / numApps); + mFastScrollerSections.add(lastFastScrollerSectionInfo); // Create a new section item, this item is used to break the flow of items in the // list - AdapterItem sectionItem = AdapterItem.asSection(position++, lastSectionInfo); + AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { - lastSectionInfo.sectionItem = sectionItem; + lastSectionInfo.sectionBreakItem = sectionItem; mSectionedFilteredApps.add(sectionItem); } } // Create an app item AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, - sectionAppIndex++, info, appIndex++); - lastSectionInfo.numAppsInSection++; + lastSectionInfo.numApps++, info, appIndex++); if (lastSectionInfo.firstAppItem == null) { lastSectionInfo.firstAppItem = appItem; + lastFastScrollerSectionInfo.appItem = appItem; } mSectionedFilteredApps.add(appItem); mFilteredApps.add(info); @@ -365,9 +391,9 @@ public class AlphabeticalAppsList { int sectionAppCount = 0; for (int i = 0; i < mSections.size(); i++) { SectionInfo section = mSections.get(i); - String mergedSectionName = section.sectionName; - sectionAppCount = section.numAppsInSection; + sectionAppCount = section.numApps; int mergeCount = 1; + // Merge rows if the last app in this section is in a column that is greater than // 0, but less than the min number of apps per row. In addition, apply the // constraint to stop merging if the number of rows in the section is greater than @@ -378,35 +404,25 @@ public class AlphabeticalAppsList { (i + 1) < mSections.size()) { SectionInfo nextSection = mSections.remove(i + 1); - // Merge the section names - if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { - mergedSectionName += nextSection.sectionName; - } // Remove the next section break - mSectionedFilteredApps.remove(nextSection.sectionItem); + mSectionedFilteredApps.remove(nextSection.sectionBreakItem); int pos = mSectionedFilteredApps.indexOf(section.firstAppItem); - if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { - // Update the section names for the two sections - for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { - AdapterItem item = mSectionedFilteredApps.get(j); - item.sectionName = mergedSectionName; - item.sectionInfo = section; - } - } // Point the section for these new apps to the merged section - for (int j = pos + section.numAppsInSection; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) { + int nextPos = pos + section.numApps; + for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) { AdapterItem item = mSectionedFilteredApps.get(j); item.sectionInfo = section; - item.sectionAppIndex += section.numAppsInSection; + item.sectionAppIndex += section.numApps; } + // Update the following adapter items of the removed section item pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem); for (int j = pos; j < mSectionedFilteredApps.size(); j++) { AdapterItem item = mSectionedFilteredApps.get(j); item.position--; } - section.numAppsInSection += nextSection.numAppsInSection; - sectionAppCount += nextSection.numAppsInSection; + section.numApps += nextSection.numApps; + sectionAppCount += nextSection.numApps; mergeCount++; if (mergeCount >= mMaxAllowableMerges) { break; diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index fb5f6d436..6556cf920 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -254,8 +254,9 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, mFastScrollSectionName.length(), mFastScrollTextBounds); + float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); canvas.drawText(mFastScrollSectionName, - (bgBounds.width() - mFastScrollTextBounds.width()) / 2, + (bgBounds.width() - textWidth) / 2, bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2, mFastScrollTextPaint); canvas.restoreToCount(restoreCount); @@ -285,38 +286,33 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } /** - * Maps the progress (from 0..1) to the position that should be visible + * Maps the touch (from 0..1) to the adapter position that should be visible. */ - private String scrollToPositionAtProgress(float progress) { - List sections = mApps.getSections(); - if (sections.isEmpty()) { + private String scrollToPositionAtProgress(float touchFraction) { + // Ensure that we have any sections + List fastScrollSections = + mApps.getFastScrollerSections(); + if (fastScrollSections.isEmpty()) { return ""; } - // Find the position of the first application in the section that contains the row at the - // current progress - List items = mApps.getAdapterItems(); - int rowAtProgress = (int) (progress * getNumRows()); - int rowCount = 0; - AlphabeticalAppsList.SectionInfo lastSectionInfo = null; - for (AlphabeticalAppsList.SectionInfo section : sections) { - int numRowsInSection = (int) Math.ceil((float) section.numAppsInSection / mNumAppsPerRow); - if (rowCount + numRowsInSection >= rowAtProgress) { - lastSectionInfo = section; + AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0); + for (int i = 1; i < fastScrollSections.size(); i++) { + AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i); + if (lastScrollSection.appRangeFraction <= touchFraction && + touchFraction < scrollSection.appRangeFraction) { break; } - rowCount += numRowsInSection; + lastScrollSection = scrollSection; } - int position = items.indexOf(lastSectionInfo.firstAppItem); // Scroll the position into view, anchored at the top of the screen if possible. We call the // scroll method on the LayoutManager directly since it is not exposed by RecyclerView. LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); stopScroll(); - layoutManager.scrollToPositionWithOffset(position, 0); + layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); - // Return the section name of the row - return lastSectionInfo.sectionName; + return lastScrollSection.sectionName; } /** @@ -392,11 +388,11 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { int appIndex = 0; int rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); - if (appIndex + info.numAppsInSection > position) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + if (appIndex + info.numApps > position) { return rowCount + ((position - appIndex) / mNumAppsPerRow); } - appIndex += info.numAppsInSection; + appIndex += info.numApps; rowCount += numRowsInSection; } return appIndex; @@ -409,7 +405,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { List sections = mApps.getSections(); int rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow); + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); rowCount += numRowsInSection; } return rowCount; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 9c05d0d10..993f9c857 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -46,7 +46,6 @@ public class AppsContainerView extends BaseContainerView implements DragSource, View.OnClickListener, View.OnLongClickListener { public static final boolean GRID_MERGE_SECTIONS = true; - public static final boolean GRID_MERGE_SECTION_HEADERS = false; public static final boolean GRID_HIDE_SECTION_HEADERS = false; private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index d83d6c97c..259740c60 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.support.v7.widget.GridLayoutManager; @@ -86,8 +85,6 @@ class AppsGridAdapter extends RecyclerView.Adapter { private HashMap mCachedSectionBounds = new HashMap<>(); private Rect mTmpBounds = new Rect(); - private String[] mTmpSections = new String[2]; - private PointF[] mTmpSectionBounds = new PointF[2]; @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { @@ -111,26 +108,24 @@ class AppsGridAdapter extends RecyclerView.Adapter { // Draw all the sections for this index String lastSectionName = item.sectionName; - for (int j = item.sectionAppIndex; j < sectionInfo.numAppsInSection;j++, pos++) { + for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) { AlphabeticalAppsList.AdapterItem nextItem = items.get(pos); + String sectionName = nextItem.sectionName; if (nextItem.sectionInfo != sectionInfo) { break; } - if (j > item.sectionAppIndex && nextItem.sectionName.equals(lastSectionName)) { + if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) { continue; } // Find the section code points - getSectionLetters(nextItem.sectionName, mTmpSections, mTmpSectionBounds); - String sectionBegin = mTmpSections[0]; - String sectionEnd = mTmpSections[1]; - PointF sectionBeginBounds = mTmpSectionBounds[0]; - PointF sectionEndBounds = mTmpSectionBounds[1]; + PointF sectionBounds = getAndCacheSectionBounds(sectionName); // Calculate where to draw the section - int sectionBaseline = (int) (viewTopOffset + sectionBeginBounds.y); + int sectionBaseline = (int) (viewTopOffset + sectionBounds.y); int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : mPaddingStart; + x += (int) ((mStartMargin - sectionBounds.x) / 2f); int y = child.getTop() + sectionBaseline; // Determine whether this is the last row with apps in that section, if @@ -139,7 +134,8 @@ class AppsGridAdapter extends RecyclerView.Adapter { int appIndexInSection = items.get(pos).sectionAppIndex; int nextRowPos = Math.min(items.size() - 1, pos + mAppsPerRow - (appIndexInSection % mAppsPerRow)); - boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(nextItem.sectionName); + AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos); + boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName); if (!fixedToRow) { y = Math.max(sectionBaseline, y); } @@ -154,25 +150,18 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (FADE_OUT_SECTIONS) { int alpha = 255; if (fixedToRow) { - alpha = Math.min(255, (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); + alpha = Math.min(255, + (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); } mSectionTextPaint.setAlpha(alpha); } - if (sectionEnd != null) { - // If there is a range, draw the range - c.drawText(sectionBegin + "/" + sectionEnd, - x + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, y, - mSectionTextPaint); - } else { - c.drawText(sectionBegin, (int) (x + (mStartMargin / 2f) - (sectionBeginBounds.x / 2f)), y, - mSectionTextPaint); - } + c.drawText(sectionName, x, y, mSectionTextPaint); lastSectionTop = y; - lastSectionHeight = (int) (sectionBeginBounds.y + mSectionHeaderOffset); - lastSectionName = nextItem.sectionName; + lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset); + lastSectionName = sectionName; } - i += (sectionInfo.numAppsInSection - item.sectionAppIndex); + i += (sectionInfo.numApps - item.sectionAppIndex); } } } @@ -184,35 +173,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { } /** - * Given a section name, return the first and last section letters. - */ - private void getSectionLetters(String sectionName, String[] lettersOut, PointF[] boundsOut) { - lettersOut[0] = lettersOut[1] = null; - boundsOut[0] = boundsOut[1] = null; - if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) { - int charOffset = 0; - while (charOffset < sectionName.length()) { - int codePoint = sectionName.codePointAt(charOffset); - int codePointSize = Character.charCount(codePoint); - if (charOffset == 0) { - // The first code point - lettersOut[0] = sectionName.substring(charOffset, charOffset + codePointSize); - boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]); - } else if ((charOffset + codePointSize) >= sectionName.length()) { - // The last code point - lettersOut[1] = sectionName.substring(charOffset, charOffset + codePointSize); - boundsOut[0] = getAndCacheSectionBounds(lettersOut[1]); - } - charOffset += codePointSize; - } - } else { - lettersOut[0] = sectionName; - boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]); - } - } - - /** - * Given a section name, return the first and last section letters. + * Given a section name, return the bounds of the given section name. */ private PointF getAndCacheSectionBounds(String sectionName) { PointF bounds = mCachedSectionBounds.get(sectionName); -- cgit v1.2.3 From 82b016cb56540fe26213e817dd0dd668099c8e20 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 8 May 2015 17:00:10 -0700 Subject: Trim all whitespace from titles and labels. Bug: 20953160 Change-Id: I1610df5e445a4139522226f68fa6439926bc70c6 --- .../android/launcher3/AlphabeticalAppsList.java | 5 ++-- src/com/android/launcher3/AppInfo.java | 4 +-- src/com/android/launcher3/AppsContainerView.java | 18 +++++++----- src/com/android/launcher3/Folder.java | 5 ++-- src/com/android/launcher3/FolderIcon.java | 2 +- src/com/android/launcher3/FolderInfo.java | 2 +- src/com/android/launcher3/IconCache.java | 10 +++---- .../android/launcher3/InstallShortcutReceiver.java | 2 +- .../launcher3/LauncherAppWidgetProviderInfo.java | 2 +- src/com/android/launcher3/LauncherModel.java | 33 ++++++++++------------ src/com/android/launcher3/ShortcutInfo.java | 8 +++--- src/com/android/launcher3/Utilities.java | 15 ++++++++++ .../WorkspaceAccessibilityHelper.java | 2 +- .../launcher3/compat/AlphabeticIndexCompat.java | 8 ++++-- .../compat/AppWidgetManagerCompatV16.java | 2 +- .../android/launcher3/widget/PackageItemInfo.java | 2 +- src/com/android/launcher3/widget/WidgetCell.java | 7 ++--- 17 files changed, 72 insertions(+), 55 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 70e36a744..f075e417d 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -29,8 +29,7 @@ class AppNameComparator { mAppNameComparator = new Comparator() { public final int compare(AppInfo a, AppInfo b) { // Order by the title - int result = collator.compare(a.title.toString().trim(), - b.title.toString().trim()); + int result = collator.compare(a.title.toString(), b.title.toString()); if (result == 0) { // If two apps have the same title, then order by the component name result = a.componentName.compareTo(b.componentName); @@ -349,7 +348,7 @@ public class AlphabeticalAppsList { int appIndex = 0; int numApps = mApps.size(); for (AppInfo info : mApps) { - String sectionName = mIndexer.computeSectionName(info.title.toString().trim()); + String sectionName = mIndexer.computeSectionName(info.title); // Check if we want to retain this app if (mFilter != null && !mFilter.retainApp(info, sectionName)) { diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 7c6b0664c..58a57a1fe 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -105,7 +105,7 @@ public class AppInfo extends ItemInfo { public AppInfo(AppInfo info) { super(info); componentName = info.componentName; - title = info.title.toString(); + title = Utilities.trim(info.title); intent = new Intent(info.intent); flags = info.flags; firstInstallTime = info.firstInstallTime; @@ -114,7 +114,7 @@ public class AppInfo extends ItemInfo { @Override public String toString() { - return "ApplicationInfo(title=" + title.toString() + " id=" + this.id + return "ApplicationInfo(title=" + title + " id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 993f9c857..aa6c05993 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -36,6 +36,7 @@ import android.widget.TextView; import com.android.launcher3.util.Thunk; import java.util.List; +import java.util.regex.Pattern; /** @@ -56,6 +57,8 @@ public class AppsContainerView extends BaseContainerView implements DragSource, private static final int FADE_OUT_DURATION = 100; private static final int SEARCH_TRANSLATION_X_DP = 18; + private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); + @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; private AppsGridAdapter mAdapter; @@ -430,23 +433,24 @@ public class AppsContainerView extends BaseContainerView implements DragSource, @Override public void afterTextChanged(final Editable s) { - if (s.toString().isEmpty()) { + String queryText = s.toString(); + if (queryText.isEmpty()) { mApps.setFilter(null); } else { String formatStr = getResources().getString(R.string.apps_view_no_search_results); - mAdapter.setEmptySearchText(String.format(formatStr, s.toString())); + mAdapter.setEmptySearchText(String.format(formatStr, queryText)); - final String filterText = s.toString().toLowerCase().replaceAll("\\s+", ""); + final String queryTextLower = queryText.toLowerCase(); mApps.setFilter(new AlphabeticalAppsList.Filter() { @Override public boolean retainApp(AppInfo info, String sectionName) { - String title = info.title.toString(); - if (sectionName.toLowerCase().contains(filterText)) { + if (sectionName.toLowerCase().contains(queryTextLower)) { return true; } - String[] words = title.toLowerCase().split("\\s+"); + String title = info.title.toString(); + String[] words = SPLIT_PATTERN.split(title.toLowerCase()); for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(filterText)) { + if (words[i].startsWith(queryTextLower)) { return true; } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index a2828054c..7a3ae3995 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -296,13 +296,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setHint(sHintText); // Convert to a string here to ensure that no other state associated with the text field // gets saved. - String newTitle = mFolderName.getText().toString(); + CharSequence newTitle = mFolderName.getText(); mInfo.setTitle(newTitle); LauncherModel.updateItemInDatabase(mLauncher, mInfo); if (commit) { sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - String.format(getContext().getString(R.string.folder_renamed), newTitle)); + String.format(getContext().getString(R.string.folder_renamed), + newTitle.toString())); } // In order to clear the focus from the text field, we set the focus on ourself. This // ensures that every time the field is clicked, focus is gained, giving reliable behavior. diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index f5836c295..b161b1cd3 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -710,7 +710,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } public void onTitleChanged(CharSequence title) { - mFolderName.setText(title.toString()); + mFolderName.setText(title); setContentDescription(String.format(getContext().getString(R.string.folder_name_format), title)); } diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 80b156413..9675371d5 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -87,7 +87,7 @@ public class FolderInfo extends ItemInfo { } public void setTitle(CharSequence title) { - this.title = title; + this.title = Utilities.trim(title); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onTitleChanged(title); } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 6c2aa397d..0596fbe16 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -400,7 +400,7 @@ public class IconCache { UserHandleCompat user = info == null ? application.user : info.getUser(); CacheEntry entry = cacheLocked(application.componentName, info, user, false, useLowResIcon); - application.title = entry.title; + application.title = Utilities.trim(entry.title); application.iconBitmap = getNonNullIcon(entry, user); application.contentDescription = entry.contentDescription; application.usingLowResIcon = entry.isLowResIcon; @@ -413,7 +413,7 @@ public class IconCache { CacheEntry entry = cacheLocked(application.componentName, null, application.user, false, application.usingLowResIcon); if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) { - application.title = entry.title; + application.title = Utilities.trim(entry.title); application.iconBitmap = entry.icon; application.contentDescription = entry.contentDescription; application.usingLowResIcon = entry.isLowResIcon; @@ -464,7 +464,7 @@ public class IconCache { UserHandleCompat user, boolean usePkgIcon, boolean useLowResIcon) { CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon); shortcutInfo.setIcon(getNonNullIcon(entry, user)); - shortcutInfo.title = entry.title; + shortcutInfo.title = Utilities.trim(entry.title); shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); shortcutInfo.usingLowResIcon = entry.isLowResIcon; } @@ -477,7 +477,7 @@ public class IconCache { PackageItemInfo infoOut) { CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon); infoOut.iconBitmap = getNonNullIcon(entry, user); - infoOut.title = entry.title; + infoOut.title = Utilities.trim(entry.title); infoOut.usingLowResIcon = entry.isLowResIcon; infoOut.contentDescription = entry.contentDescription; } @@ -530,7 +530,7 @@ public class IconCache { } if (TextUtils.isEmpty(entry.title) && info != null) { - entry.title = info.getLabel().toString(); + entry.title = info.getLabel(); entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); } } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 23bcc8577..115598f7b 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -247,7 +247,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { try { PackageManager pm = context.getPackageManager(); ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0); - name = info.loadLabel(pm).toString(); + name = info.loadLabel(pm); } catch (PackageManager.NameNotFoundException nnfe) { return ""; } diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index bb4580ce7..af680f247 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -66,7 +66,7 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { public String getLabel(PackageManager packageManager) { if (isCustomWidget) { - return label.toString().trim(); + return Utilities.trim(label); } return super.loadLabel(packageManager); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index e81c8c285..3987c0207 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -969,7 +969,7 @@ public class LauncherModel extends BroadcastReceiver break; } - folderInfo.title = c.getString(titleIndex); + folderInfo.title = Utilities.trim(c.getString(titleIndex)); folderInfo.id = id; folderInfo.container = c.getInt(containerIndex); folderInfo.screenId = c.getInt(screenIndex); @@ -2144,7 +2144,7 @@ public class LauncherModel extends BroadcastReceiver id = c.getLong(idIndex); FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id); - folderInfo.title = c.getString(titleIndex); + folderInfo.title = Utilities.trim(c.getString(titleIndex)); folderInfo.id = id; container = c.getInt(containerIndex); folderInfo.container = container; @@ -3199,7 +3199,7 @@ public class LauncherModel extends BroadcastReceiver if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction()) && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { si.updateIcon(mIconCache); - si.title = appInfo.title.toString(); + si.title = Utilities.trim(appInfo.title); si.contentDescription = appInfo.contentDescription; infoUpdated = true; } @@ -3428,18 +3428,17 @@ public class LauncherModel extends BroadcastReceiver if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) { String title = (cursor != null) ? cursor.getString(titleIndex) : null; if (!TextUtils.isEmpty(title)) { - info.title = title; + info.title = Utilities.trim(title); } } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) { if (TextUtils.isEmpty(info.title)) { - info.title = (cursor != null) ? cursor.getString(titleIndex) : ""; + info.title = (cursor != null) ? Utilities.trim(cursor.getString(titleIndex)) : ""; } } else { throw new InvalidParameterException("Invalid restoreType " + promiseType); } - info.contentDescription = mUserManager.getBadgedLabelForUser( - info.title.toString(), info.user); + info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; info.promisedIntent = intent; info.status = promiseType; @@ -3501,7 +3500,7 @@ public class LauncherModel extends BroadcastReceiver // from the db if (TextUtils.isEmpty(info.title) && c != null) { - info.title = c.getString(titleIndex); + info.title = Utilities.trim(c.getString(titleIndex)); } // fall back to the class name of the activity @@ -3511,8 +3510,7 @@ public class LauncherModel extends BroadcastReceiver info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; info.user = user; - info.contentDescription = mUserManager.getBadgedLabelForUser( - info.title.toString(), info.user); + info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user); if (lai != null) { info.flags = AppInfo.initFlags(lai); } @@ -3578,7 +3576,7 @@ public class LauncherModel extends BroadcastReceiver // TODO: If there's an explicit component and we can't install that, delete it. - info.title = c.getString(titleIndex); + info.title = Utilities.trim(c.getString(titleIndex)); int iconType = c.getInt(iconTypeIndex); switch (iconType) { @@ -3656,9 +3654,8 @@ public class LauncherModel extends BroadcastReceiver } info.setIcon(icon); - info.title = name; - info.contentDescription = mUserManager.getBadgedLabelForUser( - info.title.toString(), info.user); + info.title = Utilities.trim(name); + info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user); info.intent = intent; info.customIcon = customIcon; info.iconResource = iconResource; @@ -3699,16 +3696,16 @@ public class LauncherModel extends BroadcastReceiver labelA = mLabelCache.get(a); } else { labelA = (a instanceof LauncherAppWidgetProviderInfo) - ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a) - : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim(); + ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a)) + : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager)); mLabelCache.put(a, labelA); } if (mLabelCache.containsKey(b)) { labelB = mLabelCache.get(b); } else { labelB = (b instanceof LauncherAppWidgetProviderInfo) - ? mManager.loadLabel((LauncherAppWidgetProviderInfo) b) - : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim(); + ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b)) + : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager)); mLabelCache.put(b, labelB); } return mCollator.compare(labelA, labelB); diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 6354fcd28..8be48721c 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -153,7 +153,7 @@ public class ShortcutInfo extends ItemInfo { Bitmap icon, UserHandleCompat user) { this(); this.intent = intent; - this.title = title; + this.title = Utilities.trim(title); this.contentDescription = contentDescription; mIcon = icon; this.user = user; @@ -161,7 +161,7 @@ public class ShortcutInfo extends ItemInfo { public ShortcutInfo(Context context, ShortcutInfo info) { super(info); - title = info.title.toString(); + title = Utilities.trim(info.title); intent = new Intent(info.intent); if (info.iconResource != null) { iconResource = new Intent.ShortcutIconResource(); @@ -179,7 +179,7 @@ public class ShortcutInfo extends ItemInfo { /** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */ public ShortcutInfo(AppInfo info) { super(info); - title = info.title.toString(); + title = Utilities.trim(info.title); intent = new Intent(info.intent); customIcon = false; flags = info.flags; @@ -281,7 +281,7 @@ public class ShortcutInfo extends ItemInfo { public static ShortcutInfo fromActivityInfo(LauncherActivityInfoCompat info, Context context) { final ShortcutInfo shortcut = new ShortcutInfo(); shortcut.user = info.getUser(); - shortcut.title = info.getLabel().toString(); + shortcut.title = Utilities.trim(info.getLabel()); shortcut.contentDescription = UserManagerCompat.getInstance(context) .getBadgedLabelForUser(info.getLabel(), info.getUser()); shortcut.customIcon = false; diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 2dbf078a4..298174768 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -54,6 +54,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Various utilities shared amongst the Launcher's classes. @@ -67,6 +69,9 @@ public final class Utilities { private static final Rect sOldBounds = new Rect(); private static final Canvas sCanvas = new Canvas(); + private static final Pattern sTrimPattern = + Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$"); + static { sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG)); @@ -616,4 +621,14 @@ public final class Utilities { return false; } + + /** + * Trims the string, removing all whitespace at the beginning and end of the string. + * Non-breaking whitespaces are also removed. + */ + public static String trim(CharSequence s) { + // Just strip any sequence of whitespace or java space characters from the beginning and end + Matcher m = sTrimPattern.matcher(s); + return m.replaceAll("$1"); + } } diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java index 42e9e3c58..6f89d0eb0 100644 --- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java +++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java @@ -145,7 +145,7 @@ public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelega if (info instanceof ShortcutInfo) { return mContext.getString(R.string.create_folder_with, info.title); } else if (info instanceof FolderInfo) { - if (TextUtils.isEmpty(info.title.toString().trim())) { + if (TextUtils.isEmpty(info.title)) { // Find the first item in the folder. FolderInfo folder = (FolderInfo) info; ShortcutInfo firstItem = null; diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java index f890706ff..18cdc81f3 100644 --- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -1,6 +1,7 @@ package com.android.launcher3.compat; import android.content.Context; +import com.android.launcher3.Utilities; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -102,10 +103,11 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { /** * Computes the section name for an given string {@param s}. */ - public String computeSectionName(String s) { + public String computeSectionName(CharSequence cs) { + String s = Utilities.trim(cs); String sectionName = getBucketLabel(getBucketIndex(s)); - if (sectionName.trim().isEmpty() && s.length() > 0) { - boolean startsWithDigit = Character.isDigit(Character.codePointAt(s.trim(), 0)); + if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) { + boolean startsWithDigit = Character.isDigit(s.codePointAt(0)); if (startsWithDigit) { // Digit section return "#"; diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java index 767f16f62..967b53b0b 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java @@ -46,7 +46,7 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { @Override public String loadLabel(LauncherAppWidgetProviderInfo info) { - return info.label.trim(); + return Utilities.trim(info.label); } @Override diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java index 1a1de55c2..8f45a7754 100644 --- a/src/com/android/launcher3/widget/PackageItemInfo.java +++ b/src/com/android/launcher3/widget/PackageItemInfo.java @@ -49,7 +49,7 @@ public class PackageItemInfo extends ItemInfo { @Override public String toString() { - return "PackageItemInfo(title=" + title.toString() + " id=" + this.id + return "PackageItemInfo(title=" + title + " id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 2df170eff..27b7e6df5 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -222,10 +222,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { * Helper method to get the string info of the tag. */ private String getTagToString() { - if (getTag() instanceof PendingAddWidgetInfo) { - return ((PendingAddWidgetInfo)getTag()).toString(); - } else if (getTag() instanceof PendingAddShortcutInfo) { - return ((PendingAddShortcutInfo)getTag()).toString(); + if (getTag() instanceof PendingAddWidgetInfo || + getTag() instanceof PendingAddShortcutInfo) { + return getTag().toString(); } return ""; } -- cgit v1.2.3 From f4715974b80d06993342ca62b1298e4d90e2fab8 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Fri, 8 May 2015 17:21:03 -0700 Subject: Fixing accessibility scrolling events generated by PagedView: 1. Not generating scroll events from snapToPage(). It already gets generated from computeScrollHelper(). 2. Not setting action because doing so is not mentioned here: http://developer.android.com/reference/android/view/accessibility/AccessibilityEvent.html. 3. Not generating scroll event when the page stays same (before it was generated, say, when we simply returned from the AllApps view to Workspace). 4. From/To index is not the old and new page numbers; they are indices of the first and last item; in our case, the item is the page, and both FromIndex and ToIndex should be set to this page number. Bug: 18761184 Change-Id: I3dadf816c3d45b8bd42a13930344874584467499 --- src/com/android/launcher3/PagedView.java | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 0739babea..f77ad0564 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -712,21 +712,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc AccessibilityManager am = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (am.isEnabled()) { - AccessibilityEvent ev = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); - ev.setItemCount(getChildCount()); - ev.setFromIndex(mCurrentPage); - ev.setToIndex(getNextPage()); - - final int action; - if (getNextPage() >= mCurrentPage) { - action = AccessibilityNodeInfo.ACTION_SCROLL_FORWARD; - } else { - action = AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD; + if (mCurrentPage != getNextPage()) { + AccessibilityEvent ev = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); + ev.setItemCount(getChildCount()); + ev.setFromIndex(getNextPage()); + ev.setToIndex(getNextPage()); + + sendAccessibilityEventUnchecked(ev); } - - ev.setAction(action); - sendAccessibilityEventUnchecked(ev); } } @@ -2301,8 +2295,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc focusedChild.clearFocus(); } - sendScrollAccessibilityEvent(); - pageBeginMoving(); awakenScrollBars(duration); if (immediate) { -- cgit v1.2.3 From fee35bbfac29a55382880f846fff86003e615ccb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 11 May 2015 11:38:19 -0700 Subject: Updating folder grid sizes for various devices Change-Id: Ifd5930a4d6f54428500c0ef61ef21c0fc6a6f5cd --- src/com/android/launcher3/DeviceProfile.java | 22 ++++++++++++++++------ src/com/android/launcher3/DynamicGrid.java | 22 +++++++++++----------- src/com/android/launcher3/FolderPagedView.java | 12 ++---------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 918517ebd..94589ad87 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -69,8 +69,10 @@ public class DeviceProfile { String name; float minWidthDps; float minHeightDps; - public float numRows; - public float numColumns; + public int numRows; + public int numColumns; + public int numFolderRows; + public int numFolderColumns; float numHotseatIcons; float iconSize; private float iconTextSize; @@ -138,8 +140,9 @@ public class DeviceProfile { private ArrayList mCallbacks = new ArrayList(); - DeviceProfile(String n, float w, float h, float r, float c, - float is, float its, float hs, float his, int dlId) { + DeviceProfile(String n, float w, float h, + int r, int c, int fr, int fc, + float is, float its, float hs, float his, int dlId) { // Ensure that we have an odd number of hotseat items (since we need to place all apps) if (hs % 2 == 0) { throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); @@ -148,8 +151,12 @@ public class DeviceProfile { name = n; minWidthDps = w; minHeightDps = h; + numRows = r; numColumns = c; + numFolderRows = fr; + numFolderColumns = fc; + iconSize = is; iconTextSize = its; numHotseatIcons = hs; @@ -210,6 +217,9 @@ public class DeviceProfile { // Snap to the closest column count numColumns = closestProfile.numColumns; + numFolderRows = closestProfile.numFolderRows; + numFolderColumns = closestProfile.numFolderColumns; + // Snap to the closest hotseat size numHotseatIcons = closestProfile.numHotseatIcons; hotseatAllAppsRank = (int) (numHotseatIcons / 2); @@ -266,8 +276,8 @@ public class DeviceProfile { DeviceProfile partnerDp = p.getDeviceProfileOverride(dm); if (partnerDp != null) { if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) { - numRows = partnerDp.numRows; - numColumns = partnerDp.numColumns; + numRows = numFolderRows = partnerDp.numRows; + numColumns = numFolderColumns = partnerDp.numColumns; } if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) { allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount; diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java index 24da97fc6..d22427f44 100644 --- a/src/com/android/launcher3/DynamicGrid.java +++ b/src/com/android/launcher3/DynamicGrid.java @@ -59,30 +59,30 @@ public class DynamicGrid { DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm); // Our phone profiles include the bar sizes in each orientation deviceProfiles.add(new DeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Short Stubby", - 275, 420, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Stubby", - 255, 450, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 4", - 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 5", - 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Large Phone", - 406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); + 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); // The tablet profile is odd in that the landscape orientation // also includes the nav bar on the side deviceProfiles.add(new DeviceProfile("Nexus 7", - 575, 904, 5, 6, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); + 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); // Larger tablet profiles always have system bars on the top & bottom deviceProfiles.add(new DeviceProfile("Nexus 10", - 727, 1207, 5, 6, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); + 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); deviceProfiles.add(new DeviceProfile("20-inch Tablet", - 1527, 2527, 7, 7, 100, 20, 7, 72, R.xml.default_workspace_4x4)); + 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); mMinWidth = dpiFromPx(minWidthPx, dm); mMinHeight = dpiFromPx(minHeightPx, dm); mProfile = new DeviceProfile(context, deviceProfiles, diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index a1c909a1f..9f2f22581 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -51,9 +51,6 @@ public class FolderPagedView extends PagedView { private static final int[] sTempPosArray = new int[2]; - // TODO: Remove this restriction - private static final int MAX_ITEMS_PER_PAGE = 4; - public final boolean rtlLayout; private final LayoutInflater mInflater; @@ -80,13 +77,8 @@ public class FolderPagedView extends PagedView { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - if (ALLOW_FOLDER_SCROLL) { - mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE); - mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE); - } else { - mMaxCountX = (int) grid.numColumns; - mMaxCountY = (int) grid.numRows; - } + mMaxCountX = (int) grid.numFolderColumns; + mMaxCountY = (int) grid.numFolderRows; mMaxItemsPerPage = mMaxCountX * mMaxCountY; -- cgit v1.2.3 From ddc20bff8f09aadf65a3913ee56ff9a56e5beadc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 11 May 2015 14:08:36 -0700 Subject: When updating an icon, keeping the drawable padding same as before Bug: 17879518 Change-Id: Ifc819c353069bf470fd7ef6f989e4a2ea1289d2d --- src/com/android/launcher3/Workspace.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 5c2121ab8..55deb8559 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4185,7 +4185,7 @@ public class Workspace extends SmoothPagedView && packageNames.contains(cn.getPackageName())) { shortcutInfo.isDisabled |= reason; BubbleTextView shortcut = (BubbleTextView) v; - shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false); + shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, false); if (parent != null) { parent.invalidate(); @@ -4371,7 +4371,7 @@ public class Workspace extends SmoothPagedView BubbleTextView shortcut = (BubbleTextView) v; boolean oldPromiseState = getTextViewIcon(shortcut) instanceof PreloadIconDrawable; - shortcut.applyFromShortcutInfo(si, mIconCache, true, + shortcut.applyFromShortcutInfo(si, mIconCache, false, si.isPromise() != oldPromiseState); if (parent != null) { -- cgit v1.2.3 From 4b69f2ca91d63a59dfa921ba965b92de7f604f5a Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 11 May 2015 14:55:07 -0700 Subject: Ensure that clipping widget size is 20~40% of its width on every devices b/20338324 b/20763871 Change-Id: I69114cb6dfec95c473313a440a6db76a052e5601 --- res/layout/widget_cell.xml | 6 +- res/layout/widgets_list_row_view.xml | 11 ++-- res/values/dimens.xml | 6 +- src/com/android/launcher3/DeviceProfile.java | 5 +- src/com/android/launcher3/widget/WidgetCell.java | 37 ++++++++++- .../android/launcher3/widget/WidgetRowView.java | 74 ++++++++++++++++++++++ .../launcher3/widget/WidgetsListAdapter.java | 10 +++ 7 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 src/com/android/launcher3/widget/WidgetRowView.java diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index ab23b842e..196dfca66 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -19,10 +19,11 @@ android:layout_width="@dimen/widget_preview_container_width" android:layout_height="@dimen/widget_cell_height" android:layout_weight="1" - android:layout_marginRight="@dimen/widget_row_divider" + android:layout_marginEnd="@dimen/widget_row_divider" android:orientation="vertical" android:background="@color/widgets_cell_color" - android:focusable="true"> + android:focusable="true" + android:gravity="center_horizontal"> - - + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 46830d6e5..fd5bff30e 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -82,7 +82,7 @@ 8dp - 120dp + 130dp 8dp 8dp 8dp @@ -94,10 +94,10 @@ 16dp - 136dp + 146dp 150dp 8dp - 1dp + 2dp 0dp diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 918517ebd..c1dd85961 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -80,12 +80,11 @@ public class DeviceProfile { int defaultLayoutId; boolean isLandscape; - boolean isTablet; - boolean isLargeTablet; + public boolean isTablet; + public boolean isLargeTablet; public boolean isLayoutRtl; boolean transposeLayoutWithOrientation; - int desiredWorkspaceLeftRightMarginPx; public int edgeMarginPx; Rect defaultWidgetPadding; diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 2df170eff..f5c44ab0e 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -49,7 +49,16 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final boolean DEBUG = false; private static final int FADE_IN_DURATION_MS = 90; - private int mPresetPreviewSize; + + /** Widget cell width is calculated by multiplying this factor to grid cell width. */ + private static final float WIDTH_SCALE = 2.8f; + + /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ + private static final float PREVIEW_SCALE = 0.9f; + + private static int mPresetPreviewSize; + private static int mSize; + private static int mDividerWidth; private ImageView mWidgetImage; private TextView mWidgetName; @@ -76,12 +85,22 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { final Resources r = context.getResources(); mDimensionsFormatString = r.getString(R.string.widget_dims_format); - mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size); + setContainerWidth(); setWillNotDraw(false); setClipToPadding(false); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); + } + private void setContainerWidth() { + // Do nothing if already set + if (mSize > 0) { + return; + } + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + mSize = (int) (profile.cellWidthPx * WIDTH_SCALE); + mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE); + mDividerWidth = getResources().getDimensionPixelSize(R.dimen.widget_row_divider); } @Override @@ -98,6 +117,12 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY)); + } + /** * Called to clear the view and free attached resources. (e.g., {@link Bitmap} */ @@ -108,6 +133,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetImage.setImageDrawable(null); mWidgetName.setText(null); mWidgetDims.setText(null); + setSeparator(true); if (mActiveRequest != null) { mActiveRequest.cleanup(); @@ -229,4 +255,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } return ""; } + + public void setSeparator(boolean enable) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams(); + lp.setMarginEnd(enable? mDividerWidth : 0); + setLayoutParams(lp); + requestLayout(); + } } diff --git a/src/com/android/launcher3/widget/WidgetRowView.java b/src/com/android/launcher3/widget/WidgetRowView.java new file mode 100644 index 000000000..05760ae48 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetRowView.java @@ -0,0 +1,74 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.widget.LinearLayout; + + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DynamicGrid; +import com.android.launcher3.LauncherAppState; + +/** + * Represents the individual cell of the widget inside the widget tray. + */ +public class WidgetRowView extends LinearLayout { + + private static final int PRESET_INDENT_SIZE_TABLET = 56; + + /** Widget row width is calculated by multiplying this factor to grid cell width. */ + private static final float HEIGHT_SCALE = 2.8f; + + static int sIndent = 0; + static int sHeight = 0; + + public WidgetRowView(Context context) { + this(context, null); + } + + public WidgetRowView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetRowView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setContainerHeight(); + setWillNotDraw(false); + setClipToPadding(false); + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); + } + + /** + * Sets the widget cell container size based on the physical dimension of the device. + */ + private void setContainerHeight() { + // Do nothing if already set + if (sHeight > 0) { + return; + } + + Resources r = getResources(); + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + if (profile.isLargeTablet || profile.isTablet) { + sIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); + } + sHeight = (int) (profile.cellWidthPx * HEIGHT_SCALE); + } +} diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index a7728a11b..8b0a43b2f 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -23,6 +23,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.IconCache; @@ -105,6 +106,10 @@ public class WidgetsListAdapter extends Adapter { // set up touch. widget.setOnClickListener(mIconClickListener); widget.setOnLongClickListener(mIconLongClickListener); + // Add a devider if it is not the last item. + if (i == diff - 1) { + widget.setSeparator(false); + } row.addView(widget); } } else if (diff < 0) { @@ -156,6 +161,11 @@ public class WidgetsListAdapter extends Adapter { ViewGroup container = (ViewGroup) mLayoutInflater.inflate( R.layout.widgets_list_row_view, parent, false); + WidgetRowView row = (WidgetRowView) container.findViewById(R.id.widget_row); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) row.getLayoutParams(); + lp.setMarginStart(WidgetRowView.sIndent); + lp.height = WidgetRowView.sHeight; + row.setLayoutParams(lp); return new WidgetsRowViewHolder(container); } -- cgit v1.2.3 From 4ac30068732c7216288999d255a823adb7ca7c12 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 8 May 2015 17:34:17 -0700 Subject: Initial changes to support predicted apps. Change-Id: I80117d51074fe3dbdbb8d81cae886b1dffdfb86a --- res/layout/apps_grid_row_icon_view.xml | 4 +- res/layout/apps_list_row_icon_view.xml | 29 --------- res/layout/apps_list_view.xml | 4 +- res/values-sw600dp/dimens.xml | 1 + res/values-sw720dp/dimens.xml | 2 + res/values/dimens.xml | 2 + .../android/launcher3/AlphabeticalAppsList.java | 71 ++++++++++++++++---- .../launcher3/AppsContainerRecyclerView.java | 10 +-- src/com/android/launcher3/AppsContainerView.java | 8 +++ src/com/android/launcher3/AppsGridAdapter.java | 76 +++++++++++++++++----- src/com/android/launcher3/Launcher.java | 40 ++++++++++-- src/com/android/launcher3/LauncherCallbacks.java | 2 + src/com/android/launcher3/LauncherExtension.java | 6 ++ 13 files changed, 182 insertions(+), 73 deletions(-) delete mode 100644 res/layout/apps_list_row_icon_view.xml diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml index 81e74b985..149e28e2d 100644 --- a/res/layout/apps_grid_row_icon_view.xml +++ b/res/layout/apps_grid_row_icon_view.xml @@ -21,8 +21,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|center_vertical" - android:paddingTop="8dp" - android:paddingBottom="8dp" + android:paddingTop="@dimen/apps_icon_top_bottom_padding" + android:paddingBottom="@dimen/apps_icon_top_bottom_padding" android:focusable="true" android:background="@drawable/focusable_view_bg" launcher:deferShadowGeneration="true" /> diff --git a/res/layout/apps_list_row_icon_view.xml b/res/layout/apps_list_row_icon_view.xml deleted file mode 100644 index 867dbdc99..000000000 --- a/res/layout/apps_list_row_icon_view.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index a726cd8fe..ddcb639b8 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -25,7 +25,7 @@ 64dp 26sp 76dp + 12dp 8dp diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index 68fc1ecaf..cec6b7db0 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -16,6 +16,8 @@ 72dp + 56dp + 16dp 8dip diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 46830d6e5..1c271ecaf 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -59,6 +59,8 @@ -16dp 64dp 40dp + 52dp + 8dp 8dp diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index f075e417d..62cb237e1 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -130,17 +130,21 @@ public class AlphabeticalAppsList { public AppInfo appInfo = null; // The index of this app not including sections public int appIndex = -1; + // Whether or not this is a predicted app + public boolean isPredictedApp; public static AdapterItem asSectionBreak(int pos, SectionInfo section) { AdapterItem item = new AdapterItem(); item.position = pos; item.isSectionHeader = true; item.sectionInfo = section; + section.sectionBreakItem = item; return item; } public static AdapterItem asApp(int pos, SectionInfo section, String sectionName, - int sectionAppIndex, AppInfo appInfo, int appIndex) { + int sectionAppIndex, AppInfo appInfo, int appIndex, + boolean isPredictedApp) { AdapterItem item = new AdapterItem(); item.position = pos; item.isSectionHeader = false; @@ -149,6 +153,7 @@ public class AlphabeticalAppsList { item.sectionAppIndex = sectionAppIndex; item.appInfo = appInfo; item.appIndex = appIndex; + item.isPredictedApp = isPredictedApp; return item; } } @@ -168,6 +173,7 @@ public class AlphabeticalAppsList { private List mSectionedFilteredApps = new ArrayList<>(); private List mSections = new ArrayList<>(); private List mFastScrollerSections = new ArrayList<>(); + private List mPredictedApps = new ArrayList<>(); private RecyclerView.Adapter mAdapter; private Filter mFilter; private AlphabeticIndexCompat mIndexer; @@ -251,6 +257,17 @@ public class AlphabeticalAppsList { } } + /** + * 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. + */ + public void setPredictedApps(List apps) { + mPredictedApps.clear(); + mPredictedApps.addAll(apps); + onAppsUpdated(); + mAdapter.notifyDataSetChanged(); + } + /** * Sets the current set of apps. */ @@ -336,7 +353,7 @@ public class AlphabeticalAppsList { // Sort the list of apps Collections.sort(mApps, mAppNameComparator.getComparator()); - // Recreate the filtered and sectioned apps (for convenience for the grid layout) + // Prepare to update the list of sections, filtered apps, etc. mFilteredApps.clear(); mSections.clear(); mSectionedFilteredApps.clear(); @@ -346,36 +363,62 @@ public class AlphabeticalAppsList { FastScrollSectionInfo lastFastScrollerSectionInfo = null; int position = 0; int appIndex = 0; - int numApps = mApps.size(); - for (AppInfo info : mApps) { - String sectionName = mIndexer.computeSectionName(info.title); + List allApps = new ArrayList<>(); + + // Add the predicted apps to the combined list + int numPredictedApps = 0; + if (mPredictedApps != null && !mPredictedApps.isEmpty() && !hasFilter()) { + for (ComponentName cn : mPredictedApps) { + for (AppInfo info : mApps) { + if (cn.equals(info.componentName)) { + allApps.add(info); + numPredictedApps++; + break; + } + } + // Stop at the number of predicted apps + if (numPredictedApps == mNumAppsPerRow) { + break; + } + } + } + + // Add all the other apps to the combined list + allApps.addAll(mApps); + + // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the + // combined list + int numApps = allApps.size(); + for (int i = 0; i < numApps; i++) { + boolean isPredictedApp = i < numPredictedApps; + AppInfo info = allApps.get(i); + String sectionName = isPredictedApp ? "" : mIndexer.computeSectionName(info.title); // Check if we want to retain this app if (mFilter != null && !mFilter.retainApp(info, sectionName)) { continue; } - // Create a new section if necessary - if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { + // Create a new section if the section names do not match + if (lastSectionInfo == null || + (!isPredictedApp && !sectionName.equals(lastSectionName))) { lastSectionName = sectionName; lastSectionInfo = new SectionInfo(); - mSections.add(lastSectionInfo); lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, (float) appIndex / numApps); + mSections.add(lastSectionInfo); mFastScrollerSections.add(lastFastScrollerSectionInfo); - // Create a new section item, this item is used to break the flow of items in the - // list - AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); + // Create a new section item to break the flow of items in the list if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { - lastSectionInfo.sectionBreakItem = sectionItem; + AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); mSectionedFilteredApps.add(sectionItem); } } // Create an app item AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, - lastSectionInfo.numApps++, info, appIndex++); + lastSectionInfo.numApps++, info, appIndex++, isPredictedApp); if (lastSectionInfo.firstAppItem == null) { lastSectionInfo.firstAppItem = appItem; lastFastScrollerSectionInfo.appItem = appItem; @@ -384,8 +427,8 @@ public class AlphabeticalAppsList { mFilteredApps.add(info); } + // Go through each section and try and merge some of the sections if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { - // Go through each section and try and merge some of the sections int minNumAppsPerRow = (int) Math.ceil(mNumAppsPerRow / 2f); int sectionAppCount = 0; for (int i = 0; i < mSections.size(); i++) { diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 6556cf920..edb6f0c6e 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -229,7 +229,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { * Draws the fast scroller popup. */ private void drawFastScrollerPopup(Canvas canvas) { - if (mFastScrollAlpha > 0f) { + if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { int x; int y; boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == @@ -381,16 +381,16 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } /** - * Returns the row index for a given position in the list. + * Returns the row index for a app index in the list. */ - private int findRowForAppIndex(int position) { + private int findRowForAppIndex(int index) { List sections = mApps.getSections(); int appIndex = 0; int rowCount = 0; for (AlphabeticalAppsList.SectionInfo info : sections) { int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - if (appIndex + info.numApps > position) { - return rowCount + ((position - appIndex) / mNumAppsPerRow); + if (appIndex + info.numApps > index) { + return rowCount + ((index - appIndex) / mNumAppsPerRow); } appIndex += info.numApps; rowCount += numRowsInSection; diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index aa6c05993..b8d30d081 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3; +import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -110,6 +111,13 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mApps.setAdapter(mAdapter); } + /** + * Sets the current set of predicted apps. + */ + public void setPredictedApps(List apps) { + mApps.setPredictedApps(apps); + } + /** * Sets the current set of apps. */ diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 259740c60..9ecb2eeb6 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -35,13 +35,11 @@ class AppsGridAdapter extends RecyclerView.Adapter { */ public static class ViewHolder extends RecyclerView.ViewHolder { public View mContent; - public boolean mIsSectionHeader; public boolean mIsEmptyRow; - public ViewHolder(View v, boolean isSectionHeader, boolean isEmptyRow) { + public ViewHolder(View v, boolean isEmptyRow) { super(v); mContent = v; - mIsSectionHeader = isSectionHeader; mIsEmptyRow = isEmptyRow; } } @@ -93,13 +91,29 @@ class AppsGridAdapter extends RecyclerView.Adapter { } List items = mApps.getAdapterItems(); + boolean hasDrawnPredictedAppDivider = false; int childCount = parent.getChildCount(); int lastSectionTop = 0; int lastSectionHeight = 0; for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); - if (shouldDrawItemSection(holder, child, i, items)) { + if (!isValidHolderAndChild(holder, child, items)) { + continue; + } + + if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) { + // Draw the divider under the predicted app + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid(). + getDeviceProfile(); + int top = child.getTop() + child.getHeight(); + int left = parent.getPaddingLeft(); + int right = parent.getWidth() - parent.getPaddingRight(); + int iconInset = (((right - left) / mAppsPerRow) - grid.allAppsIconSizePx) / 2; + c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint); + hasDrawnPredictedAppDivider = true; + + } else if (shouldDrawItemSection(holder, i, items)) { // At this point, we only draw sections for each section break; int viewTopOffset = (2 * child.getPaddingTop()); int pos = holder.getPosition(); @@ -186,9 +200,9 @@ class AppsGridAdapter extends RecyclerView.Adapter { } /** - * Returns whether to draw the section for the given child. + * Returns whether we consider this a valid view holder for us to draw a divider or section for. */ - private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex, + private boolean isValidHolderAndChild(ViewHolder holder, View child, List items) { // Ensure item is not already removed GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) @@ -200,18 +214,44 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (holder == null) { return false; } + // Ensure we have a holder position + int pos = holder.getPosition(); + if (pos < 0 || pos >= items.size()) { + return false; + } + return true; + } + + /** + * Returns whether to draw the divider for a given child. + */ + private boolean shouldDrawItemDivider(ViewHolder holder, List items) { + int pos = holder.getPosition(); + return items.get(pos).isPredictedApp; + } + + /** + * Returns whether to draw the section for the given child. + */ + private boolean shouldDrawItemSection(ViewHolder holder, int childIndex, + List items) { + int pos = holder.getPosition(); + AlphabeticalAppsList.AdapterItem item = items.get(pos); + // Ensure it's not an empty row if (holder.mIsEmptyRow) { return false; } - // Ensure we have a holder position - int pos = holder.getPosition(); - if (pos <= 0 || pos >= items.size()) { + // Ensure this is not a section break + if (item.isSectionHeader) { + return false; + } + // Ensure this is not a predicted app + if (item.isPredictedApp) { return false; } // Draw the section header for the first item in each section - return (childIndex == 0) || - (items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader); + return (childIndex == 0) || (items.get(pos - 1).isSectionHeader && !item.isSectionHeader); } } @@ -232,6 +272,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Thunk int mStartMargin; @Thunk int mSectionHeaderOffset; @Thunk Paint mSectionTextPaint; + @Thunk Paint mPredictedAppsDividerPaint; public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, @@ -254,11 +295,17 @@ class AppsGridAdapter extends RecyclerView.Adapter { mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset); } mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset); + mSectionTextPaint = new Paint(); mSectionTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.apps_view_section_text_size)); mSectionTextPaint.setColor(res.getColor(R.color.apps_view_section_text_color)); mSectionTextPaint.setAntiAlias(true); + + mPredictedAppsDividerPaint = new Paint(); + mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1.5f, res.getDisplayMetrics())); + mPredictedAppsDividerPaint.setColor(0x10000000); + mPredictedAppsDividerPaint.setAntiAlias(true); } /** @@ -313,10 +360,9 @@ class AppsGridAdapter extends RecyclerView.Adapter { switch (viewType) { case EMPTY_VIEW_TYPE: return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent, - false), false /* isSectionRow */, true /* isEmptyRow */); + false), true /* isEmptyRow */); case SECTION_BREAK_VIEW_TYPE: - return new ViewHolder(new View(parent.getContext()), true /* isSectionRow */, - false /* isEmptyRow */); + return new ViewHolder(new View(parent.getContext()), false /* isEmptyRow */); case ICON_VIEW_TYPE: BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( R.layout.apps_grid_row_icon_view, parent, false); @@ -324,7 +370,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { icon.setOnClickListener(mIconClickListener); icon.setOnLongClickListener(mIconLongClickListener); icon.setFocusable(true); - return new ViewHolder(icon, false /* isSectionRow */, false /* isEmptyRow */); + return new ViewHolder(icon, false /* isEmptyRow */); default: throw new RuntimeException("Unexpected view type"); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 564575904..11b1e9aa5 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; +import android.preference.PreferenceManager; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -1018,16 +1019,22 @@ public class Launcher extends Activity if (mOnResumeState == State.WORKSPACE) { showWorkspace(false); } else if (mOnResumeState == State.APPS) { - showAppsView(false /* animated */, false /* resetListToTop */); + // Don't update the predicted apps if the user is returning to launcher in the apps + // view as they may be depending on the UI to be static to switch to another app + showAppsView(false /* animated */, false /* resetListToTop */, + false /* updatePredictedApps */); } else if (mOnResumeState == State.WIDGETS) { showWidgetsView(false, false); } mOnResumeState = State.NONE; // Restore the apps state if we are in all apps - if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mState == State.APPS) { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onAllAppsShown(); + if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { + // Otherwise, notify the callbacks if we are in all apps mode + if (mState == State.APPS) { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onAllAppsShown(); + } } } @@ -2619,7 +2626,9 @@ public class Launcher extends Activity if (isAppsViewVisible()) { showWorkspace(true); } else { - showAppsView(true /* animated */, false /* resetListToTop */); + // Try and refresh the set of predicted apps before we enter launcher + showAppsView(true /* animated */, false /* resetListToTop */, + true /* updatePredictedApps */); } } @@ -3397,10 +3406,13 @@ public class Launcher extends Activity /** * Shows the apps view. */ - void showAppsView(boolean animated, boolean resetListToTop) { + void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps) { if (resetListToTop) { mAppsView.scrollToTop(); } + if (updatePredictedApps) { + tryAndUpdatePredictedApps(); + } showAppsOrWidgets(animated, State.APPS); } @@ -3509,12 +3521,26 @@ public class Launcher extends Activity void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { - showAppsView(true, false); + showAppsView(true /* animated */, false /* resetListToTop */, + false /* updatePredictedApps */); } else if (mState == State.WIDGETS_SPRING_LOADED) { showWidgetsView(true, false); } } + /** + * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was + * resumed. + */ + private void tryAndUpdatePredictedApps() { + if (mLauncherCallbacks != null) { + List apps = mLauncherCallbacks.getPredictedApps(); + if (!apps.isEmpty()) { + mAppsView.setPredictedApps(apps); + } + } + } + void lockAllApps() { // TODO } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 25c86c977..0124d1f28 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -11,6 +11,7 @@ import android.view.ViewGroup; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; /** * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks @@ -90,6 +91,7 @@ public interface LauncherCallbacks { public boolean overrideWallpaperDimensions(); public boolean isLauncherPreinstalled(); public boolean overrideAllAppsSearch(); + public List getPredictedApps(); /** * Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup, diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index 8174af045..14ad6016c 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -15,6 +15,7 @@ import android.view.ViewGroup; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; /** * This class represents a very trivial LauncherExtension. It primarily serves as a simple @@ -258,6 +259,11 @@ public class LauncherExtension extends Launcher { return false; } + @Override + public List getPredictedApps() { + return new ArrayList<>(); + } + @Override public boolean isLauncherPreinstalled() { return false; -- cgit v1.2.3 From dfaccf64bd59343de483b12eee6db61c960aedce Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 11 May 2015 16:30:44 -0700 Subject: Various icon size changes 1) Setting different icon and text size for workspace and all apps 2) Setting padding during BubbleTextView inflation Change-Id: I59da6986b0d98e8e2aa6065743bb799a6bf06a37 --- res/layout/apps_customize_application.xml | 21 ---------- res/layout/apps_grid_row_icon_view.xml | 3 +- res/layout/apps_list_row_icon_view.xml | 29 -------------- res/layout/apps_list_row_view.xml | 33 ---------------- res/values/attrs.xml | 6 ++- src/com/android/launcher3/BubbleTextView.java | 53 ++++++++++++-------------- src/com/android/launcher3/Folder.java | 3 +- src/com/android/launcher3/FolderPagedView.java | 2 +- src/com/android/launcher3/Launcher.java | 26 ++++++------- src/com/android/launcher3/Workspace.java | 7 ++-- 10 files changed, 48 insertions(+), 135 deletions(-) delete mode 100644 res/layout/apps_customize_application.xml delete mode 100644 res/layout/apps_list_row_icon_view.xml delete mode 100644 res/layout/apps_list_row_view.xml diff --git a/res/layout/apps_customize_application.xml b/res/layout/apps_customize_application.xml deleted file mode 100644 index c56cdf3d2..000000000 --- a/res/layout/apps_customize_application.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml index 81e74b985..286deb454 100644 --- a/res/layout/apps_grid_row_icon_view.xml +++ b/res/layout/apps_grid_row_icon_view.xml @@ -25,5 +25,6 @@ android:paddingBottom="8dp" android:focusable="true" android:background="@drawable/focusable_view_bg" - launcher:deferShadowGeneration="true" /> + launcher:deferShadowGeneration="true" + launcher:iconDisplay="all_apps" /> diff --git a/res/layout/apps_list_row_icon_view.xml b/res/layout/apps_list_row_icon_view.xml deleted file mode 100644 index 867dbdc99..000000000 --- a/res/layout/apps_list_row_icon_view.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - diff --git a/res/layout/apps_list_row_view.xml b/res/layout/apps_list_row_view.xml deleted file mode 100644 index e80285b95..000000000 --- a/res/layout/apps_list_row_view.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - diff --git a/res/values/attrs.xml b/res/values/attrs.xml index a1f28452a..fee3417b0 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -22,8 +22,10 @@ - - + + + + diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index ae6ebba34..009cc2ba5 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -52,13 +52,14 @@ public class BubbleTextView extends TextView { private static final int SHADOW_SMALL_COLOUR = 0xCC000000; static final float PADDING_V = 3.0f; + private static final int DISPLAY_WORKSPACE = 0; + private static final int DISPLAY_ALL_APPS = 1; + private Drawable mIcon; private final Drawable mBackground; private final CheckLongPressHelper mLongPressHelper; private final HolographicOutlineHelper mOutlineHelper; - // TODO: Remove custom background handling code, as no instance of BubbleTextView use any - // background. private boolean mBackgroundSizeChanged; private Bitmap mPressedBackground; @@ -69,8 +70,6 @@ public class BubbleTextView extends TextView { private final boolean mCustomShadowsEnabled; private final boolean mLayoutHorizontal; private final int mIconSize; - private final int mIconPaddingSize; - private final int mTextSize; private int mTextColor; private boolean mStayPressed; @@ -95,14 +94,21 @@ public class BubbleTextView extends TextView { R.styleable.BubbleTextView, defStyle, 0); mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true); mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false); - mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride, - grid.allAppsIconSizePx); - mIconPaddingSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconPaddingOverride, - grid.iconDrawablePaddingPx); - mTextSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_textSizeOverride, - grid.allAppsIconTextSizePx); mDeferShadowGenerationOnTouch = a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false); + + int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE); + int defaultIconSize = grid.iconSizePx; + if (display == DISPLAY_WORKSPACE) { + setTextSize(grid.iconTextSizePx); + } else if (display == DISPLAY_ALL_APPS) { + setTextSize(grid.allAppsIconTextSizePx); + defaultIconSize = grid.allAppsIconSizePx; + } + + mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride, + defaultIconSize); + a.recycle(); if (mCustomShadowsEnabled) { @@ -128,26 +134,18 @@ public class BubbleTextView extends TextView { setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } - public void onFinishInflate() { - super.onFinishInflate(); - - // Ensure we are using the right text size - setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize); - } - - public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, - boolean setDefaultPadding) { - applyFromShortcutInfo(info, iconCache, setDefaultPadding, false); + public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) { + applyFromShortcutInfo(info, iconCache, false); } public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, - boolean setDefaultPadding, boolean promiseStateChanged) { + boolean promiseStateChanged) { Bitmap b = info.getIcon(iconCache); FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); iconDrawable.setGhostModeEnabled(info.isDisabled != 0); - setIcon(iconDrawable, mIconSize, setDefaultPadding ? mIconPaddingSize : -1); + setIcon(iconDrawable, mIconSize); if (info.contentDescription != null) { setContentDescription(info.contentDescription); } @@ -160,7 +158,7 @@ public class BubbleTextView extends TextView { } public void applyFromApplicationInfo(AppInfo info) { - setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize, mIconPaddingSize); + setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -409,7 +407,7 @@ public class BubbleTextView extends TextView { preloadDrawable = (PreloadIconDrawable) mIcon; } else { preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme()); - setIcon(preloadDrawable, mIconSize, -1); + setIcon(preloadDrawable, mIconSize); } preloadDrawable.setLevel(progressLevel); @@ -437,7 +435,7 @@ public class BubbleTextView extends TextView { /** * Sets the icon for this view based on the layout direction. */ - private Drawable setIcon(Drawable icon, int iconSize, int drawablePadding) { + private Drawable setIcon(Drawable icon, int iconSize) { mIcon = icon; if (iconSize != -1) { mIcon.setBounds(0, 0, iconSize, iconSize); @@ -447,9 +445,6 @@ public class BubbleTextView extends TextView { } else { setCompoundDrawablesRelative(null, mIcon, null, null); } - if (drawablePadding != -1) { - setCompoundDrawablePadding(drawablePadding); - } return icon; } @@ -463,7 +458,7 @@ public class BubbleTextView extends TextView { applyFromApplicationInfo((AppInfo) info); } else if (info instanceof ShortcutInfo) { applyFromShortcutInfo((ShortcutInfo) info, - LauncherAppState.getInstance().getIconCache(), false); + LauncherAppState.getInstance().getIconCache()); } } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 6ca488853..a955b27a7 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -1076,8 +1076,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Move the item from the folder to the workspace, in the position of the folder if (getItemCount() == 1) { ShortcutInfo finalItem = mInfo.contents.get(0); - child = mLauncher.createShortcut(R.layout.application, cellLayout, - finalItem); + child = mLauncher.createShortcut(cellLayout, finalItem); LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY); } diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 211bbfe74..63550827b 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -206,7 +206,7 @@ public class FolderPagedView extends PagedView { public View createNewView(ShortcutInfo item) { final BubbleTextView textView = (BubbleTextView) mInflater.inflate( R.layout.folder_application, null, false); - textView.applyFromShortcutInfo(item, mIconCache, false); + textView.applyFromShortcutInfo(item, mIconCache); textView.setOnClickListener(mFolder); textView.setOnLongClickListener(mFolder); textView.setOnFocusChangeListener(mFocusIndicatorView); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 564575904..b17a92f47 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -347,6 +347,8 @@ public class Launcher extends Activity private Canvas mFolderIconCanvas; private Rect mRectForFolderAnimation = new Rect(); + private DeviceProfile mDeviceProfile; + private BubbleTextView mWaitingForResume; protected static HashMap sCustomAppWidgets = @@ -423,7 +425,7 @@ public class Launcher extends Activity LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this); // Lazy-initialize the dynamic grid - DeviceProfile grid = app.initDynamicGrid(this); + mDeviceProfile = app.initDynamicGrid(this); // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), @@ -431,7 +433,7 @@ public class Launcher extends Activity mIsSafeModeEnabled = getPackageManager().isSafeMode(); mModel = app.setLauncher(this); mIconCache = app.getIconCache(); - mIconCache.flushInvalidIcons(grid); + mIconCache.flushInvalidIcons(mDeviceProfile); mDragController = new DragController(this); mInflater = getLayoutInflater(); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this); @@ -457,7 +459,7 @@ public class Launcher extends Activity setContentView(R.layout.launcher); setupViews(); - grid.layout(this); + mDeviceProfile.layout(this); registerContentObservers(); @@ -1511,22 +1513,22 @@ public class Launcher extends Activity * @return A View inflated from R.layout.application. */ View createShortcut(ShortcutInfo info) { - return createShortcut(R.layout.application, - (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); + return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); } /** * Creates a view representing a shortcut inflated from the specified resource. * - * @param layoutResId The id of the XML layout used to create the shortcut. * @param parent The group the shortcut belongs to. * @param info The data structure describing the shortcut. * * @return A View inflated from layoutResId. */ - public View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { - BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false); - favorite.applyFromShortcutInfo(info, mIconCache, true); + public View createShortcut(ViewGroup parent, ShortcutInfo info) { + BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.application, + parent, false); + favorite.applyFromShortcutInfo(info, mIconCache); + favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx); favorite.setOnClickListener(this); favorite.setOnFocusChangeListener(mFocusHandler); return favorite; @@ -4157,13 +4159,11 @@ public class Launcher extends Activity } public boolean useVerticalBarLayout() { - return LauncherAppState.getInstance().getDynamicGrid(). - getDeviceProfile().isVerticalBarLayout(); + return mDeviceProfile.isVerticalBarLayout(); } protected Rect getSearchBarBounds() { - return LauncherAppState.getInstance().getDynamicGrid(). - getDeviceProfile().getSearchBarBounds(); + return mDeviceProfile.getSearchBarBounds(); } public void bindSearchablesChanged() { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 55deb8559..4004b1d90 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3558,8 +3558,7 @@ public class Workspace extends SmoothPagedView // Came from all apps -- make a copy info = ((AppInfo) info).makeShortcut(); } - view = mLauncher.createShortcut(R.layout.application, cellLayout, - (ShortcutInfo) info); + view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info); break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout, @@ -4185,7 +4184,7 @@ public class Workspace extends SmoothPagedView && packageNames.contains(cn.getPackageName())) { shortcutInfo.isDisabled |= reason; BubbleTextView shortcut = (BubbleTextView) v; - shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, false); + shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache); if (parent != null) { parent.invalidate(); @@ -4371,7 +4370,7 @@ public class Workspace extends SmoothPagedView BubbleTextView shortcut = (BubbleTextView) v; boolean oldPromiseState = getTextViewIcon(shortcut) instanceof PreloadIconDrawable; - shortcut.applyFromShortcutInfo(si, mIconCache, false, + shortcut.applyFromShortcutInfo(si, mIconCache, si.isPromise() != oldPromiseState); if (parent != null) { -- cgit v1.2.3 From 13eb527b5ae7f564e3ace6137a8d466636d87188 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 11 May 2015 16:30:13 -0700 Subject: Exploring dense all apps layout. - Disabling section headers in all apps on tablet layouts - Fixing issue with predictions not showing on rotation - Fixing issue with over-aggressive dismissing of keyboard & filtered app state - Fixing issue where the container bounds were running straight up to the nav bar Change-Id: I5a5a56afa75b50be96af4894bf785ffbb1b15fb3 --- res/layout/apps_empty_view.xml | 2 +- res/values-sw600dp/dimens.xml | 6 +- res/values-sw720dp/dimens.xml | 2 +- .../android/launcher3/AlphabeticalAppsList.java | 101 +++++++++++++++++---- src/com/android/launcher3/AppsContainerView.java | 16 +++- src/com/android/launcher3/AppsGridAdapter.java | 8 +- src/com/android/launcher3/BaseContainerView.java | 5 +- src/com/android/launcher3/Launcher.java | 9 +- .../launcher3/widget/WidgetsContainerView.java | 2 +- 9 files changed, 115 insertions(+), 36 deletions(-) diff --git a/res/layout/apps_empty_view.xml b/res/layout/apps_empty_view.xml index 8408077a2..e4c4e2e8c 100644 --- a/res/layout/apps_empty_view.xml +++ b/res/layout/apps_empty_view.xml @@ -18,7 +18,7 @@ android:id="@+id/empty_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" + android:gravity="center" android:paddingTop="24dp" android:paddingBottom="24dp" android:paddingRight="@dimen/apps_grid_view_start_margin" diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index b18464292..2bdd4f0fe 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -18,10 +18,10 @@ 64dp - 24dp - 64dp + 18dp + 0dp 26sp - 76dp + 72dp 12dp diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index cec6b7db0..39b0c8006 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -16,7 +16,7 @@ 72dp - 56dp + 54dp 16dp diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 62cb237e1..de4edcb7b 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -3,6 +3,7 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; +import android.util.Log; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; @@ -77,6 +78,9 @@ class AppNameComparator { */ public class AlphabeticalAppsList { + public static final String TAG = "AlphabeticalAppsList"; + private static final boolean DEBUG = false; + /** * Info about a section in the alphabetic list */ @@ -162,11 +166,58 @@ public class AlphabeticalAppsList { * A filter interface to limit the set of applications in the apps list. */ public interface Filter { - public boolean retainApp(AppInfo info, String sectionName); + boolean retainApp(AppInfo info, String sectionName); + } + + /** + * Common interface for different merging strategies. + */ + private interface MergeAlgorithm { + boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount); } - // The maximum number of rows allowed in a merged section before we stop merging - private static final int MAX_ROWS_IN_MERGED_SECTION = 3; + /** + * The logic we use to merge sections on tablets. + */ + private static class TabletMergeAlgorithm implements MergeAlgorithm { + + @Override + public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + // Merge EVERYTHING + return true; + } + } + + /** + * The logic we use to merge sections on phones. + */ + private static class PhoneMergeAlgorithm implements MergeAlgorithm { + + private int mMinAppsPerRow; + private int mMinRowsInMergedSection; + private int mMaxAllowableMerges; + + public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { + mMinAppsPerRow = minAppsPerRow; + mMinRowsInMergedSection = minRowsInMergedSection; + mMaxAllowableMerges = maxNumMerges; + } + + @Override + public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + // Continue merging if the number of hanging apps on the final row is less than some + // fixed number (ragged), the merged rows has yet to exceed some minimum row count, + // and while the number of merged sections is less than some fixed number of merges + int rows = sectionAppCount / numAppsPerRow; + int cols = sectionAppCount % numAppsPerRow; + return (0 < cols && cols < mMinAppsPerRow) && + rows < mMinRowsInMergedSection && + mergeCount < mMaxAllowableMerges; + } + } + + private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3; + private static final int MAX_NUM_MERGES_PHONE = 2; private List mApps = new ArrayList<>(); private List mFilteredApps = new ArrayList<>(); @@ -174,13 +225,13 @@ public class AlphabeticalAppsList { private List mSections = new ArrayList<>(); private List mFastScrollerSections = new ArrayList<>(); private List mPredictedApps = new ArrayList<>(); + private HashMap mCachedSectionNames = new HashMap<>(); private RecyclerView.Adapter mAdapter; private Filter mFilter; private AlphabeticIndexCompat mIndexer; private AppNameComparator mAppNameComparator; + private MergeAlgorithm mMergeAlgorithm; private int mNumAppsPerRow; - // The maximum number of section merges we allow at a given time before we stop merging - private int mMaxAllowableMerges = Integer.MAX_VALUE; public AlphabeticalAppsList(Context context, int numAppsPerRow) { mIndexer = new AlphabeticIndexCompat(context); @@ -193,7 +244,16 @@ public class AlphabeticalAppsList { */ public void setNumAppsPerRow(int numAppsPerRow) { mNumAppsPerRow = numAppsPerRow; - mMaxAllowableMerges = (int) Math.ceil(numAppsPerRow / 2f); + + // Update the merge algorithm + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + if (grid.isPhone()) { + mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f), + MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); + } else { + mMergeAlgorithm = new TabletMergeAlgorithm(); + } + onAppsUpdated(); } @@ -392,7 +452,15 @@ public class AlphabeticalAppsList { for (int i = 0; i < numApps; i++) { boolean isPredictedApp = i < numPredictedApps; AppInfo info = allApps.get(i); - String sectionName = isPredictedApp ? "" : mIndexer.computeSectionName(info.title); + String sectionName = ""; + if (!isPredictedApp) { + // Only cache section names from non-predicted apps + sectionName = mCachedSectionNames.get(info.title); + if (sectionName == null) { + sectionName = mIndexer.computeSectionName(info.title); + mCachedSectionNames.put(info.title, sectionName); + } + } // Check if we want to retain this app if (mFilter != null && !mFilter.retainApp(info, sectionName)) { @@ -429,20 +497,14 @@ public class AlphabeticalAppsList { // Go through each section and try and merge some of the sections if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { - int minNumAppsPerRow = (int) Math.ceil(mNumAppsPerRow / 2f); int sectionAppCount = 0; for (int i = 0; i < mSections.size(); i++) { SectionInfo section = mSections.get(i); sectionAppCount = section.numApps; int mergeCount = 1; - // Merge rows if the last app in this section is in a column that is greater than - // 0, but less than the min number of apps per row. In addition, apply the - // constraint to stop merging if the number of rows in the section is greater than - // some limit, and also if there are no lessons to merge. - while (0 < (sectionAppCount % mNumAppsPerRow) && - (sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow && - (sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION && + // Merge rows based on the current strategy + while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) && (i + 1) < mSections.size()) { SectionInfo nextSection = mSections.remove(i + 1); @@ -465,10 +527,13 @@ public class AlphabeticalAppsList { } section.numApps += nextSection.numApps; sectionAppCount += nextSection.numApps; - mergeCount++; - if (mergeCount >= mMaxAllowableMerges) { - break; + + if (DEBUG) { + Log.d(TAG, "Merging: " + nextSection.firstAppItem.sectionName + + " to " + section.firstAppItem.sectionName + + " mergedNumRows: " + (sectionAppCount / mNumAppsPerRow)); } + mergeCount++; } } } diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index b8d30d081..8a5c6605e 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -213,7 +213,13 @@ public class AppsContainerView extends BaseContainerView implements DragSource, new AppsContainerSearchEditTextView.OnBackKeyListener() { @Override public void onBackKey() { - hideSearchField(true, true); + // Only hide the search field if there is no query, or if there + // are no filtered results + String query = Utilities.trim( + mSearchBarEditView.getEditableText().toString()); + if (query.isEmpty() || mApps.hasNoFilteredResults()) { + hideSearchField(true, true); + } } }); } @@ -277,15 +283,17 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } else { // If there are fixed bounds, then we update the padding to reflect the fixed bounds. setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, - mInsets.bottom); + mFixedBounds.bottom); } // Update the apps recycler view, inset it by the container inset as well + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + int startMargin = grid.isPhone() ? mContentMarginStart : 0; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; if (isRtl) { - mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset); + mAppsRecyclerView.setPadding(inset, inset, inset + startMargin, inset); } else { - mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset); + mAppsRecyclerView.setPadding(inset + startMargin, inset, inset, inset); } // Update the header bar diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 9ecb2eeb6..563044916 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -90,6 +90,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { return; } + DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); List items = mApps.getAdapterItems(); boolean hasDrawnPredictedAppDivider = false; int childCount = parent.getChildCount(); @@ -104,8 +105,6 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) { // Draw the divider under the predicted app - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid(). - getDeviceProfile(); int top = child.getTop() + child.getHeight(); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); @@ -113,7 +112,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint); hasDrawnPredictedAppDivider = true; - } else if (shouldDrawItemSection(holder, i, items)) { + } else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) { // At this point, we only draw sections for each section break; int viewTopOffset = (2 * child.getPaddingTop()); int pos = holder.getPosition(); @@ -132,7 +131,8 @@ class AppsGridAdapter extends RecyclerView.Adapter { continue; } - // Find the section code points + + // Find the section name bounds PointF sectionBounds = getAndCacheSectionBounds(sectionName); // Calculate where to draw the section diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java index 2a8443221..bd1c625e3 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -59,11 +59,12 @@ public class BaseContainerView extends FrameLayout implements Insettable { mFixedBounds.set(fixedBounds); if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { mFixedBounds.top = mInsets.top; - mFixedBounds.bottom = getMeasuredHeight(); + mFixedBounds.bottom = mInsets.bottom; } // To ensure that the child RecyclerView has the full width to handle touches right to // the edge of the screen, we only apply the top and bottom padding to the bounds - mFixedBounds.inset(0, mFixedBoundsContainerInset); + mFixedBounds.top += mFixedBoundsContainerInset; + mFixedBounds.bottom += mFixedBoundsContainerInset; onFixedBoundsUpdated(); } // Post the updates since they can trigger a relayout, and this call can be triggered from diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 16e4ce644..a289fce87 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -350,6 +350,9 @@ public class Launcher extends Activity private DeviceProfile mDeviceProfile; + // This is set to the view that launched the activity that navigated the user away from + // launcher. Since there is no callback for when the activity has finished launching, enable + // the press state and keep this reference to reset the press state when we return to launcher. private BubbleTextView mWaitingForResume; protected static HashMap sCustomAppWidgets = @@ -1021,10 +1024,12 @@ public class Launcher extends Activity if (mOnResumeState == State.WORKSPACE) { showWorkspace(false); } else if (mOnResumeState == State.APPS) { + boolean launchedFromApp = (mWaitingForResume != null); // Don't update the predicted apps if the user is returning to launcher in the apps - // view as they may be depending on the UI to be static to switch to another app + // view after launching an app, as they may be depending on the UI to be static to + // switch to another app, otherwise, if it was showAppsView(false /* animated */, false /* resetListToTop */, - false /* updatePredictedApps */); + !launchedFromApp /* updatePredictedApps */); } else if (mOnResumeState == State.WIDGETS) { showWidgetsView(false, false); } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 439227f52..f8d7d9256 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -373,7 +373,7 @@ public class WidgetsContainerView extends BaseContainerView } else { // If there are fixed bounds, then we update the padding to reflect the fixed bounds. setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, - mInsets.bottom); + mFixedBounds.bottom); } } -- cgit v1.2.3 From 44d0aacd5f44623ba87f1c8b3693d74df69d50e5 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 11 May 2015 18:02:55 -0700 Subject: Fixing issue with font size. Change-Id: I3f32840333f96ce49dfb915da3f335de0c0b022e --- src/com/android/launcher3/BubbleTextView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 009cc2ba5..46caf4ab9 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -100,9 +100,9 @@ public class BubbleTextView extends TextView { int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE); int defaultIconSize = grid.iconSizePx; if (display == DISPLAY_WORKSPACE) { - setTextSize(grid.iconTextSizePx); + setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); } else if (display == DISPLAY_ALL_APPS) { - setTextSize(grid.allAppsIconTextSizePx); + setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx); defaultIconSize = grid.allAppsIconSizePx; } -- cgit v1.2.3 From e2fba6cba10d9b7996ebb86dfd67bed96cff0ded Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 May 2015 10:39:59 -0700 Subject: Prevent work folder creation when launcher is upgraded. Change-Id: If7a91e0852dae1bf5dcf21e89c1771b5f37629f3 --- src/com/android/launcher3/LauncherProvider.java | 10 +++++++-- .../launcher3/util/ManagedProfileHeuristic.java | 26 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index f9f5ae1bb..8bc898855 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -52,6 +52,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.Thunk; import java.io.File; @@ -64,7 +65,7 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 24; + private static final int DATABASE_VERSION = 25; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -475,6 +476,9 @@ public class LauncherProvider extends ContentProvider { // Fresh and clean launcher DB. mMaxItemId = initializeMaxItemId(db); setFlagEmptyDbCreated(); + + // When a new DB is created, remove all previously stored managed profile information. + ManagedProfileHeuristic.processAllUsers(Collections.EMPTY_LIST, mContext); } private void addWorkspacesTable(SQLiteDatabase db) { @@ -620,7 +624,9 @@ public class LauncherProvider extends ContentProvider { } case 23: convertShortcutsToLauncherActivities(db); - case 24: { + case 24: + ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext); + case 25: { // DB Upgraded successfully return; } diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java index cefa71c39..ae67ba04f 100644 --- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java +++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java @@ -274,4 +274,30 @@ public class ManagedProfileHeuristic { keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial); keysOut.add(USER_FOLDER_ID_PREFIX + userSerial); } + + /** + * For each user, if a work folder has not been created, mark it such that the folder will + * never get created. + */ + public static void markExistingUsersForNoFolderCreation(Context context) { + UserManagerCompat userManager = UserManagerCompat.getInstance(context); + UserHandleCompat myUser = UserHandleCompat.myUserHandle(); + + SharedPreferences prefs = null; + for (UserHandleCompat user : userManager.getUserProfiles()) { + if (myUser.equals(user)) { + continue; + } + + if (prefs == null) { + prefs = context.getSharedPreferences( + LauncherFiles.MANAGED_USER_PREFERENCES_KEY, + Context.MODE_PRIVATE); + } + String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user); + if (!prefs.contains(folderIdKey)) { + prefs.edit().putLong(folderIdKey, ItemInfo.NO_ID).apply(); + } + } + } } -- cgit v1.2.3 From 1936ec048ca612f4651d6b2f18390bae8c4ac936 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 May 2015 11:03:04 -0700 Subject: Removing empty folders in loader Bug: 21050720 Change-Id: Id174bb1e18b070e32273d66649f0e286e3546f89 --- src/com/android/launcher3/LauncherModel.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3987c0207..658a3e287 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2331,6 +2331,22 @@ public class LauncherModel extends BroadcastReceiver return; } + // Remove any empty folder + LongArrayMap emptyFolders = sBgFolders.clone(); + for (ItemInfo item: sBgItemsIdMap) { + long container = item.container; + if (emptyFolders.containsKey(container)) { + emptyFolders.remove(container); + } + } + for (FolderInfo folder : emptyFolders) { + long folderId = folder.id; + sBgFolders.remove(folderId); + sBgItemsIdMap.remove(folderId); + sBgWorkspaceItems.remove(folder); + itemsToRemove.add(folderId); + } + if (itemsToRemove.size() > 0) { ContentProviderClient client = contentResolver.acquireContentProviderClient( contentUri); -- cgit v1.2.3 From 0e08f166fafd5e2a0d32e27ea510516e3f762221 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 May 2015 11:32:39 -0700 Subject: Using BubbleTextView for widget section headers > Loading highres icons on the worker thread. Change-Id: I5b68a86820aa4c132c156339e497354cf57f0fca --- res/layout/widgets_list_row_view.xml | 50 ++++++++++------------ res/values/attrs.xml | 1 + res/values/dimens.xml | 6 +-- src/com/android/launcher3/BubbleTextView.java | 29 ++++++++++--- src/com/android/launcher3/IconCache.java | 3 ++ .../launcher3/widget/WidgetsListAdapter.java | 17 ++------ 6 files changed, 56 insertions(+), 50 deletions(-) diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index ea95d2407..8bc8be44a 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -15,6 +15,7 @@ --> - - - - + android:gravity="start|center_vertical" + android:importantForAccessibility="no" + android:paddingBottom="@dimen/widget_section_vertical_padding" + android:paddingLeft="@dimen/widget_section_horizontal_padding" + android:paddingRight="@dimen/widget_section_horizontal_padding" + android:paddingTop="@dimen/widget_section_vertical_padding" + android:singleLine="true" + android:textColor="@color/widgets_view_section_text_color" + android:textSize="20sp" + launcher:customShadows="false" + launcher:deferShadowGeneration="true" + launcher:iconDisplay="widget_section" + launcher:iconSizeOverride="@dimen/widget_section_icon_size" + launcher:layoutHorizontal="true" /> + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 5447371a4..6280e5247 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -95,9 +95,9 @@ 8dp 56dp - 72dp - 8dp - 16dp + 40dp + 8dp + 16dp 146dp diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 46caf4ab9..d32c91919 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -28,13 +28,13 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.SparseArray; import android.util.TypedValue; -import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.widget.TextView; import com.android.launcher3.IconCache.IconLoadRequest; +import com.android.launcher3.widget.PackageItemInfo; /** * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan @@ -119,11 +119,6 @@ public class BubbleTextView extends TextView { mBackground = null; } - // If we are laying out horizontal, then center the text vertically - if (mLayoutHorizontal) { - setGravity(Gravity.CENTER_VERTICAL); - } - mLongPressHelper = new CheckLongPressHelper(this); mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); @@ -170,6 +165,20 @@ public class BubbleTextView extends TextView { verifyHighRes(); } + public void applyFromPackageItemInfo(PackageItemInfo info) { + setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize); + setText(info.title); + if (info.contentDescription != null) { + setContentDescription(info.contentDescription); + } + // We don't need to check the info since it's not a ShortcutInfo + super.setTag(info); + + // Verify high res immediately + verifyHighRes(); + } + + @Override protected boolean setFrame(int left, int top, int right, int bottom) { if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) { @@ -459,6 +468,8 @@ public class BubbleTextView extends TextView { } else if (info instanceof ShortcutInfo) { applyFromShortcutInfo((ShortcutInfo) info, LauncherAppState.getInstance().getIconCache()); + } else if (info instanceof PackageItemInfo) { + applyFromPackageItemInfo((PackageItemInfo) info); } } } @@ -483,6 +494,12 @@ public class BubbleTextView extends TextView { mIconLoadRequest = LauncherAppState.getInstance().getIconCache() .updateIconInBackground(BubbleTextView.this, info); } + } else if (getTag() instanceof PackageItemInfo) { + PackageItemInfo info = (PackageItemInfo) getTag(); + if (info.usingLowResIcon) { + mIconLoadRequest = LauncherAppState.getInstance().getIconCache() + .updateIconInBackground(BubbleTextView.this, info); + } } } } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 0596fbe16..fff07c6ed 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -374,6 +374,9 @@ public class IconCache { getTitleAndIcon(st, st.promisedIntent != null ? st.promisedIntent : st.intent, st.user, false); + } else if (info instanceof PackageItemInfo) { + PackageItemInfo pti = (PackageItemInfo) info; + getTitleAndIconForApp(pti.packageName, pti.user, false, pti); } mMainThreadExecutor.execute(new Runnable() { diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 8b0a43b2f..d114883ad 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -17,22 +17,21 @@ package com.android.launcher3.widget; import android.content.Context; import android.content.pm.ResolveInfo; +import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.Adapter; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; -import com.android.launcher3.compat.UserHandleCompat; import java.util.List; @@ -120,16 +119,8 @@ public class WidgetsListAdapter extends Adapter { // Bind the views in the application info section. PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(pos); - if (infoOut.usingLowResIcon) { - // TODO(hyunyoungs): call this in none UI thread in the same way as BubbleTextView. - mIconCache.getTitleAndIconForApp(infoOut.packageName, - UserHandleCompat.myUserHandle(), false /* useLowResIcon */, infoOut); - } - - TextView tv = ((TextView) holder.getContent().findViewById(R.id.section)); - tv.setText(infoOut.title); - ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image); - iv.setImageBitmap(infoOut.iconBitmap); + BubbleTextView tv = ((BubbleTextView) holder.getContent().findViewById(R.id.section)); + tv.applyFromPackageItemInfo(infoOut); // Bind the view in the widget horizontal tray region. for (int i=0; i < infoList.size(); i++) { -- cgit v1.2.3 From f044bb1edd8670fbc2cf9eeb1a51c31b91f0889c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 May 2015 13:30:07 -0700 Subject: Preventing NullPointerException when cancelling a shortcut addition Bug: 21024018 Change-Id: Iac09d9e4f1411aa9fe1ec89fbfe749b009c7d457 --- src/com/android/launcher3/Launcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a289fce87..8603a35df 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1377,7 +1377,9 @@ public class Launcher extends Activity mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); AppWidgetProviderInfo info = savedState.getParcelable( RUNTIME_STATE_PENDING_ADD_WIDGET_INFO); - mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this, info); + mPendingAddWidgetInfo = info == null ? + null : LauncherAppWidgetProviderInfo.fromProviderInfo(this, info); + mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID); setWaitingForResult(true); mRestoring = true; -- cgit v1.2.3 From dfb332f5b61cd15ec23c90d24de6d36dfc33f333 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 May 2015 14:03:15 -0700 Subject: Removing obsolete strings with no default translation Change-Id: I80613460450078ef5f1fd495c8dced3abf8d273f --- res/values-af/strings.xml | 43 ------------------------------------------- res/values-am/strings.xml | 43 ------------------------------------------- res/values-ar/strings.xml | 43 ------------------------------------------- res/values-be/strings.xml | 2 -- res/values-bg/strings.xml | 43 ------------------------------------------- res/values-bn-rBD/strings.xml | 43 ------------------------------------------- res/values-ca/strings.xml | 43 ------------------------------------------- res/values-cs/strings.xml | 43 ------------------------------------------- res/values-da/strings.xml | 43 ------------------------------------------- res/values-de/strings.xml | 43 ------------------------------------------- res/values-el/strings.xml | 43 ------------------------------------------- res/values-en-rGB/strings.xml | 43 ------------------------------------------- res/values-en-rIN/strings.xml | 43 ------------------------------------------- res/values-es-rUS/strings.xml | 43 ------------------------------------------- res/values-es/strings.xml | 43 ------------------------------------------- res/values-et-rEE/strings.xml | 43 ------------------------------------------- res/values-et/strings.xml | 38 -------------------------------------- res/values-eu-rES/strings.xml | 43 ------------------------------------------- res/values-fa/strings.xml | 43 ------------------------------------------- res/values-fi/strings.xml | 43 ------------------------------------------- res/values-fr-rCA/strings.xml | 43 ------------------------------------------- res/values-fr/strings.xml | 43 ------------------------------------------- res/values-gl-rES/strings.xml | 43 ------------------------------------------- res/values-hi/strings.xml | 43 ------------------------------------------- res/values-hr/strings.xml | 43 ------------------------------------------- res/values-hu/strings.xml | 43 ------------------------------------------- res/values-hy-rAM/strings.xml | 43 ------------------------------------------- res/values-in/strings.xml | 43 ------------------------------------------- res/values-is-rIS/strings.xml | 43 ------------------------------------------- res/values-it/strings.xml | 43 ------------------------------------------- res/values-iw/strings.xml | 43 ------------------------------------------- res/values-ja/strings.xml | 43 ------------------------------------------- res/values-ka-rGE/strings.xml | 43 ------------------------------------------- res/values-kk-rKZ/strings.xml | 43 ------------------------------------------- res/values-km-rKH/strings.xml | 43 ------------------------------------------- res/values-kn-rIN/strings.xml | 43 ------------------------------------------- res/values-ko/strings.xml | 43 ------------------------------------------- res/values-ky-rKG/strings.xml | 43 ------------------------------------------- res/values-lo-rLA/strings.xml | 43 ------------------------------------------- res/values-lt/strings.xml | 43 ------------------------------------------- res/values-lv/strings.xml | 43 ------------------------------------------- res/values-mk-rMK/strings.xml | 43 ------------------------------------------- res/values-ml-rIN/strings.xml | 43 ------------------------------------------- res/values-mn-rMN/strings.xml | 43 ------------------------------------------- res/values-mr-rIN/strings.xml | 43 ------------------------------------------- res/values-ms-rMY/strings.xml | 43 ------------------------------------------- res/values-ms/strings.xml | 38 -------------------------------------- res/values-my-rMM/strings.xml | 43 ------------------------------------------- res/values-nb/strings.xml | 43 ------------------------------------------- res/values-ne-rNP/strings.xml | 42 ------------------------------------------ res/values-nl/strings.xml | 43 ------------------------------------------- res/values-pl/strings.xml | 43 ------------------------------------------- res/values-pt-rPT/strings.xml | 43 ------------------------------------------- res/values-pt/strings.xml | 43 ------------------------------------------- res/values-rm/strings.xml | 2 -- res/values-ro/strings.xml | 43 ------------------------------------------- res/values-ru/strings.xml | 43 ------------------------------------------- res/values-si-rLK/strings.xml | 43 ------------------------------------------- res/values-sk/strings.xml | 43 ------------------------------------------- res/values-sl/strings.xml | 43 ------------------------------------------- res/values-sr/strings.xml | 43 ------------------------------------------- res/values-sv/strings.xml | 43 ------------------------------------------- res/values-sw/strings.xml | 43 ------------------------------------------- res/values-ta-rIN/strings.xml | 43 ------------------------------------------- res/values-te-rIN/strings.xml | 43 ------------------------------------------- res/values-th/strings.xml | 43 ------------------------------------------- res/values-tl/strings.xml | 43 ------------------------------------------- res/values-tr/strings.xml | 43 ------------------------------------------- res/values-uk/strings.xml | 43 ------------------------------------------- res/values-ur-rPK/strings.xml | 43 ------------------------------------------- res/values-uz-rUZ/strings.xml | 43 ------------------------------------------- res/values-vi/strings.xml | 43 ------------------------------------------- res/values-zh-rCN/strings.xml | 43 ------------------------------------------- res/values-zh-rHK/strings.xml | 43 ------------------------------------------- res/values-zh-rTW/strings.xml | 43 ------------------------------------------- res/values-zu/strings.xml | 43 ------------------------------------------- 76 files changed, 3175 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index ca860cfcc..0f90c4d82 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Tuis" - "Android-kernprogramme" "Program is nie geïnstalleer nie." "Program is nie beskikbaar nie" "Afgelaaide program in veiligmodus gedeaktiveer" "Legstukke gedeaktiveer in Veiligmodus" - "Legstukke" - "Legstukke" "Wys Mem" "Raak en hou om \'n legstuk op te tel." "%1$d × %2$d" - "Kan nie item op hierdie Tuisskerm laat los nie." - "Kies legstuk om te skep" - "Vouernaam" - "Hernoem vouer" - "OK" - "Kanselleer" - "Voeg by Tuisskerm" - "Programme" - "Kortpaaie" - "Legstukke" - "Niks meer spasie op jou Tuisskerms nie." "Niks meer spasie op die tuisskerm nie." "Geen plek meer in die Gunstelinge-laai nie" - "Hierdie legstuk is te groot vir die Gunstelinge-laai" - "Kortpad \"%s\" is geskep." - "Kortpad \"%s\" is verwyder." - "Kortpad \"%s\" bestaan reeds." - "Kies kortpad" - "Kies program" "Programme" "Tuis" - "Deïnstalleer" "Verwyder" "Deïnstalleer" "Programinligting" - "Programme" - "Verwyder" - "Deïnstalleer opdatering" - "Deïnstalleer program" - "Programbesonderhede" - "1 program gekies" - "1 legstuk gekies" - "1 vouer gekies" - "1 kortpad gekies" "installeer kortpaaie" "Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging." - "deïnstalleer kortpaaie" - "Laat die program toe om kortpaaie te verwyder sonder gebruikerinmenging." "lees Tuis-instellings en -kortpaaie" "Laat die program toe om die instellings en kortpaaie in Tuis te lees." "skryf Tuis-instellings en -kortpaaie" @@ -77,29 +45,18 @@ "Kon nie legstuk laai nie" "Stel op" "Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie." - "Vuurpyllanseerder" "Naamlose vouer" "Tuisskerm %1$d" "Bladsy %1$d van %2$d" "Tuisskerm %1$d van %2$d" - "Programme-bladsy %1$d van %2$d" - "Legstukke-bladsy %1$d van %2$d" "Welkom" - "Maak jouself tuis." - - - "Skep meer skerms vir programme en vouers" "Kopieer jou program-ikone" "Voer ikone en vouers vanaf jou ou tuisskerms in?" "KOPIEER IKONE" "BEGIN VAN NUUTS AF" - "Organiseer jou spasie" - "Raak en hou agtergrond om muurpapier, legstukke en instellings te bestuur." "Muurpapiere, legstukke en instellings" "Raak en hou agtergrond om te pasmaak" "HET DIT" - "Hier\'s \'n vouer" - "Om een soos dié te skep, raak en hou \'n program en skuif dit dan oor \'n ander een." "OK" "Vouer oopgemaak, %1$d by %2$d" "Raak om vouer toe te maak" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 4adf81187..52fb95e6c 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "ማስጀመሪያ3" "መነሻ" - "Android ዋና መተግበሪያዎች" "መተግበሪያ አልተጫነም።" "መተግበሪያ አይገኝም" "የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል" "ምግብሮች በደህንነቱ የተጠበቀ ሁኔታ ተሰናክለዋል" - "ፍርግሞች" - "ፍርግሞች" "ማህደረ ማስታወሻ አሳይ" "ፍርግም ለማንሳት ይንኩ እና ይያዙት" "%1$d × %2$d" - "ንጥሉን እዚህ የመነሻ ማያ ገጽ ላይ ማኖር አልተቻለም።" - "ለመፍጠር መግብር ይምረጡ" - "አቃፊ ስም" - "አቃፊ ዳግም ሰይም" - "እሺ" - "ይቅር" - "ወደ መነሻ ማያ ገጽ ያክሉ" - "መተግበሪያዎች" - "አቋራጮች" - "ፍርግሞች" - "የመነሻ ማያ ገጾችዎ ላይ ተጨማሪ ቦታ የለም።" "በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።" "በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም" - "ይህ መግብር ወደ የተወዳጆች መሣቢያ ላይ እንዳይገባ በጣም ትልቅ ነው" - "አቋራጭ «%s» ተፈጥሯል።" - "አቋራጭ «%s» ተወግዶ ነበር።" - "አቋራጭ «%s» አስቀድሞ አለ።" - "አቋራጭ ይምረጡ" - "መተግበሪያ ይምረጡ" "መተግበሪያዎች" "መነሻ" - "አራግፍ" "አስወግድ" "አራግፍ" "የመተግበሪያ መረጃ" - "መተግበሪያዎች" - "አስወግድ" - "ዝማኔ አራግፍ" - "መተግበሪያ አራግፍ" - "የመተግበሪያ ዝርዝሮች" - "1 መተግበሪያ ተመርጧል" - "1 ፍርግም ተመርጧል" - "1 አቃፊ ተመርጧል" - "1 አቋራጭ ተመርጧል" "አቋራጮችን ይጭናል" "መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።" - "አቋራጮችን ያራግፋል" - "መተግበሪያው አቋራጮችን ያለተጠቃሚ ጣልቃ ገብነት እንዲያስወግድ ያስችለዋል።" "የመነሻ ቅንብሮች እና አቋራጮችን ያነባል" "መተግበሪያው በመነሻ ውስጥ ያሉ ቅንብሮችን እና አቋራጮችን እንዲያነብ ያስችለዋል።" "የመነሻ ቅንብሮችን እና አቋራጮችን ይጽፋል" @@ -77,29 +45,18 @@ "ፍርግም የመጫን ችግር" "ማዋቀሪያ" "ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።" - "የሮኬት ማስጀመሪያ" "ስም-አልባ አቃፊ" "መነሻ ማያ ገጽ %1$d" "ገጽ %1$d ከ%2$d" "መነሻ ማያ ገጽ %1$d ከ%2$d" - "የመተግበሪያዎች ገጽ %1$d ከ%2$d" - "የመግብሮች ገጽ %1$d ከ%2$d" "እንኳን በደህና መጡ" - "ልክ እቤትዎ እንዳሉ ሆነው ዘና ይበሉ።" - - - "ለመተግበሪያዎች እና አቃፊዎች ተጨማሪ ማያ ገጾችን ይፍጠሩ" "የመተግበሪያ አዶዎችዎን ይቅዱ" "አዶዎች እና አቃፊዎች ከድሮው የመነሻ ማያ ገጾችዎ ይምጡ?" "አዶዎችን ይቅዱ" "እንደ አዲስ ይጀምሩ" - "ቦታዎን ያደራጁ" - "ልጣፍ ፣ ምግብሮችን እና ቅንብሮችን ለማቀናበር ጀርባውን ይንኩ እና ይያዙት።" "የግድግዳ ወረቀቶች፣ ንዑስ ፕሮግራሞች እና ቅንብሮች" "ለማበጀት ጀርባውን ነክተው ይያዙት" "ገባኝ" - "አንድ አቃፊ እነሆ" - "አንድ እንደዚህ አይነት ለመፍጠር መተግበሪያውን ነክተው ይያዙት እና ወደ ሌላ ያንቀሳቅሱት።" "እሺ" "አቃፊ ተከፍቷል፣ %1$d%2$d" "አቃፊን ለመዝጋት ይንኩ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index b5ffcf218..02e4ee862 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "الرئيسية" - "‏تطبيقات Android الأساسية" "لم يتم تثبيت التطبيق." "التطبيق ليس متاحًا" "تم تعطيل التطبيق الذي تم تنزيله في الوضع الآمن" "الأدوات معطلة في الوضع الآمن" - "الأدوات" - "الأدوات" "عرض الذاكرة" "المس مع الاستمرار لاختيار إحدى الأدوات." "%1$d × %2$d" - "تعذر إسقاط العنصر على هذه الشاشة الرئيسية." - "اختيار أداة لإنشائها" - "اسم المجلد" - "إعادة تسمية المجلد" - "موافق" - "إلغاء" - "إضافة إلى الشاشة الرئيسية" - "التطبيقات" - "الاختصارات" - "الأدوات" - "ليس هناك مساحة أخرى في الشاشات الرئيسية." "ليس هناك مساحة أخرى في هذه الشاشة الرئيسية." "لا يوجد المزيد من الحقول في علبة المفضلة" - "هذه الأداة كبيرة جدًا مما يحول دون قبولها في علبة المفضّلة" - "تم إنشاء الاختصار \"%s\"." - "تمت إزالة الاختصار \"%s\"." - "الاختصار \"%s\" موجود من قبل." - "اختيار اختصار" - "اختيار تطبيق" "التطبيقات" "الرئيسية" - "إزالة" "إزالة" "إزالة" "معلومات عن التطبيق" - "التطبيقات" - "إزالة" - "إزالة التحديث" - "إزالة التطبيق" - "تفاصيل التطبيق" - "تم تحديد تطبيق واحد" - "تم تحديد أداة واحدة" - "تم تحديد مجلد واحد" - "تم تحديد اختصار واحد" "تثبيت اختصارات" "للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم." - "إزالة الاختصارات" - "للسماح للتطبيق بإزالة الاختصارات بدون تدخل المستخدم." "قراءة إعدادات واختصارات الشاشة الرئيسية" "للسماح للتطبيق بقراءة الإعدادات والاختصارات في الشاشة الرئيسية." "كتابة إعدادات واختصارات الشاشة الرئيسية" @@ -77,29 +45,18 @@ "حدثت مشكلة أثناء تحميل الأداة" "الإعداد" "هذا تطبيق نظام وتتعذر إزالته." - "قاذفة صواريخ" "مجلد بدون اسم" "‏الشاشة الرئيسية %1$d" "‏الصفحة %1$d من %2$d" "‏الشاشة الرئيسية %1$d من %2$d" - "‏صفحة التطبيقات %1$d من %2$d" - "‏صفحة الأدوات %1$d من %2$d" "مرحبًا" - "تصرف على راحتك." - - - "إنشاء المزيد من الشاشات للتطبيقات والمجلدات" "نسخ رموز التطبيقات" "هل تريد استيراد رموز ومجلدات من الشاشات الرئيسية القديمة؟" "نسخ الرموز" "بداية جديدة" - "تنظيم مساحتك" - "المس مع الاستمرار الجزء الخلفي من صورة الشاشة لإدارة الخلفية والأدوات والإعدادات." "الخلفيات والأدوات والإعدادات" "المس مع الاستمرار الخلفية لتخصيصها" "حسنًا" - "إليك المجلد" - "لإنشاء مجلد مثل هذا، المس أحد التطبيقات مع استمرار اللمس، ثم حركه فوق آخر." "موافق" "تم فتح المجلد، بمقاس %1$d في %2$d" "المس لإغلاق المجلد" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 2d3c633c3..fc5390b80 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -173,8 +173,6 @@ - - diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index cb8f9ae96..cff5c783f 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Начало" - "Основни приложения на Android" "Приложението не е инсталирано." "Приложението не е налично" "Изтегленото приложение е деактивирано в безопасния режим" "Приспособленията са деактивирани в безопасния режим" - "Приспособления" - "Приспособления" "Показване на паметта" "Докоснете и задръжте за избор на приспособление." "%1$d × %2$d" - "Не можа да се премести на този начален екран." - "Избор на приспособл. за създаване" - "Име на папката" - "Преименуване на папка" - "ОK" - "Отказ" - "Добавяне към началния екран" - "Приложения" - "Преки пътища" - "Приспособления" - "На началните ви екрани няма повече място." "На този начален екран няма повече място." "Няма повече място в областта с любимите" - "Това приспособление е твърде голямо за областта с любимите" - "Прекият път към %s е създаден." - "Прекият път към %s бе премахнат." - "Прекият път към %s вече съществува." - "Избор на пряк път" - "Избор на приложение" "Приложения" "Начало" - "Деинсталиране" "Премахване" "Деинсталиране" "Информация за приложението" - "Приложения" - "Премахване" - "Деинст. на актуализацията" - "Деинсталиране на приложението" - "Подробности за приложението" - "Избрано е 1 приложение" - "Избрано е 1 приспособление" - "Избрана е 1 папка" - "Избран е 1 пряк път" "инсталиране на преки пътища" "Разрешава на приложението да добавя преки пътища без намеса на потребителя." - "деинсталиране на преки пътища" - "Разрешава на приложението да премахва преки пътища без намеса на потребителя." "четене на настройките и преките пътища в Начало" "Разрешава на приложението да чете настройките и преките пътища в Начало." "запис на настройките и преките пътища в Начало" @@ -77,29 +45,18 @@ "Проблем при зареждане на приспособлението" "Настройване" "Това е системно приложение и не може да се деинсталира." - "Ракетна площадка" "Папка без име" "Начален екран %1$d" "Страница %1$d от %2$d" "Начален екран %1$d от %2$d" - "Страница с приложения %1$d от %2$d" - "Страница с приспособления %1$d от %2$d" "Добре дошли" - "Персонализиране и приспособяване." - - - "Създаване на още екрани за приложения и папки" "Икони на прилож. ви: Копиране" "Да се импортират ли иконите и папките от старите ви начални екрани?" "КОПИРАНЕ НА ИКОНИТЕ" "СТАРТИРАНЕ ОТНАЧАЛО" - "Организиране на мястото ви" - "Докоснете и задръжте фона, за да управлявате тапета, приспособленията и настройките." "Тапети, приспособления и настройки" "Докоснете и задръжте фона за персонализиране" "РАЗБРАХ" - "Ето една папка" - "За да създадете подобна, докоснете и задръжте приложение, след което го преместете върху друго." "ОK" "Папката е отворена – %1$d на %2$d" "Докоснете, за да затворите папката" diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index 31dd461d2..14a93a21b 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "লঞ্চার৩" "হোম" - "Android প্রাথমিক অ্যাপ্লিকেশানগুলি" "অ্যাপ্লিকেশান ইনস্টল করা নেই৷" "অ্যাপ্লিকেশান অনুপলব্ধ" "ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে" "সুরক্ষিত মোডে উইজেট নিষ্ক্রিয় থাকে" - "উইজেটগুলি" - "উইজেটগুলি" "মেম দেখান" "একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷" "%1$d × %2$d" - "এই হোম স্ক্রীনে আইটেম রাখা যায়নি৷" - "তৈরি করেতে উইজেট চয়ন করুন" - "ফোল্ডারের নাম" - "ফোল্ডার পুনঃনামকরণ করুন" - "ঠিক আছে" - "বাতিল করুন" - "হোম স্ক্রীনে যোগ করুন" - "অ্যাপ্লিকেশানগুলি" - "শর্টকাটগুলি" - "উইজেটগুলি" - "আপনার হোম স্ক্রীনগুলিতে আর কোনো জায়গা নেই৷" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" "পছন্দসই ট্রে-তে আর কোনো জায়গা নেই" - "পছন্দসই ট্রে\'র জন্য এই উইজেটটি খুবই বড়" - "শর্টকাট \"%s\" তৈরি করা হয়েছে৷" - "শর্টকাট \"%s\" সরানো হয়েছে৷" - "শর্টকাট %s আগে থেকেই আছে৷" - "শর্টকাট চয়ন করুন" - "অ্যাপ্লিকেশান চয়ন করুন" "অ্যাপ্লিকেশানগুলি" "হোম" - "আনইনস্টল করুন" "সরান" "আনইনস্টল করুন" "অ্যাপ্লিকেশানের তথ্য" - "অ্যাপ্লিকেশানগুলি" - "সরান" - "আপডেট আনইনস্টল করুন" - "অ্যাপ্লিকেশান আনইনস্টল করুন" - "অ্যাপ্লিকেশানের বিশদ বিবরণ" - "১টি অ্যাপ্লিকেশান নির্বাচন করা হয়েছে" - "১টি উইজেট নির্বাচন করা হয়েছে" - "১টি ফোল্ডার নির্বাচন করা হয়েছে" - "১টি শর্টকাট নির্বাচন করা হয়েছে" "শর্টকাটগুলি ইনস্টল করে" "একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷" - "শর্টকাটগুলি আনইনস্টল করে" - "অ্যাপ্লিকেশানটিকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি সরানোর অনুমতি দেয়৷" "হোম সেটিংস এবং শর্টকাটগুলি পড়ে" "হোমে অ্যাপ্লিকেশানটিকে সেটিংস এবং শর্টকাটগুলি পড়তে দেয়৷" "হোম সেটিংস এবং শর্টকাটগুলি লেখে" @@ -77,29 +45,18 @@ "উইজেট লোড হতে সমস্যা হয়েছে" "সেটআপ" "এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷" - "রকেট লঞ্চার" "নামবিহীন ফোল্ডার" "%1$d নম্বর হোম স্ক্রীন" "%2$dটির মধ্যে %1$dটি পৃষ্ঠা" "%2$dটির %1$d নম্বর হোম স্ক্রীন" - "%2$dটির মধ্যে %1$dটি অ্যাপ্লিকেশান পৃষ্ঠা" - "%2$dটির মধ্যে %1$dটি উইজেট পৃষ্ঠা" "স্বাগতম" - "নিজের বাড়ির মতো স্বাচ্ছন্দ্য বোধ করুন৷" - - - "অ্যাপ্লিকেশান এবং ফোল্ডারগুলির জন্য আরো স্ক্রীn তৈরি করুন" "আপনার অ্যাপ্লিকেশান আইকনগুলি অনুলিপি করুন" "আপনার পুরানো হোম স্ক্রীন থেকে আইকন এবং ফোল্ডারগুলি আমদানি করবেন?" "আইকনগুলি অনুলিপি করুন" "নতুন করে শুরু করুন" - "আপনার স্থান সংগঠিত করুন" - "ওয়ালপেপার, উইজেট এবং সেটিংস পরিচালনা করতে পটভূমি স্পর্শ করে ধরে রাখুন৷" "ওয়ালপেপার, উইজেট এবং সেটিংস" "কাস্টমাইজ করার জন্য পটভূমি স্পর্শ করে ধরে থাকুন" "বুঝেছি" - "এখানে একটি ফোল্ডার আছে" - "এটির মতো একটি তৈরি করতে, একটি অ্যাপ্লিকেশান স্পর্শ করে ধরে রাখুন, এবং তারপরে এটিকে অন্য একটির উপরে সরিয়ে নিয়ে যান৷" "ঠিক আছে" "ফোল্ডার খোলা হয়েছে, %1$d বাই %2$d" "ফোল্ডার বন্ধ করতে স্পর্শ করুন" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index d466b5376..4d38c8164 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Inici" - "Aplicacions principals d\'Android" "L\'aplicació no s\'ha instal·lat." "L\'aplicació no està disponible." "L\'aplicació que has baixat està desactivada al mode segur." "En Mode segur, els widgets estan desactivats." - "Widgets" - "Widgets" "Mostra la memòria" "Mantén premut un widget per triar-lo." "%1$d × %2$d" - "No s\'ha pogut deixar anar l\'element a Inici." - "Tria el widget que vulguis crear" - "Nom de la carpeta" - "Canvi de nom de la carpeta" - "D\'acord" - "Cancel·la" - "Afegir a la pantalla d\'inici" - "Aplicacions" - "Dreceres" - "Widgets" - "No queda espai a les pantalles d\'inici." "Ja no queda espai en aquesta pantalla d\'inici." "No hi ha més espai a la safata Preferits." - "Aquest widget és massa gran per a la safata Preferits." - "S\'ha creat la drecera \"%s\"." - "S\'ha suprimit la drecera \"%s\"." - "La drecera \"%s\" ja existeix." - "Tria d\'una drecera" - "Tria d\'una aplicació" "Aplicacions" "Inici" - "Desinstal·la" "Suprimeix" "Desinstal·la" "Informació de l\'aplicació" - "Aplicacions" - "Suprimeix" - "Desinstal·la l\'actualització" - "Desinstal·la l\'aplicació" - "Detalls de l\'aplicació" - "1 aplicació seleccionada" - "1 widget seleccionat" - "1 carpeta seleccionada" - "1 drecera seleccionada" "instal·la dreceres" "Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari." - "desinstal·la dreceres" - "Permet que l\'aplicació suprimeixi dreceres sense la intervenció de l\'usuari." "llegeix la configuració i les dreceres de la pantalla d\'inici" "Permet que l\'aplicació llegeixi la configuració i les dreceres de la pantalla d\'inici." "escriu la configuració i les dreceres de la pantalla d\'inici" @@ -77,29 +45,18 @@ "S\'ha produït un problema en carregar el widget" "Configuració" "Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar." - "Rocket Launcher" "Carpeta sense nom" "Pantalla d\'inici %1$d" "Pàgina %1$d de %2$d" "Pantalla d\'inici %1$d de %2$d" - "Pàgina d\'aplicacions %1$d de %2$d" - "Pàgina de widgets %1$d de %2$d" "Us donem la benvinguda" - "Personalitza la pantalla d\'inici" - - - "Crea més pantalles per a aplicacions i carpetes" "Copiar les icones d\'aplicació" "Importar icones i carpetes de pantalles d\'inici anteriors?" "COPIA LES ICONES." "NOU COMENÇAMENT" - "Organitza el teu espai" - "Toca i mantén premut el fons per gestionar el fons de pantalla, els widgets i la configuració." "Fons de pantalla, widgets i configuració" "Mantén premut el fons per fer personalitzacions." "D\'ACORD" - "Aquí hi ha una carpeta" - "Per crear-ne una com aquesta, mantén premuda una aplicació i, a continuació, mou-la sobre una altra." "D\'acord" "S\'ha obert la carpeta, %1$d per %2$d" "Toca per tancar la carpeta" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 228c8fdb2..57598f70f 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Plocha" - "Android Core Apps" "Aplikace není nainstalována." "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" "V nouzovém režimu jsou widgety zakázány." - "Widgety" - "Widgety" "Zobrazit Mem" "Widget vyberete dotykem a podržením." "%1$d × %2$d" - "Na tuto plochu položku nelze přesunout." - "Vyberte widget k vytvoření" - "Název složky" - "Přejmenovat složku" - "OK" - "Zrušit" - "Přidat na plochu" - "Aplikace" - "Klávesové zkratky" - "Widgety" - "Na plochách již není místo." "Na této ploše již není místo." "Na panelu Oblíbené položky již není místo." - "Tento widget je pro panel Oblíbené položky příliš velký." - "Zástupce aplikace %s byl vytvořen." - "Zástupce aplikace %s byl odebrán." - "Zástupce aplikace %s již existuje." - "Výběr zástupce" - "Výběr aplikace" "Aplikace" "Plocha" - "Odinstalovat" "Odstranit" "Odinstalovat" "Informace o aplikaci" - "Aplikace" - "Odstranit" - "Odinstalovat aktualizaci" - "Odinstalovat aplikaci" - "Podrobnosti o aplikaci" - "Vybrána 1 aplikace" - "Vybrán 1 widget" - "Vybrána 1 složka" - "Vybrán 1 zástupce" "instalace zástupce" "Umožňuje aplikaci přidat zástupce bez zásahu uživatele." - "odinstalovat zástupce" - "Umožňuje aplikaci odstranit zástupce bez zásahu uživatele." "čtení nastavení a odkazů plochy" "Umožňuje aplikaci číst nastavení a odkazy na ploše." "zápis nastavení a odkazů plochy" @@ -77,29 +45,18 @@ "Problém s načtením widgetu" "Nastavení" "Toto je systémová aplikace a nelze ji odinstalovat." - "Rocket Launcher" "Složka bez názvu" "Plocha %1$d" "Strana %1$d z %2$d" "Plocha %1$d z %2$d" - "Stránka aplikací %1$d z %2$d" - "Stránka widgetů %1$d z %2$d" "Vítejte" - "Udělejte si pohodlí." - - - "Vytvořte několik obrazovek pro aplikace a složky" "Zkopírování ikon aplikací" "Chcete importovat ikony a složky ze svých starých ploch?" "ZKOPÍROVAT IKONY" "ZAČÍT S VÝCHOZÍM ROZVRŽENÍM" - "Organizace prostoru" - "Chcete-li spravovat tapetu, widgety a nastavení, dotkněte se pozadí a přidržte je." "Tapety, widgety a nastavení" "Pozadí můžete přizpůsobit klepnutím a podržením" "ROZUMÍM" - "Toto je složka" - "Chcete-li vytvořit složku, přetáhněte aplikaci na jinou aplikaci." "OK" "Složka otevřena, rozměry %1$d x %2$d" "Dotykem složku zavřete" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 25f22f7b5..36f345526 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Startskærm" - "Kerneapps i Android" "Appen er ikke installeret." "Appen er ikke tilgængelig" "Downloadet app er deaktiveret i sikker tilstand" "Widgets er deaktiveret i Beskyttet tilstand" - "Widgets" - "Widgets" "Vis Mem" "Tryk på en widget, og hold den nede for at vælge." "%1$d × %2$d" - "Elementet kunne ikke trækkes til startskærmen." - "Vælg den widget, du vil oprette" - "Mappenavn" - "Omdøb mappe" - "OK" - "Annuller" - "Føj til startskærm" - "Apps" - "Genveje" - "Widgets" - "Der er ikke mere plads på dine startskærme." "Der er ikke mere plads på denne startskærm." "Der er ikke mere plads i bakken Foretrukne" - "Denne widget er for stor til bakken Foretrukne" - "Genvejen \"%s\" blev oprettet." - "Genvejen \"%s\" blev fjernet." - "Genvejen \"%s\" findes allerede." - "Vælg genvej" - "Vælg app" "Apps" "Startskærm" - "Afinstaller" "Fjern" "Afinstaller" "Oplysninger om appen" - "Apps" - "Fjern" - "Afinstaller opdatering" - "Afinstaller appen" - "Oplysninger om appen" - "1 app er valgt" - "1 widget er valgt" - "1 mappe er valgt" - "1 genvej er valgt" "installere genveje" "Tillader, at en app tilføjer genveje uden brugerens indgriben." - "afinstaller genveje" - "Tillader, at appen fjerner genveje uden brugerens indgriben." "læs indstillinger og genveje for startskærmen" "Tillader, at appen læser indstillingerne og genvejene på startskærmen." "skrive indstillinger og genveje for startskærmen" @@ -77,29 +45,18 @@ "Der er problemer med indlæsning af widgetten" "Konfigurer" "Dette er en systemapp, som ikke kan afinstalleres." - "Rocket Launcher" "Unavngiven mappe" "Startskærm %1$d" "Side %1$d ud af %2$d" "Startskærm %1$d ud af %2$d" - "Apps-side %1$d ud af %2$d" - "Widgets-side %1$d ud af %2$d" "Velkommen" - "Føl dig hjemme." - - - "Opret flere skærme til apps og mapper" "Kopiér dine appikoner" "Vil du importere ikoner og mapper fra gamle startskærme?" "KOPIÉR IKONER" "START PÅ EN FRISK" - "Organiser din arbejdsplads" - "Tryk på en baggrund, og hold fingeren nede for at administrere baggrunde, widgets og indstillinger." "Baggrunde, widgets og indstillinger" "Tryk på baggrunden, og hold fingeren nede for at tilpasse den" "OK, FORSTÅET" - "Her kan du se en mappe" - "Du kan oprette en mappe magen til denne ved at trykke på en app og holde fingeren nede, mens du flytter appen til en anden mappe." "OK" "Mappen er åben, %1$d gange %2$d" "Tryk for at lukke mappen" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index af55808f9..1b9bc9e1b 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Startseite" - "Android Core Apps" "App ist nicht installiert." "App nicht verfügbar" "Heruntergeladene App im abgesicherten Modus deaktiviert" "Widgets im abgesicherten Modus deaktiviert" - "Widgets" - "Widgets" "Speicher anzeigen" "Zum Hinzufügen Widget berühren und halten" "%1$d × %2$d" - "Element wurde nicht auf diesem Startbildschirm abgelegt." - "Widget zum Erstellen auswählen" - "Ordnername" - "Ordner umbenennen" - "OK" - "Abbrechen" - "Zum Startbildschirm hinzufügen" - "Apps" - "Verknüpfungen" - "Widgets" - "Auf Ihren Startbildschirmen ist kein Platz mehr vorhanden." "Auf diesem Startbildschirm ist kein Platz mehr vorhanden." "Ablage \"Favoriten\" ist voll." - "Dieses Widget ist zu groß für die Ablage \"Favoriten\"." - "Verknüpfung \"%s\" wurde erstellt." - "Verknüpfung \"%s\" wurde entfernt." - "Verknüpfung \"%s\" ist bereits vorhanden." - "Verknüpfung auswählen" - "App auswählen" "Apps" "Startseite" - "Deinstallieren" "Entfernen" "Deinstallieren" "App-Info" - "Apps" - "Entfernen" - "Update deinstallieren" - "App deinstallieren" - "App-Details" - "1 App ausgewählt" - "1 Widget ausgewählt" - "1 Ordner ausgewählt" - "1 Verknüpfung ausgewählt" "Verknüpfungen installieren" "Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers" - "Verknüpfungen deinstallieren" - "Ermöglicht einer App das Entfernen von Verknüpfungen ohne Eingreifen des Nutzers" "Einstellungen und Verknüpfungen auf dem Startbildschirm lesen" "Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu lesen" "Einstellungen und Verknüpfungen für den Startbildschirm schreiben" @@ -77,29 +45,18 @@ "Problem beim Laden des Widgets" "Einrichten" "Dies ist eine Systemanwendung, die nicht deinstalliert werden kann." - "Rocket Launcher" "Unbenannter Ordner" "Startbildschirm %1$d" "Seite %1$d von %2$d" "Startbildschirm %1$d von %2$d" - "Apps-Seite %1$d von %2$d" - "Widgets-Seite %1$d von %2$d" "Hallo!" - "Gerät personalisieren" - - - "Mehr Bildschirme für Apps und Ordner erstellen" "App-Symbole kopieren" "Symbole und Ordner alter Startbildschirme importieren?" "Symbole kopieren" "Standardübersicht verwenden" - "Arbeitsbereich organisieren" - "Hintergrund berühren und halten, um Hintergrund, Widgets und Einstellungen zu verwalten" "Hintergründe, Widgets & Einstellungen" "Berühren und halten Sie den Hintergrund, um ihn anzupassen." "OK" - "Hier ist ein Ordner" - "Um einen Ordner zu erstellen, berühren und halten Sie eine App und verschieben Sie sie auf eine andere." "OK" "Ordner geöffnet, %1$d x %2$d" "Ordner durch Berühren schließen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 969904503..69a1e7794 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Αρχική σελίδα" - "Βασικές εφαρμογές Android" "Η εφαρμογή δεν έχει εγκατασταθεί." "Η εφαρμογή δεν είναι διαθέσιμη" "Η λήψη εφαρμογών απενεργοποήθηκε στην Ασφαλή λειτουργία" "Τα γραφικά στοιχεία απενεργοποιήθηκαν στην ασφαλή λειτουργία" - "Γραφικά στοιχεία" - "Γραφικά στοιχεία" "Εμφάνιση Mem" "Αγγίξτε παρατεταμένα για να πάρετε ένα γραφ.στοιχ." "%1$d × %2$d" - "Αδυναμία τοποθέτησης στοιχείου στην Αρχική οθόνη." - "Επιλ. γραφ. στοιχείο για δημιουργία" - "Όνομα φακέλου" - "Μετονομασία φακέλου" - "OK" - "Ακύρωση" - "Προσθήκη στην αρχική οθόνη" - "Εφαρμογές" - "Συντομεύσεις" - "Γραφικά στοιχεία" - "Δεν υπάρχει άλλος χώρος στις Αρχικές οθόνες σας." "Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη." "Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα" - "Αυτό το γραφικό στοιχείο είναι πολύ μεγάλο για την περιοχή Αγαπημένα." - "Δημιουργήθηκε η συντόμευση \"%s\"." - "Η συντόμευση \"%s\" καταργήθηκε." - "Η συντόμευση \"%s\" υπάρχει ήδη." - "Επιλογή συντόμευσης" - "Επιλογή εφαρμογής" "Εφαρμογές" "Αρχική σελίδα" - "Κατάργηση εγκατάστασης" "Κατάργηση" "Κατάργηση εγκατάστασης" "Πληροφορίες εφαρμογής" - "Εφαρμογές" - "Κατάργηση" - "Κατάργηση εγκατάστασης ενημέρωσης" - "Κατάργηση εγκατάστασης εφαρμογής" - "Λεπτομέρειες εφαρμογής" - "Επιλέχτηκε 1 εφαρμογή" - "Επιλέχτηκε 1 γραφικό στοιχείο" - "Επιλέχτηκε 1 φάκελος" - "Επιλέχτηκε 1 συντόμευση" "εγκατάσταση συντομεύσεων" "Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη." - "κατάργηση εγκατάστασης συντομεύσεων" - "Επιτρέπει στην εφαρμογή την κατάργηση συντομεύσεων χωρίς την παρέμβαση του χρήστη." "ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης" "Επιτρέπει στην εφαρμογή την ανάγνωση των ρυθμίσεων και των συντομεύσεων στην Αρχική οθόνη." "εγγραφή ρυθμίσεων και συντομεύσεων αρχικής οθόνης" @@ -77,29 +45,18 @@ "Παρουσιάστηκε πρόβλημα στη φόρτωση του γραφικού στοιχείου" "Ρύθμιση" "Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της." - "Rocket Launcher" "Φάκελος χωρίς όνομα" "Αρχική οθόνη %1$d" "Σελίδα %1$d από %2$d" "Αρχική οθόνη %1$d από %2$d" - "Σελίδα εφαρμογών %1$d από %2$d" - "Σελίδα γραφικών στοιχείων %1$d από %2$d" "Καλώς ορίσατε" - "Νιώστε σαν στο σπίτι σας." - - - "Δημιουργία περισσότερων οθονών για εφαρμογές και φακέλους" "Αντιγραφή εικονιδίων εφαρμογών" "Εισαγωγή εικονιδίων και φακέλων από παλιές αρχικές οθόνες;" "ΑΝΤΙΓΡΑΦΗ ΕΙΚΟΝΙΔΙΩΝ" "ΝΕΑ ΕΝΑΡΞΗ" - "Οργανώστε το χώρο σας" - "Αγγίξτε παρατεταμένα το φόντο για να διαχειριστείτε την ταπετσαρία, τα γραφικά στοιχεία και τις ρυθμίσεις." "Ταπετσαρίες, γραφικά στοιχεία και ρυθμίσεις" "Αγγίξτε παρατεταμένα το παρασκήνιο για προσαρμογή" "ΕΓΙΝΕ" - "Ορίστε ένας φάκελος" - "Για να δημιουργήσετε έναν φάκελο σαν κι αυτόν, πατήστε παρατεταμένα μια εφαρμογή και στη συνέχεια, μετακινήστε τη πάνω σε μια άλλη." "OK" "Άνοιγμα φακέλου, %1$d επί %2$d" "Αγγίξτε για να κλείσετε τον φάκελο" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 2af4666ca..cb25fd9be 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Home" - "Android Core Apps" "App isn\'t installed." "App isn\'t available" "Downloaded app disabled in Safe mode" "Widgets disabled in Safe mode" - "Widgets" - "Widgets" "Show Mem" "Touch & hold to pick up a widget." "%1$d × %2$d" - "Couldn\'t drop item on this Home screen." - "Choose widget to create" - "Folder name" - "Rename folder" - "OK" - "Cancel" - "Add to Home screen" - "Apps" - "Shortcuts" - "Widgets" - "No more room on your Home screens." "No more room on this Home screen." "No more room in the Favourites tray" - "This widget is too large for the Favourites tray" - "Shortcut \"%s\" created." - "Shortcut \"%s\" was removed." - "Shortcut \"%s\" already exists." - "Choose shortcut" - "Choose app" "Apps" "Home" - "Uninstall" "Remove" "Uninstall" "App info" - "Apps" - "Remove" - "Uninstall update" - "Uninstall app" - "App details" - "1 app selected" - "1 widget selected" - "1 folder selected" - "1 shortcut selected" "install shortcuts" "Allows an app to add shortcuts without user intervention." - "uninstall shortcuts" - "Allows the app to remove shortcuts without user intervention." "read Home settings and shortcuts" "Allows the app to read the settings and shortcuts in Home." "write Home settings and shortcuts" @@ -77,29 +45,18 @@ "Problem loading widget" "Setup" "This is a system app and can\'t be uninstalled." - "Rocket Launcher" "Unnamed Folder" "Home screen %1$d" "Page %1$d of %2$d" "Home screen %1$d of %2$d" - "Apps page %1$d of %2$d" - "Widgets page %1$d of %2$d" "Welcome" - "Make yourself at home." - - - "Create more screens for apps and folders" "Copy your app icons" "Import icons and folders from your old Home screens?" "COPY ICONS" "START AFRESH" - "Organise your space" - "Touch & hold background to manage wallpaper, widgets and settings." "Wallpapers, widgets, & settings" "Touch & hold background to customise" "GOT IT" - "Here\'s a folder" - "To create one like this, touch & hold an app, then move it over another." "OK" "Folder opened, %1$d by %2$d" "Touch to close folder" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 2af4666ca..cb25fd9be 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Home" - "Android Core Apps" "App isn\'t installed." "App isn\'t available" "Downloaded app disabled in Safe mode" "Widgets disabled in Safe mode" - "Widgets" - "Widgets" "Show Mem" "Touch & hold to pick up a widget." "%1$d × %2$d" - "Couldn\'t drop item on this Home screen." - "Choose widget to create" - "Folder name" - "Rename folder" - "OK" - "Cancel" - "Add to Home screen" - "Apps" - "Shortcuts" - "Widgets" - "No more room on your Home screens." "No more room on this Home screen." "No more room in the Favourites tray" - "This widget is too large for the Favourites tray" - "Shortcut \"%s\" created." - "Shortcut \"%s\" was removed." - "Shortcut \"%s\" already exists." - "Choose shortcut" - "Choose app" "Apps" "Home" - "Uninstall" "Remove" "Uninstall" "App info" - "Apps" - "Remove" - "Uninstall update" - "Uninstall app" - "App details" - "1 app selected" - "1 widget selected" - "1 folder selected" - "1 shortcut selected" "install shortcuts" "Allows an app to add shortcuts without user intervention." - "uninstall shortcuts" - "Allows the app to remove shortcuts without user intervention." "read Home settings and shortcuts" "Allows the app to read the settings and shortcuts in Home." "write Home settings and shortcuts" @@ -77,29 +45,18 @@ "Problem loading widget" "Setup" "This is a system app and can\'t be uninstalled." - "Rocket Launcher" "Unnamed Folder" "Home screen %1$d" "Page %1$d of %2$d" "Home screen %1$d of %2$d" - "Apps page %1$d of %2$d" - "Widgets page %1$d of %2$d" "Welcome" - "Make yourself at home." - - - "Create more screens for apps and folders" "Copy your app icons" "Import icons and folders from your old Home screens?" "COPY ICONS" "START AFRESH" - "Organise your space" - "Touch & hold background to manage wallpaper, widgets and settings." "Wallpapers, widgets, & settings" "Touch & hold background to customise" "GOT IT" - "Here\'s a folder" - "To create one like this, touch & hold an app, then move it over another." "OK" "Folder opened, %1$d by %2$d" "Touch to close folder" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index ab2e79f0a..36f32a722 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Pantalla principal" - "Aplicaciones básicas de Android" "No se instaló la aplicación." "La aplicación no está disponible." "Aplicación descargada inhabilitada en modo seguro" "Widgets inhabilitados en modo seguro" - "Widgets" - "Widgets" "Mostrar memoria" "Mantén presionado el widget que desees elegir." "%1$d × %2$d" - "Error al soltar elemento en la pantalla principal" - "Elegir los widgets para crear" - "Nombre de carpeta" - "Cambiar nombre de carpeta" - "Aceptar" - "Cancelar" - "Agregar a la pantalla principal" - "Aplicaciones" - "Accesos directos" - "Widgets" - "No hay más espacio en tus pantallas principales." "No hay más espacio en esta pantalla principal." "La bandeja de favoritos está llena." - "Este widget es demasiado grande para la bandeja de favoritos." - "Se creó el acceso directo \"%s\"." - "Se eliminó el acceso directo \"%s\"." - "El acceso directo \"%s\" ya existe." - "Elegir acceso directo" - "Elegir aplicación" "Aplicaciones" "Pantalla principal" - "Desinstalar" "Eliminar" "Desinstalar" "Información de la aplicación" - "Aplicaciones" - "Eliminar" - "Desinstalar actualización" - "Desinstalar aplicación" - "Detalles de la aplicación" - "Se seleccionó 1 aplicación." - "Se seleccionó 1 widget." - "Se seleccionó 1 carpeta." - "Se seleccionó 1 acceso directo." "instalar accesos directos" "Permite que una aplicación agregue accesos directos sin que el usuario intervenga." - "desinstalar accesos directos" - "Permite que la aplicación elimine accesos directos sin que el usuario intervenga." "leer configuración y accesos directos de la pantalla principal" "Permite que la aplicación lea la configuración y los accesos directos de la pantalla principal." "escribir configuración y accesos directos de la pantalla principal" @@ -77,29 +45,18 @@ "Problema al cargar el widget" "Configuración" "Esta es una aplicación del sistema y no se puede desinstalar." - "Lanzacohetes" "Carpeta sin nombre" "Pantalla principal %1$d" "Página %1$d de %2$d" "Pantalla principal %1$d de %2$d" - "Página de aplicaciones %1$d de %2$d" - "Página de widgets %1$d de %2$d" "Bienvenido" - "Siéntete como en casa." - - - "Crea más pantallas para aplicaciones y carpetas." "Copiar íconos de aplicaciones" "¿Importar íconos y carpetas de pant. principales antiguas?" "COPIAR ÍCONOS" "EMPEZAR DE CERO" - "Organiza tu espacio" - "Mantén presionado el fondo para administrar el fondo de pantalla, los widgets y la configuración." "Fondos, widgets y configuración" "Mantén presionado el fondo para personalizarlo" "ENTENDIDO" - "Aquí tienes una carpeta" - "Para crear una carpeta como esta, mantén presionada una aplicación y luego muévela sobre otra." "Aceptar" "Carpeta abierta, %1$d por %2$d" "Toca para cerrar la carpeta." diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 196db8a80..6dcfba4a4 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Home" - "Aplicaciones básicas de Android" "La aplicación no está instalada." "La aplicación no está disponible" "Aplicación descargada inhabilitada en modo seguro" "Widgets inhabilitados en modo seguro" - "Widgets" - "Widgets" "Mostrar memoria" "Mantén pulsado el widget que quieras seleccionar." "%1$d × %2$d" - "Error al arrastrar elemento a pantalla de inicio." - "Selecciona widget a añadir" - "Nombre de carpeta" - "Cambiar nombre de carpeta" - "Aceptar" - "Cancelar" - "Añadir a la pantalla de inicio" - "Aplicaciones" - "Accesos directos" - "Widgets" - "No queda espacio en las pantallas de inicio." "No queda espacio en la pantalla de inicio." "La bandeja de favoritos está completa" - "Este widget es demasiado grande para la bandeja de favoritos" - "Se ha creado el acceso directo \"%s\"." - "Se ha eliminado el acceso directo \"%s\"." - "El acceso directo \"%s\" ya existe." - "Selecciona un acceso directo" - "Selecciona una aplicación" "Aplicaciones" "Inicio" - "Desinstalar" "Eliminar" "Desinstalar" "Información de la aplicación" - "Aplicaciones" - "Eliminar" - "Desinstalar actualización" - "Desinstalar aplicación" - "Información de la aplicación" - "1 aplicación seleccionada" - "1 widget seleccionado" - "1 carpeta seleccionada" - "1 acceso directo seleccionado" "instalar accesos directos" "Permite que una aplicación añada accesos directos sin intervención del usuario." - "desinstalar accesos directos" - "Permite que la aplicación elimine accesos directos sin intervención del usuario." "leer información de accesos directos y de ajustes de la pantalla de inicio" "Permite que la aplicación consulte los ajustes y los accesos directos de la pantalla de inicio." "escribir información de accesos directos y de ajustes de la pantalla de inicio" @@ -77,29 +45,18 @@ "Problema al cargar el widget" "Configuración" "Esta aplicación es del sistema y no se puede desinstalar." - "Rocket Launcher" "Carpeta sin nombre" "Pantalla de inicio %1$d" "Página %1$d de %2$d" "Pantalla de inicio %1$d de %2$d" - "Página de aplicaciones %1$d de %2$d" - "Página de widgets %1$d de %2$d" "Te damos la bienvenida" - "Personaliza tu pantalla de inicio." - - - "Crea más pantallas para aplicaciones y carpetas" "Copiar iconos de aplicaciones" "¿Importar iconos y carpetas de pantallas de inicio antiguas?" "COPIAR ICONOS" "AJUSTES PREDETERMINADOS" - "Organiza tu espacio" - "Mantén pulsado el fondo para gestionar el fondo de pantalla, los widgets y los ajustes." "Fondos de pantalla, widgets y ajustes" "Mantén pulsado el fondo para personalizarlo" "ENTENDIDO" - "Esto es una carpeta" - "Para crear una carpeta como esta, mantén pulsada una aplicación y muévela sobre otra." "Aceptar" "Carpeta abierta, %1$d por %2$d" "Toca para cerrar la carpeta" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 044f10abd..c56bc6986 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Avaekraan" - "Androidi tuumrakendused" "Rakendus pole installitud." "Rakendus ei ole saadaval" "Allalaetud rakendus on turvarežiimis keelatud" "Turvarežiimis on vidinad keelatud" - "Vidinad" - "Vidinad" "Mälu kuvamine" "Vidina valimiseks vajutage ja hoidke seda all." "%1$d × %2$d" - "Üksust ei saanud sellele avaekraanile tuua." - "Valige loomiseks vidin" - "Kausta nimi" - "Kausta ümbernimetamine" - "OK" - "Tühista" - "Avaekraanile lisamine" - "Rakendused" - "Otseteed" - "Vidinad" - "Teie avaekraanidel ei ole enam ruumi." "Sellel avaekraanil pole enam ruumi." "Salves Lemmikud pole rohkem ruumi" - "See vidin on salve Lemmikud jaoks liiga suur" - "Otsetee „%s” on loodud." - "Otsetee „%s” on eemaldatud." - "Otsetee „%s” on juba olemas." - "Otsetee valimine" - "Rakenduse valimine" "Rakendused" "Avaekraan" - "Desinstalli" "Eemalda" "Desinstalli" "Rakenduse teave" - "Rakendused" - "Eemalda" - "Desinstalli värskendus" - "Rakenduse desinstallimine" - "Rakenduse üksikasjad" - "Valitud on 1 rakendus" - "Valitud on 1 vidin" - "Valitud on 1 kaust" - "Valitud on 1 otsetee" "installi otseteed" "Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta." - "desinstalli otseteed" - "Võimaldab rakendusel eemaldada otseteid kasutaja sekkumiseta." "loe avaekraani seadeid ja otseteid" "Võimaldab rakendusel lugeda avaekraanil seadeid ja otseteid." "kirjuta avaekraani seaded ja otseteed" @@ -77,29 +45,18 @@ "Probleem vidina laadimisel" "Seadistamine" "See on süsteemirakendus ja seda ei saa desinstallida." - "Rocket Launcher" "Nimetu kaust" "Avaekraan %1$d" "Leht %1$d/%2$d" "Avaekraan %1$d/%2$d" - "Rakenduste leht %1$d/%2$d" - "Vidinate leht %1$d/%2$d" "Tere tulemast" - "Tundke end nagu kodus." - - - "Looge rakenduste ja kaustade jaoks rohkem ekraanikuvasid" "Kopeerige rakenduste ikoonid" "Kas importida vanade avaekraanide ikoonid ja kaustad?" "KOPEERI IKOONID" "ALUSTA ALGUSEST" - "Korraldage oma ruumi" - "Taustapildi, vidinate ja seadete haldamiseks puudutage tausta ning hoidke seda all." "Taustapildid, vidinad ja seaded" "Kohandamiseks puudutage ja hoidke tausta all" "SELGE" - "Siin on kaust" - "Sarnase loomiseks vajutage ja hoidke rakendust all, seejärel viige see teise peale." "OK" "Kaust on avatud, %1$d x %2$d" "Puudutage kausta sulgemiseks" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index a4955ceda..bc9520675 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -21,69 +21,31 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Käivitaja" "Kodu" - "Androidi tuumrakendused" "Määra taustapilt" "Taustapildid" "Rakendus pole installitud." - "Vidinad" "Vidina valimiseks puudutage seda pikalt." "%1$d × %2$d" - "Üksust ei saa sellele avaekraanile tuua." - "Valige loomiseks vidin" - "Kausta nimi" - "Nimeta kaust ümber" - "OK" - "Tühista" - "Lisa avamenüüsse" - "Rakendused" - "Otseteed" - "Vidinad" - "Teie avakuvadel ei ole enam ruumi." "Sellel avalehel pole enam ruumi." "Kohandataval dokialal pole rohkem ruumi." - "See vidin on tööpunkti jaoks liiga suur." - "Otsetee „%s” loodud." - "Otsetee „%s” eemaldatud." - "Otsetee „%s” on juba olemas." - "Otsetee valimine" - "Rakenduse valimine" "Rakendused" "Kodu" - "Desinstalli" "Eemalda" "Desinstalli" "Rakenduse teave" - "Rakendused" - "Eemalda" - "Desinstalli värskendus" - "Rakenduse desinstallimine" - "Rakenduse üksikasjad" - "Valitud on 1 rakendus" - "1 vidin on valitud" - "1 kaust on valitud" - "1 otsetee on valitud" "otseteede installimine" "Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta." - "otseteede desinstallimine" - "Võimaldab rakendusel eemaldada otseteid kasutaja sekkumiseta." "avalehe seadete ja otseteede lugemine" "Võimaldab rakendusel lugeda avalehe seadeid ja otseteid." "avalehe seadete ja otseteede kirjutamine" "Võimaldab rakendusel muuta avalehel seadeid ja otseteid." "Probleem vidina laadimisel" "See on süsteemirakendus ja seda ei saa desinstallida." - "Rocket Launcher" "Nimeta kaust" "Avakuva %1$d" "Leht %1$d/%2$d" "Avakuva %1$d/%2$d" - "Rakenduste leht %1$d/%2$d" - "Vidinate leht %1$d/%2$d" - "Tunne end nagu kodus" - "Võite panna oma lemmikrakendused siia." - "Korraldage oma rakendused kaustadesse" - "Avakuval uue kausta tegemiseks virnastage üks rakendus teisele." "OK" "Kaust on avatud, %1$d x %2$d" "Puudutage kausta sulgemiseks" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 8cd151855..781f262ea 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Abiarazlea3" "Hasiera" - "Android-en nukleoko aplikazioak" "Aplikazioa instalatu gabe dago." "Ez dago erabilgarri aplikazioa" "Deskargatutako aplikazioa modu seguruan desgaitu da" "Widgetak desgaitu egin dira modu seguruan" - "Widgetak" - "Widgetak" "Erakutsi memoria" "Eduki ukituta widgeta aukeratzeko." "%1$d × %2$d" - "Ezin izan da elementua hasierako pantailan jaregin." - "Aukeratu sortu beharreko widgeta" - "Karpetaren izena" - "Aldatu karpetaren izena" - "Ados" - "Utzi" - "Gehitu hasierako pantailan" - "Aplikazioak" - "Lasterbideak" - "Widgetak" - "Hasierako pantailetan ez dago toki gehiago." "Hasierako pantaila honetan ez dago toki gehiago." "Ez dago toki gehiago Gogokoak erretiluan" - "Widgeta handiegia da Gogokoak erretiluan ezartzeko" - "\"%s\" lasterbidea sortu da." - "\"%s\" lasterbidea kendu da." - "\"%s\" lasterbidea lehendik dago." - "Aukeratu lasterbidea" - "Aukeratu aplikazioa" "Aplikazioak" "Hasiera" - "Desinstalatu" "Kendu" "Desinstalatu" "Aplikazioaren informazioa" - "Aplikazioak" - "Kendu" - "Desinstalatu eguneratzea" - "Desinstalatu aplikazioa" - "Aplikazioaren xehetasunak" - "Aplikazio bat hautatu da" - "Widget bat hautatu da" - "Karpeta bat hautatu da" - "Lasterbide bat hautatu da" "Instalatu lasterbideak" "Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei." - "Desinstalatu lasterbideak" - "Erabiltzaileak ezer egin gabe lasterbideak kentzea baimentzen die aplikazioei." "Irakurri hasierako ezarpenak eta lasterbideak" "Hasierako pantailako ezarpenak eta lasterbideak irakurtzea baimentzen die aplikazioei." "Idatzi hasierako ezarpenak eta lasterbideak" @@ -77,29 +45,18 @@ "Arazo bat izan da widgeta kargatzean" "Konfigurazioa" "Sistema-aplikazioa da hau eta ezin da desinstalatu." - "Rocket Launcher" "Izenik gabeko karpeta" "%1$d hasierako pantaila" "%1$d/%2$d orria" "%1$d/%2$d hasierako pantaila" - "%1$d/%2$d aplikazio-orria" - "%1$d/%2$d widget-orria" "Ongi etorri" - "Pertsonalizatu hasierako pantaila nahieran." - - - "Sortu pantaila gehiago aplikazioak eta karpetak ezartzeko" "Kopiatu aplikazioen ikonoak" "Ikonoak eta karpetak aurreko hasierako pantailatik inportatu?" "KOPIATU IKONOAK" "HASI HUTSETIK" - "Antolatu zure txokoa" - "Eduki ukituta atzeko planoa horma-paperak, widgetak eta ezarpenak kudeatzeko." "Horma-paperak, widgetak eta ezarpenak" "Pertsonalizatzeko, eduki ukituta atzeko planoa" "ADOS" - "Hortxe duzu karpeta" - "Horrelako bat sortzeko, eduki ukituta aplikazio bat eta eraman beste baten gainera." "Ados" "Karpeta ireki da: %1$d x %2$d" "Karpeta ixteko, uki ezazu" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 894b9f594..bf3a04f5c 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "صفحه اصلی" - "‏برنامه‌های Android Core" "برنامه نصب نشده است." "برنامه در دسترس نیست" "برنامه دانلود شده در حالت ایمن غیرفعال شد" "ابزارک‌ها در حالت ایمن غیرفعال هستند" - "ابزارک‌ها" - "ابزارک‌ها" "‏نمایش Mem" "برای انتخاب ابزارک لمس کنید و نگه دارید." "%1$d × %2$d" - "این مورد را نمی‌توان در این صفحه اصلی رها کرد." - "انتخاب ابزارکی که باید ایجاد شود" - "نام پوشه" - "تغییر نام پوشه" - "تأیید" - "لغو" - "افزودن به صفحه اصلی" - "برنامه‌ها" - "میان‌برها" - "ابزارک‌ها" - "فضای بیشتری در صفحات نمایش اصلی شما موجود نیست." "فضای بیشتری در این صفحه اصلی موجود نیست." "فضای بیشتری در سینی موارد دلخواه وجود ندارد" - "این ابزارک برای سینی موارد دلخواه بسیار بزرگ است" - "میان‌بر «%s» ایجاد شد." - "میان‌بر «%s» حذف شد." - "میان‌بر «%s» در حال حاضر وجود دارد." - "انتخاب میان‌بر" - "انتخاب برنامه" "برنامه‌ها" "صفحه اصلی" - "حذف نصب" "حذف" "حذف نصب" "اطلاعات برنامه" - "برنامه‌ها" - "حذف" - "حذف نصب به‌روزرسانی" - "حذف نصب برنامه" - "جزئیات برنامه" - "۱ برنامه انتخاب شد" - "۱ ابزارک انتخاب شد" - "۱ پوشه انتخاب شد" - "۱ میان‌بر انتخاب شد" "نصب میان‌برها" "به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند." - "حذف نصب میان‌برها" - "به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر حذف کند." "خواندن تنظیمات و میان‌برهای صفحه اصلی" "به برنامه اجازه می‌دهد تنظیمات و میان‌برها را در صفحه اصلی بخواند." "نوشتن تنظیمات و میان‌برهای صفحه اصلی" @@ -77,29 +45,18 @@ "مشکل در بارگیری ابزارک" "تنظیم" "این برنامه سیستمی است و حذف نصب نمی‌شود." - "Rocket Launcher" "پوشه بی‌نام" "‏صفحه اصلی %1$d" "‏صفحه %1$d از %2$d" "‏صفحه اصلی %1$d از %2$d" - "‏صفحه برنامه‌ها %1$d از %2$d" - "‏صفحه ابزارک‌ها %1$d از %2$d" "خوش آمدید" - "راحت باشید." - - - "صفحات بیشتری را برای برنامه‌ها و پوشه‌ها ایجاد کنید" "کپی کردن نمادهای برنامه شما" "نمادها و پوشه‌ها از صفحه‌های اصلی قدیمی شما وارد شوند؟" "کپی نمادها" "شروع تازه" - "فضای خود را سازماندهی کنید" - "برای مدیریت کاغذدیواری، ابزارک‌ها و تنظیمات، پس‌زمینه را لمس کرده و نگه‌دارید." "کاغذدیواری‌ها، ابزارک‌ها و تنظیمات" "برای سفارشی کردن، پس‌زمینه را لمس کنید و نگه‌دارید" "متوجه شدم" - "اینجا یک پوشه است" - "برای ایجاد پوشه‌ای مثل این، یک برنامه را لمس کرده و نگه‌دارید، سپس آن را روی برنامه دیگر بیاندازید." "تأیید" "پوشه باز شده، %1$d در %2$d" "برای بستن پوشه لمس کنید" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 455436b3f..da298be8c 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Aloitusruutu" - "Androidin ydinsovellukset" "Sovellusta ei ole asennettu." "Sovellus ei ole käytettävissä" "Ladattu sovellus poistettiin käytöstä suojatussa tilassa" "Widgetit poistettu käytöstä vikasietotilassa" - "Widgetit" - "Widgetit" "Näytä muisti" "Valitse widget painamalla sitä pitkään." "%1$d × %2$d" - "Kohteen lisääminen tähän aloitusruutuun epäonnistui." - "Valitse luotava widget" - "Kansion nimi" - "Nimeä kansio uudelleen" - "OK" - "Peruuta" - "Lisää aloitusruutuun" - "Sovellukset" - "Pikakuvakkeet" - "Widgetit" - "Aloitusruuduilla ei ole enää tilaa." "Tässä aloitusruudussa ei ole enää tilaa." "Suosikit-valikossa ei ole enää tilaa" - "Tämä widget on liian suuri Suosikit-valikkoon lisättäväksi" - "Pikakuvake %s luotiin." - "Pikakuvake %s poistettiin." - "Pikakuvake %s on jo olemassa." - "Valitse pikakuvake" - "Valitse sovellus" "Sovellukset" "Aloitusruutu" - "Poista" "Poista" "Poista" "Sovelluksen tiedot" - "Sovellukset" - "Poista" - "Poista päivitys" - "Poista sovellus" - "Sovelluksen tiedot" - "1 sovellus valittu" - "1 widget valittu" - "1 kansio valittu" - "1 pikakuvake valittu" "asenna pikakuvakkeita" "Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa." - "poista pikakuvakkeita" - "Antaa sovelluksen poistaa pikakuvakkeita ilman käyttäjän valintaa." "lue aloitusruudun asetuksia ja pikakuvakkeita" "Antaa sovelluksen lukea aloitusruudun asetukset ja pikakuvakkeet." "kirjoita aloitusruudun asetuksia ja pikakuvakkeita" @@ -77,29 +45,18 @@ "Ongelma ladattaessa widgetiä" "Asetus" "Tämä on järjestelmäsovellus, eikä sitä voi poistaa." - "Sinko" "Nimetön kansio" "Aloitusruutu %1$d" "Sivu %1$d / %2$d" "Aloitusruutu %1$d/%2$d" - "Apps-sivu %1$d / %2$d" - "Widgetit-sivu %1$d / %2$d" "Tervetuloa" - "Ole kuin kotonasi." - - - "Luo lisää ruutuja sovelluksille ja kansioille" "Kopioi sovelluskuvakkeet" "Tuodaanko kuvakkeet ja kansiot vanhoista aloitusruuduista?" "KOPIOI KUVAKKEET" "ALOITA ALUSTA" - "Järjestä tilasi" - "Hallitse taustakuvaa, widgetejä ja asetuksia koskettamalla taustaa pitkään." "Taustakuvat, widgetit ja asetukset" "Muokkaa taustaa koskettamalla ja painamalla pitkään" "SELVÄ" - "Tässä on kansio" - "Luo se seuraavasti: kosketa sovellusta pitkään ja siirrä se sitten toisen päälle." "OK" "Kansio avattu, koko %1$d x %2$d" "Sulje kansio koskettamalla" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index cee8a6aca..cc4d2a958 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Lanceur3" "Accueil" - "Applications de base Android" "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." "Widgets désactivés en mode sans échec" - "Widgets" - "Widgets" "Afficher la mémoire" "Maintenez un doigt sur le widget pour l\'ajouter." "%1$d × %2$d" - "Imposs. de déposer l\'élément sur l\'écran d\'accueil" - "Sélectionnez le widget à créer" - "Nom du dossier" - "Renommer le dossier" - "OK" - "Annuler" - "Ajouter à l\'écran d\'accueil" - "Applications" - "Raccourcis" - "Widgets" - "Vous n\'avez plus d\'espace libre sur vos écrans d\'accueil." "Pas d\'espace libre sur l\'écran d\'accueil." "Il n\'y a plus d\'espace dans la zone des favoris" - "Ce widget est trop volumineux pour la zone des favoris" - "Le raccourci « %s » a été créé." - "Le raccourci « %s » a été supprimé." - "Le raccourci « %s » existe déjà." - "Sélectionner un raccourci" - "Sélectionner une application" "Applications" "Accueil" - "Désinstaller" "Supprimer" "Désinstaller" "Détails de l\'application" - "Applications" - "Supprimer" - "Désinstaller la mise à jour" - "Désinstaller l\'application" - "Détails de l\'application" - "1 application sélectionnée" - "1 widget sélectionné" - "1 dossier sélectionné" - "1 raccourci sélectionné" "installer des raccourcis" "Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur." - "désinstaller des raccourcis" - "Permet à l\'application de supprimer des raccourcis sans l\'intervention de l\'utilisateur." "lire les paramètres et les raccourcis de la page d\'accueil" "Permet à l\'application de lire les paramètres et les raccourcis de l\'écran d\'accueil." "enregistrer les paramètres de la page d\'accueil et des raccourcis" @@ -77,29 +45,18 @@ "Problème lors du chargement du widget" "Configuration" "Impossible de désinstaller cette application, car il s\'agit d\'une application système." - "Lance-missile" "Dossier sans nom" "Écran d\'accueil %1$d" "Page %1$d sur %2$d" "Écran d\'accueil %1$d sur %2$d" - "Page des applications : %1$d sur %2$d" - "Page des widgets : %1$d sur %2$d" "Bienvenue" - "Faites comme chez vous." - - - "Créer plus d\'écrans pour les applications et les dossiers" "Copier les icônes de vos applis" "Importer les icônes et dossiers des anciens écrans d\'accueil?" "COPIER LES ICÔNES" "DISPOSITION PAR DÉFAUT" - "Organiser son espace personnel" - "Maintenez votre doigt sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres." "Fonds d\'écran, widgets et paramètres" "Maintenez le doigt sur le fond d\'écran pour personnaliser" "J\'ai compris" - "Voici un dossier" - "Pour créer un dossier comme ça, maintenez votre doigt sur une application, puis déplacez-la sur une autre." "OK" "Dossier ouvert, %1$d par %2$d" "Toucher pour fermer le dossier" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index adcc1e8b6..826e17251 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Accueil" - "Applications de base Android" "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." "Les widgets sont désactivés en mode sécurisé." - "Widgets" - "Widgets" "Afficher la mémoire" "App. de manière prolongée pour sélectionner widget." "%1$d x %2$d" - "Impossible de déposer élément sur écran d\'accueil." - "Sélectionner le widget à créer" - "Nom du dossier" - "Renommer le dossier" - "OK" - "Annuler" - "Ajouter à l\'écran d\'accueil" - "Applications" - "Raccourcis" - "Widgets" - "Vous n\'avez plus d\'espace libre sur vos écrans d\'accueil." "Pas d\'espace libre sur cet écran d\'accueil." "Plus d\'espace disponible dans la zone de favoris." - "Ce widget est trop volumineux pour la zone de favoris." - "Le raccourci \"%s\" a été créé." - "Le raccourci \"%s\" a été supprimé." - "Le raccourci \"%s\" existe déjà." - "Sélectionner un raccourci" - "Sélectionner une application" "Applications" "Accueil" - "Désinstaller" "Supprimer" "Désinstaller" "Informations sur l\'application" - "Applications" - "Supprimer" - "Désinstaller la mise à jour" - "Désinstaller l\'application" - "Informations sur l\'application" - "1 application sélectionnée" - "1 widget sélectionné" - "1 dossier sélectionné" - "1 raccourci sélectionné" "installer des raccourcis" "Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur" - "désinstaller des raccourcis" - "Permettre à l\'application de supprimer des raccourcis sans l\'intervention de l\'utilisateur" "lire les paramètres et les raccourcis de l\'écran d\'accueil" "Permettre à l\'application de lire les paramètres et les raccourcis de l\'écran d\'accueil" "modifier les paramètres et les raccourcis de l\'écran d\'accueil" @@ -77,29 +45,18 @@ "Problème lors du chargement du widget." "Configuration" "Impossible de désinstaller cette application, car il s\'agit d\'une application système." - "Rocket Launcher" "Dossier sans nom" "Écran d\'accueil %1$d" "Page %1$d sur %2$d" "Écran d\'accueil %1$d sur %2$d" - "Page des applications %1$d sur %2$d" - "Page des widgets %1$d sur %2$d" "Bienvenue" - "Familiarisez-vous avec l\'écran d\'accueil." - - - "Créez des écrans personnalisés pour vos applis et dossiers" "Copier les icônes de vos applis" "Importer les icônes et dossiers des anciens écrans d\'accueil ?" "COPIER LES ICÔNES" "DISPOSITION PAR DÉFAUT" - "Organisez votre espace" - "Appuyez de manière prolongée sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres." "Fonds d\'écran, widgets et paramètres" "Appuyez de manière prolongée sur l\'arrière-plan pour le personnaliser." "OK" - "Voici un dossier" - "Pour en créer un, appuyez de manière prolongée sur une application, puis déplacez-la vers une autre." "OK" "Dossier ouvert, %1$d par %2$d" "Appuyez pour fermer le dossier." diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index ed0eeb04a..8cd568654 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Inicio" - "Aplicacións básicas de Android" "A aplicación non está instalada" "A aplicación non está dispoñible" "A aplicación que descargaches está desactivada no modo seguro" "Os widgets están desactivados no modo seguro" - "Widgets" - "Widgets" "Mostrar memoria" "Mantén premido un widget para seleccionalo." "%1$d × %2$d" - "Non se puido engadir á pantalla de inicio." - "Escolle o widget que queiras crear" - "Nome do cartafol" - "Cambiar o nome do cartafol" - "Aceptar" - "Cancelar" - "Engadir á pantalla de inicio" - "Aplicacións" - "Atallos" - "Widgets" - "Non hai máis espazo nas pantallas de inicio." "Non hai máis espazo nesta pantalla de inicio." "Non hai máis espazo na bandexa de favoritos" - "Este widget é demasiado grande para a bandexa de favoritos" - "Creouse o atallo \"%s\"." - "Eliminouse o atallo \"%s\"." - "O atallo \"%s\" xa existe." - "Escoller un atallo" - "Escoller unha aplicación" "Aplicacións" "Inicio" - "Desinstalar" "Eliminar" "Desinstalar" "Información da aplicación" - "Aplicacións" - "Eliminar" - "Desinstalar actualización" - "Desinstalar aplicación" - "Detalles da aplicación" - "1 aplicación seleccionada" - "1 widget seleccionado" - "1 cartafol seleccionado" - "1 atallo seleccionado" "instalar atallos" "Permite a unha aplicación engadir atallos sen intervención do usuario." - "desinstalar atallos" - "Permite a unha aplicación eliminar atallos sen intervención do usuario." "ler a configuración e os atallos da pantalla de inicio" "Permite a unha aplicación ler a configuración e os atallos da páxina de inicio." "modificar a configuración e os atallos da pantalla de inicio" @@ -77,29 +45,18 @@ "Produciuse un problema ao cargar o widget" "Configuración" "Esta aplicación é do sistema e non se pode desinstalar." - "Lanzacohetes" "Cartafol sen nome" "Pantalla de inicio %1$d" "Páxina %1$d de %2$d" "Pantalla de inicio %1$d de %2$d" - "Páxina de aplicacións %1$d de %2$d" - "Páxina de widgets %1$d de %2$d" "Dámosche a benvida" - "Síntete como na túa casa." - - - "Crea máis pantallas para aplicacións e cartafoles" "Copiar iconas das aplicacións" "Queres importar as iconas e os cartafoles doutras pantallas de inicio?" "COPIAR ICONAS" "COMEZAR DE CERO" - "Organiza o espazo" - "Mantén premido o fondo para xestionar o fondo de pantalla e máis." "Fondos pantalla, widgets e configuración" "Mantén tocado o segundo plano para personalizar" "DE ACORDO" - "Isto é un cartafol" - "Para crear un igual, mantén premida a aplicación e móvea sobre outra." "Aceptar" "Abriuse o cartafol, %1$d por %2$d" "Toca para pechar o cartafol" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 995de4000..49feb66cb 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "होम" - "Android के मुख्य ऐप्लिकेशन" "एप्‍लिकेशन इंस्‍टॉल नहीं है." "ऐप्स उपलब्ध नहीं है" "डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है" "विजेट सुरक्षित मोड में अक्षम हैं" - "शॉर्टकट" - "शॉर्टकट" "मेमोरी दिखाएं" "विजेट को चुनने के लिए स्‍पर्श करके रखें." "%1$d × %2$d" - "आइटम को इस होम स्‍क्रीन पर नहीं छोड़ा जा सका." - "बनाने के लिए विजेट चुनें" - "फ़ोल्‍डर का नाम" - "फ़ोल्‍डर का नाम बदलें" - "ठीक" - "रहने दें" - "होम स्‍क्रीन में जोड़ें" - "ऐप्लिकेशन" - "शॉर्टकट" - "शॉर्टकट" - "आपकी होम स्‍क्रीन पर स्थान शेष नहीं है." "इस होम स्‍क्रीन पर स्थान शेष नहीं है." "पसंदीदा ट्रे में और स्थान नहीं है" - "पसंदीदा ट्रे के लिए यह विजेट बहुत ही बड़ा है" - "शॉर्टकट \"%s\" बनाया गया." - "शॉर्टकट \"%s\" निकाल दिया गया था." - "शॉर्टकट \"%s\" पहले से मौजूद है." - "शॉर्टकट चुनें" - "एप्‍लिकेशन चुनें" "ऐप्लिकेशन" "होम" - "अनइंस्टॉल करें" "निकालें" "अनइंस्टॉल करें" "ऐप्लिकेशन की जानकारी" - "ऐप्लिकेशन" - "निकालें" - "अपडेट अनइंस्‍टॉल करें" - "ऐप्लिकेशन अनइंस्‍टॉल करें" - "ऐप्लिकेशन का विवरण" - "1 एप्‍लिकेशन चयनित" - "1 विजेट चयनित" - "1 फ़ोल्‍डर चयनित" - "1 शॉर्टकट चयनित" "शॉर्टकट इंस्‍टॉल करें" "ऐप्लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है." - "शॉर्टकट अनइंस्टॉल करें" - "ऐप्लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट निकालने देती है." "होम सेटिंग और शॉर्टकट पढ़ें" "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट पढ़ने देती है." "होम सेटिंग और शॉर्टकट लिखें" @@ -77,29 +45,18 @@ "विजेट लोड करने में समस्‍या" "सेटअप" "यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता." - "रॉकेट लॉन्‍चर" "अनामित फ़ोल्डर" "होम स्‍क्रीन %1$d" "पृष्ठ %2$d में से %1$d" "होम स्क्रीन %2$d में से %1$d" - "ऐप्लिकेशन पृष्ठ %2$d में से %1$d" - "शॉर्टकट %2$d में से %1$d" "स्वागत है" - "जैसा चाहें वैसा उपयोग करें." - - - "ऐप्लिकेशन और फ़ोल्डर के लिए और अधिक स्क्रीन बनाएं" "ऐप्स आइकन की प्रतिलिपि बनाएं" "अपनी पुरानी होम स्क्रीन से आइकन और फ़ोल्डर आयात करें?" "आइकन की प्रतिलिपि बनाएं" "फिर से शुरू करें" - "अपने स्थान को व्यवस्थित करें" - "वॉलपेपर, शॉर्टकट और सेटिंग प्रबंधित करने के लिए पृष्ठभूमि को स्पर्श करके रखें." "वॉलपेपर, शॉर्टकट और सेटिंग" "पृष्ठभूमि कस्टमाइज़ करने के लिए स्पर्श करके रखें" "समझ लिया" - "यहां एक फ़ोल्डर है" - "इसके जैसा कोई एक बनाने के लिए, किसी ऐप्लिकेशन को स्पर्श करके रखें, फिर इसे किसी दूसरे पर ले जाएं." "ठीक" "फ़ोल्डर खोला गया, %1$d गुणा %2$d" "फ़ोल्‍डर बंद करने के लिए स्‍पर्श करें" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 5c3e86fde..c43d2f40b 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Pokretač3" "Početna" - "Matične aplikacije za Android" "Aplikacija nije instalirana." "Aplikacija nije dostupna" "Preuzeta aplikacija onemogućena je u Sigurnom načinu rada" "Widgeti su onemogućeni u Sigurnom načinu rada" - "Widgeti" - "Widgeti" "Prikaži mem" "Dodirnite i držite kako biste podigli widget." "%1$d × %2$d" - "Stavka nije ispuštena na ovaj početni zaslon." - "Odabir widgeta za stvaranje" - "Naziv mape" - "Preimenovanje mape" - "U redu" - "Odustani" - "Dodavanje na početni zaslon" - "Aplikacije" - "Prečaci" - "Widgeti" - "Na vašim početnim zaslonima više nema mjesta." "Na ovom početnom zaslonu više nema mjesta." "Nema više prostora na traci Favoriti" - "Widget je prevelik za traku Favoriti" - "Izrađen je prečac za \"%s\"." - "Uklonjen je prečac za \"%s\"." - "Prečac za \"%s\" već postoji." - "Odabir prečaca" - "Odabir aplikacije" "Aplikacije" "Početna" - "Deinstaliraj" "Ukloni" "Deinstaliraj" "Informacije o aplikaciji" - "Aplikacije" - "Ukloni" - "Deinstalacija ažuriranja" - "Deinstaliranje aplikacije" - "Pojedinosti o aplikaciji" - "Odabrana je 1 aplikacija" - "Odabran je 1 widget" - "Odabrana je 1 mapa" - "Odabran je 1 prečac" "instaliranje prečaca" "Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika." - "deinstaliranje prečaca" - "Aplikaciji omogućuje uklanjanje prečaca bez intervencije korisnika." "čitanje postavki početnog zaslona i prečaca" "Aplikaciji omogućuje čitanje postavki i prečaca na početnom zaslonu." "pisanje postavki početnog zaslona i prečaca" @@ -77,29 +45,18 @@ "Problem pri učitavanju widgeta" "Postavljanje" "Ovo je aplikacija sustava i ne može se ukloniti." - "Lansirna rampa" "Neimenovana mapa" "Početni zaslon %1$d" "Stranica %1$d od %2$d" "Početni zaslon %1$d od %2$d" - "Stranica aplikacija %1$d od %2$d" - "Stranica widgeta %1$d od %2$d" "Dobro došli" - "Osjećajte se kao kod kuće." - - - "Izradite više zaslona za aplikacije i mape" "Kopiranje ikona aplikacija" "Želite li uvesti ikone i mape sa starih početnih zaslona?" "KOPIRAJ IKONE" "POKRENI NOVO" - "Organizirajte svoj prostor" - "Dodirnite i držite pozadinu da biste upravljali pozadinskom slikom, widgetima i postavkama." "Pozadinske slike, widgeti i postavke" "Dodirnite i zadržite pozadinu radi prilagodbe" "SHVAĆAM" - "Evo mape" - "Da biste izradili ovakvu mapu, dodirnite i držite aplikaciju pa je pomaknite preko druge aplikacije." "U redu" "Mapa je otvorena, %1$d x %2$d" "Dodirnite da biste zatvorili mapu" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 0ee2748e2..ef804a4d8 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Főoldal" - "Alap Android-alkalmazások" "Az alkalmazás nincs telepítve." "Az alkalmazás nem érhető el" "A letöltött alkalmazás Csökkentett módban ki van kapcsolva" "A modulok ki vannak kapcsolva Csökkentett módban" - "Modulok" - "Modulok" "Mem. megjelenítése" "Modul felvételéhez érintse meg, és tartsa lenyomva" "%1$d × %2$d" - "Nem lehet elemeket dobni erre a kezdőképernyőre." - "A létrehozáshoz válasszon modult" - "Mappa neve" - "Mappa átnevezése" - "OK" - "Mégse" - "Hozzáadás a kezdőképernyőhöz" - "Alkalmazások" - "Parancsikonok" - "Modulok" - "Nincs több hely a kezdőképernyőkön." "Nincs több hely ezen a kezdőképernyőn." "Nincs több hely a Kedvencek tálcán" - "Ez a modul túl nagy a Kedvencek tálcán való elhelyezéshez" - "A(z) „%s” parancsikon létrehozva." - "A(z) „%s” parancsikon eltávolítva." - "A(z) „%s” parancsikon már létezik." - "Parancsikon választása" - "Válasszon alkalmazást" "Alkalmazások" "Főoldal" - "Eltávolítás" "Eltávolítás" "Eltávolítás" "Alkalmazásinformáció" - "Alkalmazások" - "Eltávolítás" - "Eltávolítja a frissítést" - "Alkalmazás eltávolítása" - "Az alkalmazás adatai" - "1 alkalmazás kiválasztva" - "1 modul kiválasztva" - "1 mappa kiválasztva" - "1 parancsikon kiválasztva" "parancsikonok telepítése" "Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat." - "parancsikonok eltávolítása" - "Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül távolítson el parancsikonokat." "Főoldal beállításainak és parancsikonjainak beolvasása" "Lehetővé teszi az alkalmazás számára, hogy beolvassa a kezdőképernyő beállításait és parancsikonjait." "Főoldal beállításainak és parancsikonjainak írása" @@ -77,29 +45,18 @@ "Probléma történt a modul betöltésekor" "Beállítás" "Ez egy rendszeralkalmazás, és nem lehet eltávolítani." - "Aknavető" "Névtelen mappa" "%1$d. kezdőképernyő" "%2$d/%1$d. oldal" "%2$d/%1$d. kezdőképernyő" - "%2$d/%1$d. alkalmazásoldal" - "%2$d/%1$d. moduloldal" "Üdvözöljük!" - "Varázsolja egyedivé készülékét." - - - "Hozzon létre további képernyőket az alkalmazásoknak és mappáknak" "Alkalmazásikonok másolása" "Importálja ikonjait és mappáit régi kezdőképernyőiről?" "IKONOK MÁSOLÁSA" "TELJESEN ÚJ" - "Munkaterület rendezése" - "Érintse meg és tartsa lenyomva a hátteret a háttérkép, modulok és beállítások kezeléséhez." "Háttérképek, modulok és beállítások" "Érintse meg és tartsa lenyomva a személyre szabáshoz" "MEGÉRTETTEM" - "Itt egy mappa" - "Mappa létrehozásához érintse meg és tartsa lenyomva az alkalmazást, majd húzza egy másik fölé." "OK" "Mappa megnyitva – szélesség: %1$d; magasság: %2$d" "Érintse meg a mappa bezárásához" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index 73c066837..cbc5585da 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Հիմնական" - "Android Core Apps" "Ծրագիրը տեղադրված չէ:" "Հավելվածը հասանելի չէ" "Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում" "Վիջեթներն անջատված են անվտանգ ռեժիմում" - "Վիջեթներ" - "Վիջեթներ" "Ցուցադրել մեմը" "Հպեք և պահեք՝ վիջեթն ընտրելու համար:" "%1$d × %2$d" - "Հնարավոր չէ տեղադրել տարրն այս հիմնական էկրանին:" - "Ստեղծելու համար ընտրեք վիջեթը" - "Թղթապանակի անունը" - "Վերանվանել թղթապանակը" - "Լավ" - "Չեղարկել" - "Ավելացնել հիմնական էկրանին" - "Ծրագրեր" - "Դյուրանցումներ" - "Վիջեթներ" - "Այլևս տեղ չկա ձեր հիմնական էկրաններին:" "Այլևս տեղ չկա այս հիմնական էկրանին:" "Ընտրյալների ցուցակում այլևս ազատ տեղ չկա" - "Այս վիջեթը շատ մեծ է Ընտրյալների ցուցակի համար" - %s» դյուրանցումը ստեղծված է:" - %s» դյուրանցումը հեռացվեց:" - %s» դյուրանցումն արդեն գոյություն ունի:" - "Ընտրել դյուրանցումը" - "Ընտրել ծրագիրը" "Ծրագրեր" "Հիմնական" - "Ապատեղադրել" "Հեռացնել" "Ապատեղադրել" "Ծրագրի տեղեկություններ" - "Ծրագրեր" - "Հեռացնել" - "Ապատեղադրել թարմացումը" - "Ապատեղադրել ծրագիրը" - "Ծրագրի մանրամասներ" - "1 ընտրված ծրագիր" - "1 ընտրված վիջեթ" - "1 ընտրված թղթապանակ" - "1 ընտրված դյուրանցում" "տեղադրել դյուրանցումներ" "Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:" - "ապատեղադրել դյուրանցումները" - "Ծրագրին թույլ է տալիս հեռացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:" "կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները" "Ծրագրին թույլ է տալիս կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները:" "ստեղծել հիմնաէջի կարգավորումներ ու դյուրանցումներ" @@ -77,29 +45,18 @@ "Վիջեթի բեռնման խնդիր կա" "Կարգավորում" "Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:" - "Հրթիռային թողարկիչ" "Անանուն թղթապանակ" "Հիմնական էկրան %1$d" "Էջ %1$d՝ %2$d-ից" "Հիմնական էկրան %1$d` %2$d-ից" - "Ծրագրերի էջերը՝ %1$d %2$d-ից" - "Վիջեթների էջերը՝ %1$d %2$d-ից" "Բարի գալուստ" - "Զգացեք ձեզ ինչպես տանը:" - - - "Ստեղծեք նոր էկրաններ ծրագրերի և թղթապանակների համար" "Պատճենել ձեր ծրագրի պատկերակները" "Ներմուծե՞լ պատկերակները և թղթապանակները ձեր նախկին Հիմնական էկրանից" "ՊԱՏՃԵՆԵԼ ՊԱՏԿԵՐԱԿՆԵՐԸ" "ՄԵԿՆԱՐԿԵԼ ԸՍՏ ԿԱՆԽԱԴՐՎԱԾԻ" - "Կառավարեք ձեր տարածությունը" - "Հպեք և պահեք հետնաշերտի վրա՝ պաստառները, վիջեթներն ու կարգավորումները կառավարելու համար:" "Պաստառներ, վիջեթներ և կարգավորումներ" "Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա" "ՀԱՍԿԱՆԱԼԻ Է" - "Ահա մի թղթապանակ" - "Նման թղթապանակ ստեղծելու համար հպեք և պահեք որևէ ծրագրի վրա, ապա տեղաշարժեք այն մեկ ուրիշ ծրագրի վրա:" "Լավ" "Թղթապանակը բաց է, %1$d-ից %2$d" "Հպեք՝ թղթապանակը փակելու համար" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 8af1d2344..ea9ebb594 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Layar Utama" - "Aplikasi Inti Android" "Aplikasi tidak dipasang." "Aplikasi tidak tersedia" "Aplikasi yang diunduh dinonaktifkan dalam mode Aman" "Widget dinonaktifkan dalam mode Aman" - "Widget" - "Widget" "Tampilkan Memori" "Sentuh lama untuk memilih widget." "%1$d × %2$d" - "Tidak dapat melepas item ke layar Utama ini." - "Pilih widget untuk membuat" - "Nama folder" - "Ganti nama folder" - "Oke" - "Batal" - "Tambahkan ke layar Utama" - "Aplikasi" - "Pintasan" - "Widget" - "Tidak ada ruang lagi di layar Utama Anda." "Tidak ada ruang lagi pada layar Utama ini." "Tidak ada ruang tersisa di baki Favorit" - "Widget ini terlalu besar untuk baki Favorit" - "Pintasan \"%s\" sudah dibuat." - "Pintasan \"%s\" telah dihapus." - "Pintasan \"%s\" sudah ada." - "Pilih pintasan" - "Pilih aplikasi" "Aplikasi" "Layar Utama" - "Copot pemasangan" "Hapus" "Copot pemasangan" "Info aplikasi" - "Aplikasi" - "Hapus" - "Copot pemasangan pembaruan" - "Copot aplikasi" - "Detail aplikasi" - "1 aplikasi dipilih" - "1 widget dipilih" - "1 folder dipilih" - "1 pintasan dipilih" "memasang pintasan" "Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna." - "mencopot pemasangan pintasan" - "Mengizinkan aplikasi menghapus pintasan tanpa campur tangan pengguna." "membaca setelan dan pintasan layar Utama" "Mengizinkan aplikasi membaca setelan dan pintasan di layar Utama." "menulis setelan dan pintasan layar Utama" @@ -77,29 +45,18 @@ "Masalah memuat widget" "Siapkan" "Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya." - "Rocket Launcher" "Folder Tanpa Nama" "Layar utama %1$d" "Laman %1$d dari %2$d" "Layar utama %1$d dari %2$d" - "Laman aplikasi %1$d dari %2$d" - "Laman widget %1$d dari %2$d" "Selamat Datang" - "Serasa di rumah sendiri." - - - "Buat lebih banyak layar untuk aplikasi dan folder" "Salin ikon aplikasi Anda" "Impor ikon dan folder dari layar Utama lama Anda?" "IKON SALIN" "MULAI DARI AWAL" - "Kelola ruang Anda" - "Sentuh lama latar belakang untuk mengelola wallpaper, widget, dan setelan." "Wallpaper, widget, & setelan" "Sentuh & tahan latar belakang untuk menyesuaikan" "MENGERTI" - "Ini adalah folder" - "Untuk membuat seperti yang ini, sentuh lama aplikasi, lalu pindahkan ke atas aplikasi lain." "Oke" "Folder dibuka, %1$d x %2$d" "Sentuh untuk menutup folder" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index bf0b0ca25..41e43b597 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Heim" - "Kjarnaforrit Android" "Forritið er ekki uppsett." "Forritið er ekki í boði" "Sótt forrit er óvirkt í öryggisstillingu" "Græjur eru óvirkar í öruggri stillingu" - "Græjur" - "Græjur" "Sýna minni" "Haltu fingri á græju til að grípa hana." "%1$d × %2$d" - "Ekki er hægt að sleppa atriði á þennan heimaskjá." - "Veldu græju til að búa til" - "Möppuheiti" - "Endurnefna möppu" - "Í lagi" - "Hætta við" - "Bæta á heimaskjá" - "Forrit" - "Flýtileiðir" - "Græjur" - "Heimaskjáirnir þínir eru fullskipaðir." "Ekki meira pláss á þessum heimaskjá." "Ekki meira pláss í bakka fyrir uppáhald" - "Þessi græja er of stór fyrir bakkann fyrir uppáhald" - "Flýtileiðin „%s“ var búin til." - "Flýtileiðin „%s“ var fjarlægð." - "Flýtileiðin „%s“ er þegar til." - "Veldu flýtileið" - "Veldu forrit" "Forrit" "Heim" - "Eyða" "Fjarlægja" "Eyða" "Upplýsingar um forrit" - "Forrit" - "Fjarlægja" - "Fjarlægja uppfærslu" - "Fjarlægja forrit" - "Upplýsingar um forrit" - "1 forrit valið" - "1 græja valin" - "1 mappa valin" - "1 flýtileið valin" "setja upp flýtileiðir" "Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda." - "fjarlægja flýtileiðir" - "Leyfir forriti að fjarlægja flýtileiðir án íhlutunar notanda." "lesa stillingar og flýtileiðir heimaskjás" "Leyfir forriti að lesa stillingar og flýtileiðir heimaskjás." "skrifa stillingar og flýtileiðir heimaskjás" @@ -77,29 +45,18 @@ "Vandamál við að hlaða græju" "Uppsetning" "Þetta er kerfisforrit sem ekki er hægt að fjarlægja." - "Eldflaugapallur" "Ónefnd mappa" "Heimaskjár %1$d" "Síða %1$d af %2$d" "Heimaskjár %1$d af %2$d" - "Forritasíða %1$d af %2$d" - "Græjusíða %1$d af %2$d" "Komdu fagnandi" - "Komdu þér vel fyrir." - - - "Búðu til fleiri skjái fyrir forrit og möppur" "Afritaðu forritatáknin þín" "Flytja inn tákn og möppur af eldri heimaskjáum?" "AFRITA TÁKN" "BYRJA UPP Á NÝTT" - "Settu hlutina á sína staði" - "Haltu inni á bakgrunni til að stjórna veggfóðri, græjum og stillingum." "Veggfóður, græjur og stillingar" "Haltu fingri á bakgrunninum til að sérsníða hann" "ÉG SKIL" - "Hér er mappa" - "Til að búa til svona skaltu draga forrit yfir á annað forrit." "Í lagi" "Mappa opnuð, %1$d sinnum %2$d" "Snertu til að loka möppunni" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index c08ab6b75..6307fff8f 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Home page" - "Applicazioni di base Android" "App non installata." "App non disponibile" "L\'app scaricata è stata disattivata in modalità provvisoria" "Widget disabilitati in modalità provvisoria" - "Widget" - "Widget" "Mostra Mem" "Tocca e tieni premuto per scegliere un widget." "%1$d × %2$d" - "Rilascio elemento in schermata Home non riuscito." - "Scegli il widget da creare" - "Nome cartella" - "Rinomina cartella" - "OK" - "Annulla" - "Aggiungi a schermata Home" - "App" - "Scorciatoie" - "Widget" - "Spazio nelle schermate Home esaurito." "Spazio nella schermata Home esaurito." "Spazio esaurito nella barra dei Preferiti" - "Questo widget è troppo grande per la barra dei Preferiti" - "Scorciatoia \"%s\" creata." - "La scorciatoia \"%s\" è stata rimossa." - "Scorciatoia \"%s\" già presente." - "Scegli scorciatoia" - "Scegli app" "App" "Home page" - "Disinstalla" "Rimuovi" "Disinstalla" "Informazioni app" - "App" - "Rimuovi" - "Disinstalla aggiornamento" - "Disinstalla app" - "Dettagli sull\'app" - "1 app selezionata" - "1 widget selezionato" - "1 cartella selezionata" - "1 scorciatoia selezionata" "aggiunta di scorciatoie" "Consente a un\'app di aggiungere scorciatoie automaticamente." - "eliminazione di scorciatoie" - "Consente all\'app di rimuovere scorciatoie automaticamente." "lettura di impostazioni e scorciatoie in Home" "Consente all\'app di leggere le impostazioni e le scorciatoie in Home." "creazione di impostazioni e scorciatoie in Home" @@ -77,29 +45,18 @@ "Errore durante il caricamento del widget" "Configurazione" "Questa è un\'app di sistema e non può essere disinstallata." - "Lanciamissili" "Cartella senza nome" "Schermata Home %1$d" "Pagina %1$d di %2$d" "Schermata Home %1$d di %2$d" - "Pagina di applicazioni %1$d di %2$d" - "Pagina di widget %1$d di %2$d" "Benvenuto" - "Personalizza la schermata Home." - - - "Creare più schermate per app e cartelle" "Copia le icone delle tue app" "Importare icone e cartelle dalle schermate Home precedenti?" "COPIA ICONE" "RICOMINCIA" - "Organizza il tuo spazio" - "Tocca e tieni premuto lo sfondo per gestire sfondi, widget e impostazioni." "Sfondi, widget e impostazioni" "Tocca lo sfondo e tieni premuto per personalizzare" "OK" - "Ecco una cartella" - "Per crearne una simile, tocca un\'app e tieni premuto, dopodiché spostala sopra un\'altra." "OK" "Cartella aperta, %1$d per %2$d" "Tocca per chiudere la cartella" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index effebd627..479be106c 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "דף הבית" - "‏אפליקציות הליבה של Android" "האפליקציה לא מותקנת." "האפליקציה אינה זמינה" "אפליקציה שהורדת הושבתה במצב בטוח" "ווידג\'טים מושבתים במצב בטוח" - "רכיבי ווידג\'ט" - "רכיבי ווידג\'ט" "הצג זכרון" "גע נגיעה רציפה בווידג\'ט כדי לבחור בו." "%1$d × %2$d" - "לא ניתן היה לשחרר את הפריט במסך דף הבית הזה." - "בחר ווידג\'ט ליצירה" - "שם תיקיה" - "שנה את שם התיקיה" - "אישור" - "בטל" - "הוסף למסך דף הבית" - "אפליקציות" - "קיצורי דרך" - "רכיבי ווידג\'ט" - "אין יותר מקום במסכי דף הבית." "אין עוד מקום במסך דף הבית הזה." "אין עוד מקום במגש המועדפים" - "הווידג\'ט הזה גדול מדי עבור מגש המועדפים." - "קיצור הדרך \'%s\' נוצר." - "קיצור הדרך \'%s\' הוסר." - "קיצור הדרך \'%s\' כבר קיים." - "בחר קיצור דרך" - "בחר אפליקציה" "אפליקציות" "דף הבית" - "הסר התקנה" "הסר" "הסר התקנה" "פרטי אפליקציה" - "אפליקציות" - "הסר" - "הסר את התקנת העדכון" - "הסר את התקנת האפליקציה" - "פרטי האפליקציה" - "נבחרה אפליקציה אחת" - "נבחר ווידג\'ט אחד" - "נבחרה תיקיה אחת" - "נבחר קיצור דרך אחד" "התקן קיצורי דרך" "מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש." - "הסר התקנה של קיצורי דרך" - "מאפשר לאפליקציה להסיר קיצורי דרך ללא התערבות המשתמש." "קרא הגדרות וקיצורי דרך של דף הבית" "מאפשר לאפליקציה לקרוא את ההגדרות וקיצורי הדרך בדף הבית." "כתוב הגדרות וקיצורי דרך של דף הבית" @@ -77,29 +45,18 @@ "בעיה בטעינת ווידג\'ט" "הגדר" "זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה." - "Rocket Launcher" "תיקיה ללא שם" "‏מסך דף הבית %1$d" "‏דף %1$d מתוך %2$d" "‏מסך דף הבית %1$d מתוך %2$d" - "‏דף אפליקציות %1$d מתוך %2$d" - "‏דף רכיבי ווידג\'ט ‏%1$d מתוך %2$d" "ברוכים הבאים" - "להרגיש בבית." - - - "צור מסכים נוספים עבור אפליקציות ותיקיות" "העתקת סמלי האפליקציות שלך" "האם לייבא סמלים ותיקיות ממסכי דף הבית הישנים שלך?" "העתק סמלים" "התחל דף חדש" - "ארגן את אזור העבודה שלך" - "גע נגיעה רציפה ברקע כדי לנהל את הטפט, רכיבי הווידג\'ט וההגדרות." "טפטים, ווידג\'טים והגדרות" "גע והחזק ברקע לביצוע התאמה אישית" "הבנתי" - "הנה תיקייה" - "כדי ליצור תיקייה כזו, גע נגיעה רציפה באפליקציה, ולאחר מכן גרור ושחרר אותו על-גבי אפליקציה אחרת." "אישור" "תיקיה פתוחה, %1$d על %2$d" "גע כדי לסגור את התיקיה" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 555f29089..aa700aa54 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "ホーム" - "Android Core Apps" "このアプリはインストールされていません。" "このアプリは使用できません" "ダウンロードしたアプリは、セーフモードでは無効です" "セーフモードではウィジェットは無効です" - "ウィジェット" - "ウィジェット" "メモリーを表示" "ウィジェットを追加するには押し続けます。" "%1$dx%2$d" - "このホーム画面にアイテムをドロップできませんでした" - "作成するウィジェットの選択" - "フォルダ名" - "フォルダ名を変更" - "OK" - "キャンセル" - "ホーム画面に追加" - "アプリ" - "ショートカット" - "ウィジェット" - "ホーム画面に空きスペースがありません。" "このホーム画面に空きスペースがありません。" "お気に入りトレイに空きスペースがありません" - "このウィジェットはお気に入りトレイには大きすぎます" - "ショートカット「%s」を作成しました。" - "ショートカット「%s」を削除しました。" - "ショートカット「%s」は既に存在します。" - "ショートカットを選択" - "アプリを選択" "アプリ" "ホーム" - "アンインストール" "削除" "アンインストール" "アプリ情報" - "アプリ" - "削除" - "更新をアンインストール" - "アプリをアンインストール" - "アプリの詳細" - "1つのアプリが選択されています" - "1つのウィジェットが選択されています" - "1つのフォルダが選択されています" - "1つのショートカットが選択されています" "ショートカットのインストール" "ユーザー操作なしでショートカットを追加することをアプリに許可します。" - "ショートカットのアンインストール" - "ユーザー操作なしでショートカットを削除することをアプリに許可します。" "ホームの設定とショートカットの読み取り" "ホームの設定とショートカットの読み取りをアプリに許可します。" "ホームの設定とショートカットの書き込み" @@ -77,29 +45,18 @@ "ウィジェットを表示できません" "セットアップ" "このシステムアプリはアンインストールできません。" - "Rocket Launcher" "名前のないフォルダ" "ホーム画面: %1$d" "%1$d/%2$dページ" "ホーム画面: %1$d/%2$d" - "アプリの%1$d/%2$dページ" - "ウィジェットの%1$d/%2$dページ" "ようこそ" - "ホームをカスタマイズします。" - - - "アプリとフォルダの画面をもっと作成します" "アプリのアイコンをコピー" "古いホーム画面からアイコンとフォルダをインポートしますか?" "アイコンをコピー" "初期状態にリセットする" - "スペースを整理" - "壁紙、ウィジェット、設定を管理するには、背景を押し続けます。" "壁紙、ウィジェット、設定" "カスタマイズするにはバックグラウンドを押し続けます" "OK" - "これがフォルダです" - "これと同じフォルダを作成するには、アプリを押し続けてから別のアプリの上に移動します。" "OK" "フォルダが開いています。%1$dx%2$dの大きさです" "タップしてフォルダを閉じます" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index d4c2582d6..79434c0f1 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "მთავარი" - "Android-ის ბირთვის აპები" "აპი არ არის დაყენებული." "აპი მიუწვდომელია" "უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია" "უსაფრთხო რეჟიმში ვიჯეტი გამორთულია" - "ვიჯეტები" - "ვიჯეტები" "Mem-ის ჩვენება" "შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად." "%1$d × %2$d" - "ერთეულის მთავარ ეკრანზე ჩაგდება ვერ მოხერხდა." - "აირჩიეთ ვიჯეტი შესაქმნელად" - "საქაღალდის სახელი" - "საქაღალდის გადარქმევა" - "კარგი" - "გაუქმება" - "მთავარ ეკრანზე დამატება" - "აპები" - "მალსახმობები" - "ვიჯეტები" - "მთავარ ეკრანებზე ადგილი აღარ არის." "ამ მთავარ ეკრანზე ადგილი აღარ არის." "რჩეულების თაროზე ადგილი არ არის" - "ეს ვიჯეტი ძალიან დიდია რჩეულების თაროსთვის" - "შეიქმნა მალსახმობი „%s“." - "მასლახმობი „%s“ წაშლილია." - "მალსახმობი „%s“ უკვე არსებობს." - "აირჩიეთ მალსახმობი" - "აირჩიეთ აპი" "აპები" "მთავარი" - "დეინსტალაცია" "წაშლა" "დეინსტალაცია" "აპის შესახებ" - "აპები" - "წაშლა" - "განახლების დეინსტალაცია" - "აპის დეინსტალაცია" - "აპის შესახებ" - "არჩეულია 1 აპი" - "არჩეულია 1 ვიჯეტი" - "არჩეულია 1 საქაღალდე" - "არჩეულია 1 მალსახმობი" "მალსახმობების დაყენება" "აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა." - "მალსახმობების წაშლა" - "აპისთვის მალსახმობების დამოუკიდებლად წაშლის უფლების მიცემა." "მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა" "აპისთვის მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვის უფლების მიცემა." "მთავარი ეკრანის პარამეტრებისა და მალსახმობების ჩაწერა" @@ -77,29 +45,18 @@ "პრობლემა ვიჯეტის ჩატვირთვისას" "დაყენება" "ეს სისტემური აპია და მისი წაშლა შეუძლებელია." - "ფეიერვერკი" "უსახელო საქაღალდე" "მთავარი ეკრანი %1$d" "გვერდი %1$d %2$d-დან" "მთავარი ეკრანი %1$d, %2$d-დან" - "აპების გვერდი %1$d, %2$d-დან" - "ვიჯეტების გვერდი %1$d, %2$d-დან" "მოგესალმებით" - "იგრძენით თავი საკუთარ სახლში" - - - "აპებისა და საქაღალდეებისთვის კიდევ ერთი ეკრანის შექმნა" "თქვენი აპის ხატულების კოპირება" "გსურთ, ძველი მთავარი ეკრანიდან ხატულების და საქაღ. იმპორტი?" "ხატულების კოპირება" "სტანდარტული განლაგება" - "თქვენი სივრცის ორგანიზება" - "თუ გსურთ ფონების, ვიჯეტების და პარამეტრების მართვა, შეეხეთ და არ აუშვათ ფონს." "ფონები, ვიჯეტები, & პარამეტრები" "მოსარგებად შეეხეთ & დააყოვნეთ ფონზე" "გასაგებია" - "აი, საქაღალდე" - "ასეთის შესაქმნელად, შეეხეთ და დააყოვნეთ აპზე, ხოლო შემდეგ გადააჩოჩეთ შემდეგზე." "კარგი" "საქაღალდე გახსნილია, %1$d x %2$d" "შეეხეთ საქაღალდის დასახურად" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index 3adf2d9e6..8a0c6277f 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Негізгі" - "Android Core қолданбалары" "Қолданба орнатылмаған." "Қолданба қол жетімді емес" "Жүктелген қолданба қауіпсіз режимде өшірілген" "Қауіпсіз режимде виджеттер өшіріледі" - "Виджеттер" - "Виджеттер" "Жадты көрсету" "Виджетті таңдау үшін түртіп, мықтап ұстаңыз." "%1$d × %2$d" - "Элементті осы Негізгі Экранға тастау орындалмады." - "Жасақтау үшін виджет таңдау" - "Қалта атауы" - "Қалтаның атауын өзгерту" - "Жарайды" - "Өшіру" - "Негізгі экранға қосу" - "Қолданбалар" - "Төте пернелер" - "Виджеттер" - "Негізгі экранда орын жоқ." "Бұл Негізгі экранда орын қалмады." "Қалаулылар науасында орын қалмады" - "Бұл виджет Қалаулылар науасы үшін тым үлкен" - "\"%s\" төте пернесі жасақталды." - "\"%s\" төте пернесі алынды." - "\"%s\" төте пернесі бұрыннан бар." - "Төте перне таңдау" - "Қолданба таңдау" "Қолданбалар" "Негізгі" - "Алмау" "Алып тастау" "Алмау" "Қолданба ақпары" - "Қолданбалар" - "Алып тастау" - "Жаңартуды алмау" - "Қолданбаны алып тастау" - "Қолданба туралы толығырақ" - "1 қолданба таңдалған" - "1 виджет таңдалған" - "1 қалта таңдалған" - "1 төте перне таңдалған" "төте пернелерді орнату" "Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді." - "төте пернелерді алып тастау" - "Қолданбаға пайдаланушының қатысуынсыз төте пернелерді алу мүмкіндігін береді." "Негізгі экрандағы параметрлер мен төте пернелерді оқу" "Қолданбаға Негізгі экрандағы параметрлер мен төте пернелерді оқу мүмкіндігін береді." "Негізгі экран параметрлері мен төте пернелерін жазу" @@ -77,29 +45,18 @@ "Виджетті жүктеу барысында мәселе орын алды" "Орнату" "Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес." - "Rocket Launcher" "Атауы жоқ қалта" "%1$d негізгі экран" "%1$d бет, барлығы %2$d" "%1$d негізгі экран, барлығы %2$d" - "%1$d қолданба беті, барлығы %2$d" - "%1$d виджет беті, барлығы %2$d" "Қош келдіңіз" - "Өзіңізді ыңғайлы сезініңіз." - - - "Қолданбалар мен қалталар үшін көбірек экрандар жасау" "Қолданба таңбаларын көшіру" "Бұрынғы негізгі экрандарыңыздағы таңбалар мен қалталар импортталсын ба?" "ТАҢБАЛАРДЫ КӨШІРУ" "ЖАҢАДАН БАСТАУ" - "Кеңістікті реттеу" - "Артқы фонды, виджеттерді және параметрлерді басқару үшін артқы шебін түртіп, мықтап ұстаңыз." "Тұсқағаздар, виджеттер және параметрлер" "Теңшеу үшін фонды түртіп, ұстап тұрыңыз" "ТҮСІНДІМ" - "Міне, қалта." - "Осы сияқты қалта жасау үшін, қолданбаны түртіп, мықтап ұстаңыз, одан кейін екіншісінің үстінен жылжытыңыз." "Жарайды" "Қалта ашылды, %1$d және %2$d" "Қалтаны жабу үшін түртіңіз" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index c7e683000..a61be73cd 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "ដើម" - "កម្មវិធី​​សំខាន់​ៗ​របស់ Android" "មិន​បាន​ដំឡើង​កម្មវិធី។" "មិន​មាន​កម្មវិធី" "បាន​បិទ​កម្មវិធី​ដែល​បាន​ទាញ​យក​ក្នុង​របៀប​សុវត្ថិភាព" "បាន​បិទ​ធាតុ​ក្រាហ្វិក​ក្នុង​របៀប​សុវត្ថិភាព" - "ធាតុ​ក្រាហ្វិក" - "ធាតុ​ក្រាហ្វិក" "បង្ហាញ​ Mem" "ប៉ះ & សង្កត់ ដើម្បី​ជ្រើស​ធាតុ​ក្រាហ្វិក។" "%1$d × %2$d" - "មិន​អាច​ទម្លាក់​ធាតុ​លើ​អេក្រង់​ដើម​នេះ​ទេ" - "ជ្រើស​ធាតុ​ក្រាហ្វិក ដើម្បី​​​បង្កើត" - "ឈ្មោះ​ថត" - "ប្ដូរ​ឈ្មោះ​ថត" - "យល់ព្រម" - "បោះបង់" - "បន្ថែម​ទៅ​អេក្រង់​ដើម" - "កម្មវិធី" - "ផ្លូវកាត់" - "ធាតុ​ក្រាហ្វិក" - "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​រស់​អ្នក​ទៀត​ទេ។" "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" "គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ" - "ធាតុ​ក្រាហ្វិក​នេះ​ធំ​ពេក​សម្រាប់​ថាស​និយម​ប្រើ" - "បាន​បង្កើត​ផ្លូវកាត់ \"%s\" ។" - "បាន​លុប​ផ្លូវកាត់ \"%s\" ។" - "មាន​ផ្លូវកាត់ \"%s\" រួច​ហើយ។" - "ជ្រើស​ផ្លូវកាត់" - "ជ្រើស​កម្មវិធី" "កម្មវិធី" "ដើម" - "លុប" "លុប​ចេញ" "លុប" "ព័ត៌មាន​កម្មវិធី" - "កម្មវិធី" - "លុប​ចេញ" - "លុប​បច្ចុប្បន្នភាព" - "លុប​កម្មវិធី" - "ព័ត៌មាន​លម្អិត​កម្មវិធី" - "បាន​ជ្រើស​កម្មវិធី ១" - "បាន​ជ្រើស​ធាតុ ១" - "បាន​ជ្រើស​ថត ១" - "បាន​ជ្រើស​ផ្លូវកាត់ ១" "ដំឡើង​ផ្លូវកាត់" "អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។" - "លុប​ផ្លូវកាត់" - "អនុញ្ញាត​ឲ្យ​កម្មវិធី​លុប​ផ្លូវកាត់​ដោយ​មិន​ចាំបាច់​អំពើ​ពី​អ្នក​ប្រើ។" "អាន​ការ​កំណត់​ និង​ផ្លូវកាត់​​អេក្រង់​ដើម" "អនុញ្ញាត​ឲ្យ​កម្មវិធី​អាន​ការ​កំណត់ និង​ផ្លូវកាត់​ក្នុង​អេក្រង់​ដើម។" "សរសេរ​ការ​កំណត់ ​និង​ផ្លូវកាត់​​លើ​អេក្រង់​ដើម" @@ -77,29 +45,18 @@ "បញ្ហា​ក្នុង​ការ​ផ្ទុក​ធាតុ​​ក្រាហ្វិក" "រៀបចំ" "នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។" - "កម្មវិធី​ចាប់ផ្ដើម​រ៉ូកែត" "ថត​គ្មាន​ឈ្មោះ" "អេក្រង់​ដើម %1$d" "ទំព័រ %1$d នៃ %2$d" "អេក្រង់​ដើម %1$d នៃ %2$d" - "ទំព័រ​កម្មវិធី %1$d នៃ %2$d" - "ទំព័រ​ធាតុ​ក្រាហ្វិក ​%1$d នៃ %2$d" "សូម​ស្វាគមន៍​" - "ធ្វើ​ដោយ​ខ្លួន​ឯង​នៅ​លើ​អេក្រង់​ដើម។" - - - "បង្កើត​អេក្រង់​ច្រើន​សម្រាប់​កម្មវិធី​ ​និង​ថតឯកសារ" "ចម្លង​រូបតំណាង​កម្មវិធី​របស់​អ្នក" "នាំចូល​រូបតំណាង និង​ថត​ពី​អេក្រង់​ដើម​ចាស់​របស់​អ្នក?" "រូប​តំណាង​ច្បាប់​ចម្លង" "ចាប់ផ្ដើម​ធ្វើ​ឲ្យ​ស្រស់" - "រៀបចំ​ចន្លោះ​របស់​អ្នក" - "ប៉ះ & សង្កត់​លើ​ផ្ទៃ​ខាង​ក្រោម ដើម្បី​គ្រប់គ្រង​ផ្ទាំង​រូបភាព, ធាតុ​ក្រាហ្វិក និង​ការ​កំណត់។" "ផ្ទាំងរូបភាព,ធាតុក្រាហ្វិក & ការកំណត់" "ប៉ះ & សង្កត់​ផ្ទៃ​ខាង​ក្រោយ​ដើម្បី​ប្ដូរ​តាម​​តម្រូវ​ការ" "យល់​ហើយ" - "នេះ​ជា​ថត" - "ដើម្បី​បង្កើត​មួយ​ដូច​នេះ ប៉ះ & សង្កត់​​លើ​កម្មវិធី បន្ទាប់​មក​ផ្លាស់ទី​វា​ទៅ​លើ​ធាតុ​មួយ​ផ្សេង​ទៀត។" "យល់ព្រម" "បាន​បើក​ថត %1$d ដោយ %2$d" "ប៉ះ ដើម្បី​បិទ​ថត" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index b327ebf0a..9680cb694 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "ಲಾಂಚರ್3" "ಮುಖಪುಟ" - "Android Core ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" "ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ" "ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" "ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" - "ವಿಜೆಟ್‌ಗಳು" - "ವಿಜೆಟ್‌ಗಳು" "ಸ್ಮರಣೆ ತೋರಿಸು" "ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ & ಹಿಡಿದುಕೊಳ್ಳಿ." "%1$d × %2$d" - "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಐಟಂ ಅನ್ನು ಬಿಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ." - "ರಚಿಸಲು ವಿಜೆಟ್‌ ಆಯ್ಕೆಮಾಡಿ" - "ಫೋಲ್ಡರ್ ಹೆಸರು" - "ಫೋಲ್ಡರ್ ಅನ್ನು ಮರುಹೆಸರಿಸಿ" - "ಸರಿ" - "ರದ್ದುಮಾಡು" - "ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು" - "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" - "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು" - "ವಿಜೆಟ್‌ಗಳು" - "ನಿಮ್ಮ ಮುಖಪುಟದ ಪರದೆಗಳಲ್ಲಿ ಯಾವುದೇ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." "ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ" - "ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇಗೆ ಈ ವಿಜೆಟ್‌ ತುಂಬಾ ದೊಡ್ಡದಾಗಿದೆ" - "ಶಾರ್ಟ್‌ಕಟ್‌ \"%s\" ಅನ್ನು ರಚಿಸಲಾಗಿದೆ." - "ಶಾರ್ಟ್‌ಕಟ್‌ \"%s\" ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ." - "ಶಾರ್ಟ್‌ಕಟ್‌ \"%s\" ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ." - "ಶಾರ್ಟ್‌ಕಟ್‌ ಆರಿಸಿ" - "ಅಪ್ಲಿಕೇಶನ್ ಆಯ್ಕೆಮಾಡಿ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" "ಮುಖಪುಟ" - "ಅಸ್ಥಾಪಿಸು" "ತೆಗೆದುಹಾಕು" "ಅಸ್ಥಾಪಿಸು" "ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ" - "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" - "ತೆಗೆದುಹಾಕು" - "ನವೀಕರಣವನ್ನು ಅಸ್ಥಾಪಿಸು" - "ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸು" - "ಅಪ್ಲಿಕೇಶನ್ ವಿವರಗಳು" - "1 ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" - "1 ವಿಜೆಟ್‌ ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ" - "1 ಫೋಲ್ಡರ್‌ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" - "1 ಶಾರ್ಟ್‌ಕಟ್‌ ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ" "ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ." - "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಅಸ್ಥಾಪಿಸಿ" - "ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ." "ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ" "ಮುಖಪುಟದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ." "ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಬರೆಯಿರಿ" @@ -77,29 +45,18 @@ "ವಿಜೆಟ್ ಲೋಡ್‌ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ" "ಸೆಟಪ್" "ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ." - "ರಾಕೆಟ್ ಲಾಂಚರ್" "ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್" "ಮುಖಪುಟದ ಪರದೆ %1$d" "%2$d ರಲ್ಲಿ %1$d ಪುಟ" "%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ" - "%2$d ರಲ್ಲಿ %1$d ಅಪ್ಲಿಕೇಶನ್ ಪುಟ" - "%2$d ರಲ್ಲಿ %1$d ವಿಜೆಟ್‌ಗಳ ಪುಟ" "ಸುಸ್ವಾಗತ" - "ನಿಮ್ಮ ಮನೆಯಂತೆ ಭಾವಿಸಿ." - - - "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಫೋಲ್ಡರ್‌ಗಳಿಗಾಗಿ ಇನ್ನಷ್ಟು ಪರದೆಗಳನ್ನು ರಚಿಸಿ" "ಅಪ್ಲಿಕೇಶನ್‌ ಐಕಾನ್‌ ನಕಲಿಸು" "ನಿಮ್ಮ ಹಳೆಯ ಮುಖಪುಟದ ಪರದೆಗಳಿಂದ ಐಕಾನ್‌ಗಳು ಮತ್ತು ಫೋಲ್ಡರ್‌ಗಳನ್ನು ಆಮದು ಮಾಡಿಕೊಳ್ಳುವುದೇ?" "ಐಕಾನ್‌ಗಳನ್ನು ನಕಲಿಸು" "ಹೊಸದಾಗಿ ಪ್ರಾರಂಭಿಸು" - "ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ವ್ಯವಸ್ಥಿತಗೊಳಿಸಿ" - "ವಾಲ್‌ಪೇಪರ್‌, ವಿಜೆಟ್‌ಗಳು ಮತ್ತು ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ & ಹಿಡಿದುಕೊಳ್ಳಿ." "ವಾಲ್‌ಪೇಪರ್‌ಗಳು, ವಿಜೆಟ್‌ಗಳು, & ಸೆಟ್ಟಿಂಗ್‌ಗಳು" "ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ & ಒತ್ತಿ ಹಿಡಿಯಿರಿ" "ಅರ್ಥವಾಯಿತು" - "ಇಲ್ಲೊಂದು ಫೋಲ್ಡರ್ ಇದೆ" - "ಈ ರೀತಿ ರಚಿಸಲು, ಸ್ಪರ್ಶಿಸಿ & ಆಪ್‌ ಹಿಡಿದುಕೊಂಡು ಮತ್ತೊಂದರ ಮೇಲೆ ಸರಿಸಿ." "ಸರಿ" "ಫೋಲ್ಡರ್ ತೆರೆಯಲಾಗಿದೆ, %1$d ಬೈ %2$d" "ಫೋಲ್ಡರ್‌ ಮುಚ್ಚಲು ಸ್ಪರ್ಶಿಸಿ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index a9aeedc71..7e924cb19 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "홈" - "Android 핵심 앱" "앱이 설치되지 않았습니다." "앱을 사용할 수 없음" "다운로드한 앱은 안전 모드에서 사용할 수 없습니다." "안전 모드에서 위젯 사용 중지됨" - "위젯" - "위젯" "메모리 표시" "위젯을 선택하려면 길게 터치하세요." "%1$d×%2$d" - "홈 화면에 항목을 놓을 수 없습니다." - "만들 위젯 선택" - "폴더 이름" - "폴더 이름 바꾸기" - "확인" - "취소" - "홈 화면에 추가" - "앱" - "바로가기" - "위젯" - "홈 화면에 더 이상 공간이 없습니다." "홈 화면에 더 이상 공간이 없습니다." "즐겨찾기 트레이에 더 이상 공간이 없습니다." - "위젯이 너무 커서 즐겨찾기 트레이에 들어갈 수 없습니다." - "바로가기(\'%s\')가 생성되었습니다." - "바로가기(\'%s\')가 삭제되었습니다." - "바로가기(\'%s\')가 이미 있습니다." - "바로가기 선택" - "앱 선택" "앱" "홈" - "제거" "삭제" "제거" "앱 정보" - "앱" - "삭제" - "업데이트 제거" - "앱 제거" - "앱 세부정보" - "앱 1개 선택됨" - "위젯 1개 선택됨" - "폴더 1개 선택됨" - "바로가기 1개 선택됨" "바로가기 설치" "앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다." - "바로가기 제거" - "앱이 사용자의 작업 없이 바로가기를 삭제할 수 있도록 합니다." "홈 설정 및 바로가기 읽기" "앱이 홈에 있는 설정 및 바로가기를 읽을 수 있도록 합니다." "홈 설정 및 바로가기 쓰기" @@ -77,29 +45,18 @@ "위젯을 로드하는 중 문제가 발생했습니다." "설정" "시스템 앱은 제거할 수 없습니다." - "로켓 실행기" "이름이 없는 폴더" "홈 화면 %1$d" "페이지 %1$d/%2$d" "홈 화면 %1$d/%2$d" - "앱 페이지 %1$d/%2$d" - "위젯 페이지 %1$d/%2$d" "환영합니다." - "편리한 사용 환경을 만드세요." - - - "앱 및 폴더를 표시할 화면 더 만들기" "앱 아이콘 복사" "이전 메인 스크린에서 아이콘과 폴더를 가져오시겠습니까?" "아이콘 복사" "새로 시작" - "공간 관리하기" - "배경화면, 위젯, 설정을 관리하려면 백그라운드를 길게 터치합니다." "배경화면, 위젯, 설정" "백그라운드를 길게 터치하여 맞춤설정합니다." "확인" - "폴더" - "폴더를 만들려면 앱을 길게 터치한 다음 다른 앱 위에 올려 놓으세요." "확인" "폴더 열림(%1$dX%2$d)" "터치하여 폴더를 닫음" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index 7dd1206b4..39cbee2a6 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Үйгө" - "Android Core колдонмолору" "Колдонмо орнотулган эмес." "Колдонмо жеткиликтүү эмес" "Жүктөп алынган колдонмо Коопсуз режиминде иштен чыгарылды" "Виджеттер Коопсуз режимде өчүрүлгөн" - "Виджеттер" - "Виджеттер" "Мемди көргөзүү" "Виджетти тандаш үчүн, басып туруңуз" "%1$d × %2$d" - "Муну бул Үй экранына ыргытуу мүмкүн эмес." - "Түзүлүүчү виджетти тандаңыз" - "Фолдердин аты" - "Фолдердин атын өзгөртүү" - "OK" - "Жокко чыгаруу" - "Үй экранына кошуу" - "Колдонмолор" - "Тез чакырмалар" - "Виджеттер" - "Үй экрандарыңызда бош орун калган жок." "Бул Үй экранында бош орун жок." "Тандамалдар тайпасында орун калган жок" - "Бул виджет Тандамалдар үчүн өтө чоң" - "\"%s\" тез чакырмасы түзүлдү." - "\"%s\" тез чакырмасы алынып салынды." - "\"%s\" тез чакырмасы бар." - "Тез чакырма тандоо" - "Колдонмо тандоо" "Колдонмолор" "Үйгө" - "Чечип салуу" "Алып салуу" "Чечип салуу" "Колдонмо тууралуу" - "Колдонмолор" - "Алып салуу" - "Жаңыртууну чечип салуу" - "Колдонмону чечип салуу" - "Колдонмонун кеңири маалыматтары" - "1 колдонмо тандалды" - "1 виджет тандалды" - "1 фолдер тандалды" - "1 тез чакырма тандалды" "тез чакырмаларды орнотуу" "Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет." - "тез чакырмаларды жок кылуу" - "Колдонмого колдонуучуга кайрылбастан тез чакырмаларды жок кылууга уруксат берет." "Үйдүн тууралоолорун жана тез чакырмаларын окуу" "Колдонмого Үйдүн тууралоолорун жана тез чакырмаларын окууга уруксат берет." "Үйдүн тууралоолорун жана тез чакырмаларын жазуу" @@ -77,29 +45,18 @@ "Виджетти жүктөөдө маселе бар" "Орнотуу" "Бул системдик колдонмо жана аны чечкенге болбойт." - "Rocket Launcher" "Аты жок фолдер" "Үй экраны %1$d" "%2$d ичинен %1$d барак" "Үй экраны %2$d ичинен %1$d" - "Колдонмолор барагы %2$d ичинен %1$d" - "Виджеттер барагы %2$d ичинен %1$d" "Кош келиңиз" - "Өзүңүздү үйүңүздөгүдөй эркин сезиңиз." - - - "Колдонмолор жана фолдерлер үчүн кошумча экрандарды түзүңүз" "Колдонмоңуздун сүрөтчөлөрүн көчүрүү" "Эски үй экрандарыңыздан сүрөтчөлөр жана фолдерлер импорттолсунбу?" "СҮРӨТЧӨЛӨРДҮ КӨЧҮРҮҮ" "ТАЗАСЫН БАШТОО" - "Өз мейкиндигиңизди уюштуруңуз" - "Тушкагаздарды, виджеттерди жана тууралоолорду башкаруу үчүн фонду басып туруңуз." "Тушкагаздар, виджеттер & жөндөөлөр" "Өзгөчөлөштүрүү үчүн фонго тийип & коё бербей туруңуз" "ТҮШҮНДҮМ" - "Мынакей фолдер" - "Башкасын түзүш үчүн колдонмону басып туруп, башканын жанына жылдырыңыз." "OK" "Фолдер ачылды, туурасы %1$d, бийиктиги %2$d" "Фолдерди жабыш үчүн тийиңиз" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index 8832408cd..aa45950f0 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "ໜ້າຫຼັກ" - "ແອັບພລິເຄຊັນຫຼັກຂອງ Android" "ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ." "ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້" "ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode" "​ວິດ​ເຈັດ​ຖືກ​ປິດ​ໃນ Safe mode" - "ວິດເຈັດ" - "ວິດເຈັດ" "ສະແດງຄວາມຈຳ" "ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ." "%1$d × %2$d" - "ບໍ່ສາມາດວາງລາຍການໃສ່ໜ້າຈໍຫຼັກນີ້ໄດ້" - "ເລືອກວິດເຈັດເພື່ອສ້າງມັນ" - "ຊື່ໂຟນເດີ" - "ປ່ຽນຊື່ໂຟນເດີ" - "ຕົກລົງ" - "ຍົກ​ເລີກ​" - "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" - "ແອັບຯ" - "ທາງລັດ" - "ວິດເຈັດ" - "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກຂອງທ່ານ." "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." "ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ" - "ວິດເຈັດນີ້ໃຫຍ່ເກີນໄປທີ່ຈະເກັບໄວ້ໃນຖາດເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ" - "ທາງລັດ \"%s\" ຖືກສ້າງແລ້ວ." - "ທາງລັດ \"%s\" ຖືກລຶບແລ້ວ." - "ທາງລັດ \"%s\" ມີຢູ່ແລ້ວ." - "ເລືອກທາງລັດ" - "ເລືອກແອັບຯ" "ແອັບຯ" "ໜ້າຫຼັກ" - "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ລຶບ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ຂໍ້ມູນແອັບຯ" - "ແອັບຯ" - "ລຶບ" - "ຖອນອັບເດດ" - "ຖອນແອັບຯ" - "ລາຍລະອຽດແອັບຯ" - "1 ແອັບຯຖືກເລືອກ" - "1 ວິດເຈັດຖືກເລືອກ" - "1 ໂຟນເດີຖືກເລືອກ" - "1 ທາງລັດຖືກເລືອກ" "ຕິດຕັ້ງທາງລັດ" "ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້." - "ຖອນທາງລັດ" - "ອະນຸຍາດໃຫ້ແອັບຯດັ່ງກ່າວ ລຶບທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້." "ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ" "ອະນຸຍາດໃຫ້ແອັບຯດັ່ງກ່າວອ່ານການຕັ້ງຄ່າ ແລະທາງລັດໃນໜ້າຫຼັກ." "ຂຽນການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ" @@ -77,29 +45,18 @@ "ມີບັນຫາໃນການໂຫລດວິດເຈັດ" "ຕິດຕັ້ງ" "ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້." - "Rocket Launcher" "ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່" "ໜ້າຈໍຫຼັກ %1$d" "ໜ້າ %1$d ຈາກ %2$d" "ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d" - "ແອັບຯໜ້າ %1$d ໃນ %2$d" - "ວິດເຈັດໜ້າ %1$d ໃນ %2$d" "ຍິນດີຕ້ອນຮັບ" - "ເຮັດໂຕໃຫ້ຄືຢູ່ໃນບ້ານ" - - - "ສ້າງຈໍເພີ່ມເຕີມສຳລັບແອັບຯ ແລະໂຟນເດີ" "ສຳເນົາໄອຄອນແອັບຯຂອງທ່ານ" "ນຳເຂົ້າໄອຄອນ ແລະ ໂຟນເດີຈາກໂຮມສະກຣີນອັນເກົ່າຂອງທ່ານບໍ່?" "ສຳເນົາໄອຄອນ" "ເລີ່ມຕົ້ນໃໝ່" - "ຈັດການພື້ນທີ່ຂອງທ່ານ" - "ແຕະຄ້າງໄວ້ທີ່ພາບພື້ນຫຼັງເພື່ອຈັດການພາບພື້ນຫຼັງ, ວິດເຈັດແລະການຕັ້ງຄ່າ." "​ຮູບ​ພື້ນຫຼັງ, ວິດເຈັດ, & ​ການ​ຕັ້ງ​ຄ່າ" "ແຕະທີ່​ພາບ​ພື້ນ​ຫລັງ​ຄ້າງ​ໄວ້​ເພື່ອ​ປັບ​ແຕ່ງ" "ເຂົ້າໃຈແລ້ວ" - "ນີ້ແມ່ນໂຟນເດີ" - "ເພື່ອ​ສ້າງ​ອັນໃໝ່​ແບບນີ້ ໃຫ້​ແຕະ​ຄ້າງ​ໄວ້​ທີ່​ແອັບຯ​ທີ່​ຕ້ອງການ​ຍ້າຍ​ແລ້ວ​ລາກ​ມັນ​ໄປ​ຫາ​ໂຕ​ອື່ນ." "ຕົກລົງ" "ເປີດໂຟນເດີແລ້ວ, %1$d ຄູນ %2$d" "ສຳພັດເພື່ອປິດໂຟນເດີ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 6bb39650e..3dd9fc9ec 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Pagrindinis" - "Pagrindinės „Android“ programos" "Programa neįdiegta." "Programa nepasiekiama" "Atsisiųsta programa išjungta Saugos režimu" "Valdikliai išjungti Saugiame režime" - "Valdikliai" - "Valdikliai" "Rodyti atmintinę" "Palieskite ir laikykite, kad pasirinkt. valdiklį." "%1$d × %2$d" - "Nepavyko nuvilkti elemento į šį pagrindinį ekraną." - "Pasirinkite norimą kurti valdiklį" - "Aplanko pavadinimas" - "Pervardyti aplanką" - "Gerai" - "Atšaukti" - "Pridėti prie pagrindinio ekrano" - "Programos" - "Spartieji klavišai" - "Valdikliai" - "Pagrindiniuose ekranuose vietos nebėra." "Šiame pagrindiniame ekrane vietos nebėra." "Mėgstamiausių dėkle nebėra vietos" - "Mėgstamiausių dėklui šis valdiklis per didelis." - "Spartusis klavišas „%s“ sukurtas." - "Spartusis klavišas „%s“ pašalintas." - "Spartusis klavišas „%s“ jau yra." - "Pasirinkite spartųjį klavišą" - "Pasirinkite programą" "Programos" "Pagrindinis" - "Pašalinti" "Pašalinti" "Pašalinti" "Programos informacija" - "Programos" - "Pašalinti" - "Pašalinti naujinį" - "Pašalinti programą" - "Išsami programos informacija" - "Pasirinkta 1 programa" - "Pasirinktas 1 valdiklis" - "Pasirinktas 1 aplankas" - "Pasirinktas 1 spartusis klavišas" "įdiegti sparčiuosius klavišus" "Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo." - "pašalinti sparčiuosius klavišus" - "Programai leidžiama pašalinti sparčiuosius klavišus be naudotojo įsikišimo." "skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus" "Programai leidžiama skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus." "rašyti pagrindinio puslapio nustatymus ir sparčiuosius klavišus" @@ -77,29 +45,18 @@ "Problema įkeliant valdiklį" "Sąranka" "Tai sistemos programa ir jos negalima pašalinti." - "Rocket Launcher" "Aplankas be pavadinimo" "%1$d pagrindinis ekranas" "%1$d psl. iš %2$d" "%1$d pagrindinis ekranas iš %2$d" - "%1$d programų psl. iš %2$d" - "%1$d valdiklių psl. iš %2$d" "Sveiki" - "Jauskitės kaip namie." - - - "Sukurkite daugiau programų ir aplankų ekrano kopijų" "Programų piktogramų kopij." "Importuoti piktogramas ir aplankus iš senų pagr. ekranų?" "KOPIJUOTI PIKTOGRAMAS" "PRADĖTI IŠ NAUJO" - "Tvarkykite savo vietą" - "Palieskite ir laikykite foną, jei norite tvarkyti ekrano foną, valdiklius ir nustatymus." "Ekrano fonai, valdikliai ir nustatymai" "Jei norite tinkinti, palieskite ir palaikykite foną" "SUPRATAU" - "Štai aplankas" - "Kad sukurtumėte tokį patį, palieskite ir laikykite programą, tada perkelkite ją virš kitos programos." "Gerai" "Atidarytas aplankas, %1$d ir %2$d" "Palieskite, kad uždarytumėte aplanką" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 347cd3eb1..07ed9ece4 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Sākums" - "Android pamatlietotnes" "Lietotne nav instalēta." "Lietotne nav pieejama." "Lejupielādētā lietotne ir atspējota drošajā režīmā." "Logrīki atspējoti drošajā režīmā" - "Logrīki" - "Logrīki" "Rādīt atmiņu" "Lai izvēlētos logrīku, pieskarieties un turiet to." "%1$d × %2$d" - "Nevarēja nomest vienumu šajā sākuma ekrānā." - "Izveidojamā logrīka izvēle" - "Mapes nosaukums" - "Mapes pārdēvēšana" - "Labi" - "Atcelt" - "Pievienošana sākuma ekrānam" - "Lietotnes" - "Saīsnes" - "Logrīki" - "Sākuma ekrānos vairs nav vietas." "Šajā sākuma ekrānā vairs nav vietas." "Izlases joslā vairs nav vietas." - "Šis logrīks ir pārāk liels izlases joslai." - "Tika izveidota saīsne “%s”." - "Tika noņemta saīsne “%s”." - "Saīsne “%s” jau pastāv." - "Saīsnes izvēle" - "Lietotnes izvēle" "Lietotnes" "Sākums" - "Atinstalēt" "Noņemt" "Atinstalēt" "Lietotnes informācija" - "Lietotnes" - "Noņemt" - "Atinstalēt atjauninājumu" - "Atinstalēt lietotni" - "Lietotnes informācija" - "Atlasīta 1 lietotne" - "Atlasīts 1 logrīks" - "Atlasīta 1 mape" - "Atlasīta 1 saīsne" "instalēt saīsnes" "Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam." - "atinstalēt saīsnes" - "Ļauj lietotnei noņemt saīsnes, nejautājot lietotājam." "lasīt sākuma ekrāna iestatījumus un saīsnes" "Ļauj lietotnei lasīt iestatījumus un saīsnes sākuma ekrānā." "rakstīt sākuma ekrāna iestatījumus un saīsnes" @@ -77,29 +45,18 @@ "Ielādējot logrīku, radās problēma." "Notiek iestatīšana" "Šī ir sistēmas lietotne, un to nevar atinstalēt." - "Rocket Launcher" "Mape bez nosaukuma" "Sākuma ekrāns: %1$d" "%1$d. lapa no %2$d" "Sākuma ekrāns: %1$d no %2$d" - "%1$d. lietotņu lapa no %2$d" - "%1$d. logrīku lapa no %2$d" "Laipni lūdzam!" - "Informācija par pamatfunkcijām" - - - "Izveidojiet vairāk ekrānu lietotnēm un mapēm." "Lietotņu ikonu kopēšana" "Vai importēt ikonas, mapes no iepriekšējiem sākuma ekrāniem?" "KOPĒT IKONAS" "SĀKT NO SĀKUMA" - "Kārtojiet savu darbvietu" - "Pieskarieties fonam un turiet to, lai pārvaldītu fona tapeti, logrīkus un iestatījumus." "Fona tapetes, logrīki un iestatījumi" "Lai pielāgotu, pieskarieties fonam un turiet to nospiestu." "SAPRATU!" - "Lūk, mape!" - "Lai izveidotu tādu pašu, pieskarieties lietotnei un turiet to, pēc tam pārvietojiet to virs citas lietotnes." "Labi" "Atvērta mape: %1$d x %2$d" "Pieskarieties, lai aizvērtu mapi." diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index 129f52c64..4e7c52573 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Почетна страница" - "Основни апликации на Android" "Апликацијата не е инсталирана." "Апликацијата не е достапна" "Преземената апликација е оневозможена во безбеден режим" "Додатоците се оневозможени во безбеден режим" - "Виџети" - "Виџети" "Прикажи „Мени“" "Допри и задржи за да се избере виџетот." "%1$d × %2$d" - "Не можеше да се спушти елемент на овој екран на почетната страница." - "Избери виџет за да се создаде" - "Име на папка" - "Преименувај папка" - "Во ред" - "Откажи" - "Додај во екран на почетна страница" - "Апликации" - "Кратенки" - "Виџети" - "Нема повеќе простор на вашите екрани на почетна страница." "Нема повеќе простор на овој екран на почетната страница." "Нема повеќе простор на лентата „Омилени“" - "Овој виџет е премногу голем за лентата „Омилени“" - "Кратенката „%s“ е создадена." - "Кратенката „%s“ е отстранета." - "Кратенката „%s“ веќе постои." - "Избери кратенка" - "Избери апликација" "Апликации" "Почетна страница" - "Деинсталирај" "Отстрани" "Деинсталирај" "Информации за апликацијата" - "Апликации" - "Отстрани" - "Деинсталирај ажурирање" - "Деинсталирај апликација" - "Детали за апликација" - "1 апликација е избрана" - "1 виџет е избран" - "1 папка е избрана" - "1 кратенка е избрана" "инсталирај кратенки" "Овозможува апликацијата да додава кратенки без интервенција на корисникот." - "деинсталирај кратенки" - "Овозможува апликацијата да ги отстрани кратенките без интервенција на корисникот." "прочитај подесувања и кратенки на почетна страница" "Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница." "напиши подесувања и кратенки на почетна страница" @@ -77,29 +45,18 @@ "Проблем при вчитувањето на виџетот" "Поставување" "Ова е системска апликација и не може да се деинсталира." - "Rocket Launcher" "Неименувана папка" "Екран на почетна страница %1$d" "Страница %1$d од %2$d" "Екран на почетна страница %1$d од %2$d" - "Страница на апликации %1$d од %2$d" - "Страница на виџети %1$d од %2$d" "Добредојдовте" - "Чувствувајте се како дома." - - - "Создади повеќе екрани за апликации и папки" "Копирај икони за апликација" "Зачувај икони и папки од твоите стари почетни страни?" "КОПИРАЈ ИКОНИ" "СТАРТУВАЈ ОД ПОЧЕТОК" - "Организирајте го вашиот простор" - "Допри и задржи ја заднината за управување со тапети, виџети и подесувања." "Тапети, додатоци и поставки" "Допрете и задржете на заднината за да приспособите" "СФАТИВ" - "Еве папка" - "За да создадете ваква, допрете и држете ја апликацијата, а потоа поместете ја врз другата." "Во ред" "Отворена е папка, %1$d на %2$d" "Допри за да се затвори папката" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index 210b78138..d1338b6b5 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "ഹോം" - "Android Core അപ്ലിക്കേഷനുകൾ" "അപ്ലിക്കേഷൻ ഇൻസ്‌റ്റാളുചെ‌യ്‌തിട്ടില്ല." "അപ്ലിക്കേഷൻ ലഭ്യമല്ല" "ഡൗൺലോഡുചെയ്‌ത അപ്ലിക്കേഷൻ സുരക്ഷാ മോഡിൽ പ്രവർത്തനരഹിതമാക്കി" "സുരക്ഷിത മോഡിൽ വിജറ്റുകൾ പ്രവർത്തനരഹിതമാക്കി" - "വിജറ്റുകൾ" - "വിജറ്റുകൾ" "മെമ്മറി കാണിക്കുക" "ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക." "%1$d × %2$d" - "ഹോം സ്‌ക്രീനിൽ ഇനം വലിച്ചിടാനായില്ല." - "സൃഷ്‌ടിക്കുന്നതിന് വിജറ്റ് തിരഞ്ഞെടുക്കുക" - "ഫോൾഡറിന്റെ പേര്" - "ഫോൾഡറിന്റെ പേരുമാറ്റുക" - "ശരി" - "റദ്ദാക്കുക" - "ഹോം സ്ക്രീനിൽ ചേർക്കുക" - "അപ്ലിക്കേഷനുകൾ" - "കുറുക്കുവഴികൾ" - "വിജറ്റുകൾ" - "നിങ്ങളുടെ ഹോം സ്‌ക്രീനുകളിൽ സ്ഥലമില്ല." "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." "പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല" - "ഈ വിജറ്റ് പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഉൾക്കൊള്ളിക്കാവുന്നതിലും വളരെ വലുതാണ്" - "കുറുക്കുവഴി \"%s\" സൃഷ്‌ടിച്ചു." - "കുറുക്കുവഴി \"%s\" നീക്കംചെയ്‌തു." - "കുറുക്കുവഴി \"%s\" ഇതിനകം നിലവിലുണ്ട്." - "കുറുക്കുവഴി തിരഞ്ഞെടുക്കുക" - "അപ്ലിക്കേഷൻ തിരഞ്ഞെടുക്കുക" "അപ്ലിക്കേഷനുകൾ" "ഹോം" - "അണ്‍ഇസ്റ്റാളുചെയ്യുക" "നീക്കംചെയ്യുക" "അണ്‍ഇസ്റ്റാളുചെയ്യുക" "അപ്ലിക്കേഷൻ വിവരം" - "അപ്ലിക്കേഷനുകൾ" - "നീക്കംചെയ്യുക" - "അപ്‌ഡേറ്റ് അൺഇൻസ്റ്റാളുചെയ്യുക" - "അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യുക" - "അപ്ലിക്കേഷൻ വിശദാംശങ്ങൾ" - "ഒരു അപ്ലിക്കേഷൻ തിരഞ്ഞെടുത്തു" - "ഒരു വിജറ്റ് തിരഞ്ഞെടുത്തു" - "ഒരു ഫോൾഡർ തിരഞ്ഞെടുത്തു" - "ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുത്തു" "കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക" "ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." - "കുറുക്കുവഴികൾ അൺഇൻസ്റ്റാളുചെയ്യുക" - "ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ നീക്കംചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." "ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക" "ഹോമിലെ ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." "ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റൈറ്റുചെയ്യുക" @@ -77,29 +45,18 @@ "വിജറ്റ് ലോഡുചെയ്യുന്നതിൽ പ്രശ്നമുണ്ട്" "സജ്ജീകരിക്കുക" "ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല." - "റോക്കറ്റ് ലോഞ്ചർ" "പേരുനൽകാത്ത ഫോൾഡർ" "ഹോം സ്‌ക്രീൻ %1$d" "പേജ് %1$d / %2$d" "ഹോം സ്‌ക്രീൻ %1$d / %2$d" - "അപ്ലിക്കേഷനുകളുടെ പേജ് %1$d / %2$d" - "വിജറ്റുകളുടെ പേജ് %1$d / %2$d" "സ്വാഗതം" - "ഹോം നിങ്ങളുടേതാക്കി മാറ്റുക." - - - "അപ്ലിക്കേഷനുകൾക്കും ഫോൾഡറുകൾക്കും വേണ്ടി കൂടുതൽ സ്‌ക്രീനുകൾ സൃഷ്‌ടിക്കുക" "നിങ്ങളുടെ അപ്ലിക്കേഷൻ ഐക്കണുകൾ പകർത്തുക" "നിങ്ങളുടെ പഴയ ഹോം സ്ക്രീനുകളിൽ നിന്ന് ഐക്കണുകളും ഫോൾഡറുകളും ഇമ്പോർട്ടുചെയ്യണോ?" "ഐക്കണുകൾ പകർത്തുക" "പുതുതായി ആരംഭിക്കുക" - "നിങ്ങളുടെ ഇടം ഓർഗനൈസുചെയ്യുക" - "വാൾപേപ്പർ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ നിയന്ത്രിക്കുന്നതിന് പശ്ചാത്തലം സ്‌പർശിച്ച് പിടിക്കുക." "വാൾപേപ്പറുകൾ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ" "ഇഷ്‌ടാനുസൃതമാക്കുന്നതിന് പശ്‌ചാത്തലം സ്‌പർശിച്ചുപിടിക്കുക" "മനസ്സിലായി" - "ഇവിടെയൊരു ഫോൾഡർ ഉണ്ട്" - "ഇതുപോലൊന്ന് സൃഷ്‌ടിക്കുന്നതിന്, ഒരു അപ്ലിക്കേഷൻ സ്‌പർശിച്ച് പിടിച്ചുകൊണ്ട് അത് മറ്റൊന്നിലേക്ക് നീക്കുക." "ശരി" "ഫോൾഡർ തുറന്നു, %1$d / %2$d" "ഫോൾഡർ അടയ്ക്കാൻ സ്‌പർശിക്കുക" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index da9662b9c..6639e6df1 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Нүүр" - "Андройд үндсэн апп" "Апп суугаагүй байна." "Апп-г ашиглах боломжгүй" "Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн" "Safe горимд виджетүүдийг идэвхгүйжүүлсэн" - "Виджет" - "Виджет" "Мем харуулах" "Виджетийг авах бол хүрээд барина уу." "%1$d × %2$d" - "Энэ Нүүр дэлгэцэнд буулгах боломжгүй." - "Үүсгэх виджетээ сонгоно уу" - "Фолдерын нэр" - "Фолдерын нэр өөрчлөх" - "Тийм" - "Цуцлах" - "Нүүр дэлгэцэнд нэмэх" - "Апп" - "Товчлол" - "Виджет" - "Таны Нүүр дэлгэц зайгүй." "Энэ Нүүр дэлгэц зайгүй." "\"Дуртай\" трей дээр өөр зай байхгүй байна" - "Энэ виджет трей дээр хэт томдож байна" - "\"%s\" товчлол үүсэв." - "\"%s\" товчлол устгагдав." - "\"%s\" товчлол өмнө үүссэн байна." - "Товчлол сонгох" - "Апп сонгох" "Апп" "Нүүр" - "Устгах" "Устгах" "Устгах" "Апп мэдээлэл" - "Апп" - "Устгах" - "Шинэчлэлийг устгах" - "Апп устгах" - "Апп дэлгэрэнгүй" - "1 апп сонгогдсон" - "1 виджет сонгогдсон" - "1 фолдер сонгогдсон" - "1 товчлол сонгогдсон" "товчлол суулгах" "Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна" - "товчлолыг устгах" - "Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг устгаж чадна" "Нүүрний тохиргоо болон товчлолыг унших" "Апп нь Нүүрэндэх товчлол болон тохиргоог уншиж чадна." "Нүүрний тохиргоо болон товчлолыг бичих" @@ -77,29 +45,18 @@ "Виджет ачаалахад асуудал гарав" "Тохируулга" "Энэ апп нь системийн апп ба устгах боломжгүй." - "Пуужин хөөргөгч" "Нэргүй фолдер" "Нүүр дэлгэц %1$d" "%2$d-н %1$d хуудас" "%2$d-н Нүүр дэлгэц %1$d" - "%2$d-н %1$d апп хуудас" - "%2$d-н %1$d виджет хуудас" "Тавтай морилно уу" - "Гэртээ байгаа мэт тухлаарай." - - - "Апп болон фолдеруудад зориулан өөр дэлгэцүүд үүсгээрэй" "Таны апп дүрсүүдийг хуулах" "Таны хуучин Үндсэн дэлгэц дээрх дүрсүүдийг импорт хийх үү?" "ДҮРСҮҮДИЙГ ХУУЛАХ" "ШИНЭЭР ЭХЛЭХ" - "Өөрийнхөө зайг тохируулаарай" - "Арын дэвсгэр дээр хүрээд & дарснаар ханын зураг, виджет болон тохиргоог өөрчилж болно." "Дэвсгэр зураг, виджет, & тохиргоо" "Тааруулахын тулд арын дэлгэцэнд хүрээд & барина уу" "Ойлголоо" - "Фолдер энд байна" - "Үүнтэй адилханыг үүсгэхийн тулд апп дээр хүрч & бариад нөгөөхийн дээр зөөнө үү." "Тийм" "%1$d %2$d фолдер нээгдэв" "Фолдер хаах бол хүрнэ үү" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index db27b21d1..0d61ff3ca 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "मुख्‍यपृष्‍ठ" - "Android Core Apps" "अॅप स्थापित केलेला नाही." "अॅप उपलब्ध नाही" "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" "विजेट सुरक्षित मोडमध्ये अक्षम झाले" - "विजेट" - "विजेट" "Mem दर्शवा" "विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." "%1$d × %2$d" - "या मुख्य स्क्रीनवर आयटम ड्रॉप करू शकलो नाही." - "तयार करण्यासाठी विजेट निवडा" - "फोल्डर नाव" - "फोल्डरचे नाव बदला" - "ठीक" - "रद्द करा" - "मुख्य स्क्रीनवर जोडा" - "अॅप्स" - "शॉर्टकट" - "विजेट" - "आपल्या मुख्य स्क्रीनवर अधिक जागा नाही." "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" - "हे विजेट आवडत्या ट्रे साठी खूप मोठे आहे" - "\"%s\" शॉर्टकट तयार केला." - "\"%s\" शॉर्टकट काढला." - "\"%s\" शॉर्टकट आधीपासून अस्तित्वात आहे." - "शॉर्टकट निवडा" - "अॅप निवडा" "अॅप्स" "मुख्‍यपृष्‍ठ" - "विस्थापित करा" "काढा" "विस्थापित करा" "अॅप माहिती" - "अॅप्स" - "काढा" - "अद्यतन विस्थापित करा" - "अॅप विस्थापित करा" - "अॅप तपशील" - "1 अॅप निवडला" - "1 विजेट निवडले" - "1 फोल्डर निवडले" - "1 शॉर्टकट निवडला" "शॉर्टकट स्‍थापित करा" "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते." - "शॉर्टकट विस्‍थापित करा" - "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट काढण्यास अॅप ला अनुमती देते." "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते." "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा" @@ -77,29 +45,18 @@ "विजेट लोड करण्यात समस्या" "सेटअप" "हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही." - "रॉकेट लाँचर" "अनामित फोल्डर" "मुख्य स्क्रीन %1$d" "%2$d पैकी %1$d पृष्ठ" "%2$d पैकी %1$d मुख्य स्क्रीन" - "%2$d पैकी %1$d Apps पृष्ठ" - "%2$d पैकी %1$d विजेट पृष्ठ" "सुस्वागतम" - "जसे पाहिजे तसे वापरा." - - - "अॅप्स आणि फोल्डरसाठी आणखी स्क्रीन तयार करा" "आपली अॅप चिन्हे कॉपी करा" "आपल्या जुन्या मुख्य स्क्रीनवरून चिन्हे आणि फोल्डर आयात करायची?" "चिन्हे कॉपी करा" "नव्याने प्रारंभ करा" - "आपले स्थान व्यवस्थापित करा" - "वॉलपेपर, विजेट आणि सेटिंग्ज व्यवस्थापित करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरून ठेवा." "वॉलपेपर, विजेट आणि सेटिंग्ज" "सानुकूल करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरुन ठेवा" "समजले" - "येथे एक फोल्डर आहे" - "यासारखे एखादे तयार करण्यासाठी अॅप ला स्पर्श करा आणि धरून ठेवा, नंतर तो दुसर्‍यावर हलवा." "ठीक" "फोल्डर उघडले, %1$d बाय %2$d" "फोल्डर बंद करण्यासाठी स्पर्श करा" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index fb28bc358..a9344f5b5 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Laman Utama" - "Apl Teras Android" "Apl tidak dipasang." "Apl tidak tersedia" "Apl yang dimuat turun dilumpuhkan dalam mod Selamat" "Widget dilumpuhkan dalam mod Selamat" - "Widget" - "Widget" "Papar Mem" "Sentuh & tahan untuk mengambil widget." "%1$d × %2$d" - "Tidak dapat melepaskan item pada Skrin Utama." - "Pilih widget yang hendak dibuat" - "Nama folder" - "Namakan semula folder" - "OK" - "Batal" - "Tambah ke skrin Laman Utama" - "Apl" - "Pintasan" - "Widget" - "Tiada lagi ruang pada skrin Laman Utama anda." "Tiada lagi ruang pada skrin Laman Utama ini." "Tiada ruang dalam dulang Kegemaran lagi" - "Widget ini terlalu besar untuk dulang Kegemaran" - "Pintasan \"%s\" telah dibuat." - "Pintasan \"%s\" telah dialih keluar." - "Pintasan \"%s\" sudah wujud." - "Pilih jalan pintas" - "Pilih apl" "Apl" "Laman Utama" - "Nyahpasang" "Alih keluar" "Nyahpasang" "Maklumat apl" - "Apl" - "Alih keluar" - "Nyahpasang kemas kini" - "Nyahpasang apl" - "Butiran apl" - "1 apl dipilih" - "1 widget dipilih" - "1 folder dipilih" - "1 pintasan dipilih" "pasang pintasan" "Membenarkan apl menambah pintasan tanpa campur tangan pengguna." - "nyahpasang pintasan" - "Membenarkan apl mengalih keluar pintasan tanpa campur tangan pengguna." "baca tetapan dan pintasan Laman Utama" "Membenarkan apl membaca tetapan dan pintasan di Laman Utama." "tulis tetapan dan pintasan Laman Utama" @@ -77,29 +45,18 @@ "Masalah memuatkan widget" "Persediaan" "Ini ialah apl sistem dan tidak boleh dinyahpasang." - "Pelancar Roket" "Folder Tanpa Nama" "Skrin Laman Utama %1$d" "Halaman %1$d daripada %2$d" "Skrin Laman Utama %1$d daripada %2$d" - "Halaman apl %1$d daripada %2$d" - "Halaman widget %1$d daripada %2$d" "Selamat datang" - "Buat seperti berada di rumah sendiri." - - - "Buat lebih banyak skrin untuk apl dan folder" "Salin ikon apl anda" "Import ikon dan folder dari skrin Laman Utama lama anda?" "SALIN IKON" "MULAKAN YANG BAHARU" - "Susun ruang anda" - "Sentuh & tahan latar belakang untuk mengurus kertas dinding, widget dan tetapan." "Kertas dinding, widget & tetapan" "Sentuh & tahan latar belakang untuk memperibadikan" "FAHAM" - "Ini ada folder" - "Untuk membuat satu folder seperti ini, sentuh & tahan apl, kemudian alihkan ke atas folder lain." "OK" "Folder dibuka, %1$d kali %2$d" "Sentuh untuk menutup folder" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 248ca1517..de274d7d7 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -21,69 +21,31 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Pelancar" "Laman Utama" - "Apl Teras Android" "Tetapkan kertas dinding" "Kertas dinding" "Aplikasi tidak dipasang." - "Widget" "Sentuh & tahan untuk mengambil widget." "%1$d × %2$d" - "Tidak dapat melepaskan item pada skrin Utama ini." - "Pilih widget untuk dibuat" - "Nama folder" - "Namakan semula folder" - "OK" - "Batal" - "Tambah ke Skrin utama" - "Aplikasi" - "Pintasan" - "Widget" - "Tiada lagi ruang pada skrin Utama anda." "Tiada lagi ruang pada skrin Utama ini" "Tiada lagi ruang pada kerusi panas." - "Widget ini terlalu besar untuk kerusi panas." - "Pintasan \"%s\" diwujudkan." - "Pintasan \"%s\" telah dialih keluar." - "Pintasan \"%s\" sudah pun wujud." - "Pilih pintasan" - "Pilih aplikasi" "Apl" "Laman Utama" - "Nyahpasang" "Alih keluar" "Nyahpasang" "Maklumat apl" - "Aplikasi" - "Alih keluar" - "Nyahpasang kemas kini" - "Nyahpasang aplikasi" - "Butiran aplikasi" - "1 aplikasi dipilih" - "1 widget dipilih" - "1 folder dipilih" - "1 pintasan dipilih" "pasang pintasan" "Membenarkan aplikasi menambah pintasan tanpa campur tangan pengguna." - "nyahpasang pintasan" - "Membenarkan apl mengalih keluar pintasan tanpa campur tangan pengguna." "membaca tetapan dan pintasan Laman Utama" "Membenarkan apl membaca tetapan dan pintasan di Laman Utama." "menulis tetapan dan pintasan Laman Utama" "Membenarkan apl menukar tetapan dan pintasan di Laman Utama." "Masalah memuatkan widget" "Ini adalah aplikasi sistem dan tidak boleh dinyahpasang." - "Pelancar Roket" "Folder Tanpa Nama" "Skrin utama %1$d" "Halaman %1$d dari %2$d" "Skrin utama %1$d dari %2$d" - "Halaman apl %1$d dari %2$d" - "Halaman widget %1$d dari %2$d" - "Buat diri anda seperti di rumah" - "Anda boleh meletakkan aplikasi kegemaran anda di sini." - "Susun aplikasi anda dengan folder" - "Untuk membuat folder baharu pada skrin Utama anda, tindihkan satu aplikasi di atas yang lain." "OK" "Folder dibuka, %1$d kali %2$d" "Sentuh untuk menutup folder" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index c6bbc1ab1..fc3f4a4d1 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher၃" "ပင်မစာမျက်နှာ" - "Androidပင်မ အပ်ပလီကေးရှင်းများ" "အပ်ပလီကေးရှင်း မထည့်သွင်းထားပါ" "App လက်လှမ်း မမှီပါ" "ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား" "လုံခြုံရေး မုဒ်ထဲမှာ ဝီဂျက်များကို ပိတ်ထား" - "ဝဒ်ဂျက်များ" - "ဝဒ်ဂျက်များ" "Mem ကိုပြရန်" "ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ" "%1$d × %2$d" - "ပင်မမျက်နှာစာတွင် အရာများ ချ လို့ မရတော့ပါ" - "ဝဒ်ဂျက်တစ်ခုအား ပြုဖန်တီးရန် ရွေးပါ" - "အကန့်အမည်" - "အကန့်အမည်ပြောင်းရန်" - "ကောင်းပြီ" - "ထားတော့" - "ပင်မမျက်နှာစာသို့ ထည့်ပါ" - "အပ်ပလီကေးရှင်းများ" - "အတိုကောက်မှတ်သားမှုများ" - "ဝဒ်ဂျက်များ" - "ပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" "အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ" - "ဤဝဒ်ဂျက်မှာ အနှစ်သက်ဆုံးအရာများထားရာနေရာ အတွက် ကြီးလွန်းနေပါသည်" - "အတိုကောက်မှတ်သားမှု \"%s\" ကို ပြုလုပ်ပြီးပါပြီ" - "အတိုကောက်မှတ်သားမှု \"%s\" ကို ဖယ်ရှားပြီးပါပြီ" - "အတိုကောက်မှတ်သားမှု \"%s\" ရှိပြီးသား ဖြစ်နေပါသည်" - "အတိုကောက်မှတ်သားမှုကို ရွေးရန်" - "အပလီကေးရှင်း တစ်ခုခုကို ရွေးချယ်ပါ" "အပ်ပလီကေးရှင်းများ" "ပင်မစာမျက်နှာ" - "ဖယ်ရှားခြင်း" "ဖယ်ရှာခြင်း" "ဖယ်ရှားခြင်း" "အပ်ပလီကေးရှင်း အချက်အလက်များ" - "အပ်ပလီကေးရှင်းများ" - "ဖယ်ရှာခြင်း" - "အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ" - "အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ" - "အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်" - "အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး" - "ဝဒ်ဂျက်တစ်ခု ရွေးချယ်ထားပြီး" - "အကန့် တစ်ခု ရွေးချယ်ထားပြီး" - "အတိုကောက်မှတ်သားမှုတစ်ခု ရွေးချယ်ထားပြီး" "အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း" "အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း" - "အတိုကောက်မှတ်သားမှုများ ဖယ်ထုတ်ခြင်း" - "အပ်ပလီကေးရှင်းအား အသုံးပြုသူ မပါဝင်ပဲ အတိုကောက်မှတ်သားမှုများ ဖယ်ရှားခွင့် ပြုခြင်း" "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း" "ပင်မမျက်နှာစာတွင်ရှိသော အပြင်အဆင်နှင့် အတိုကောက်မှတ်သားမှုများကို အပ်ပလီကေးရှင်းအား ဖတ်ခွင့်ပြုခြင်း" "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း" @@ -77,29 +45,18 @@ "ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်" "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" - "ဒုံပျံ ပစ်လွှတ်သောအရာ" "အမည်မရှိအကန့်" "ပင်မစာမျက်နှာ %1$d" "စာမျက်နှာ %1$d မှ %2$d" "ပင်မစာမျက်နှာ %1$d မှ %2$d" - "အပ်ပလီကေးရှင်းပြ စာမျက်နှာ %1$d မှ %2$d" - "ဝဒ်ဂျက်ပြ စာမျက်နှာ %1$d မှ %2$d" "မင်္ဂလာပါ" - "ကိုယ့်အိမ်လို သဘောထားပါ" - - - "အပ်ပလီကေးရှင်း နှင့် အကန့်များအတွက် ဖန်သားပြင်မှာ ထပ်ထည့်ပါ" "အပ်ပလီကေးရှင်းပုံညွှန်းများကို ကူးယူပါ" "ပင်မစာမျက်နှာအဟောင်းမှ ပုံညွှန်းများ နှင့် အကန့်များကို ယူလာပါမလား" "COPY ICONS" "START FRESH" - "စနစ်တကျဖြစ်အောင် ပြုလုပ်ပါ" - "နောက်ခံကို ဖိကိုင်၍ နောက်ခံပုံ၊ဝဒ်ဂျက်များ၊အပြင်အဆင်များကို ထိန်းချုပ်ပါ" "နောက်ခံများ၊ ဝီဂျက်များ& ဆက်တင်များ" "နောက်ခံကို စိတ်တိုင်းကျ ပြုလုပ်ရန် ထိလျက် & ကိုင်ထားပါ" "ရပြီ" - "ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်" - "ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ" "ကောင်းပြီ" "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index fbed8c6e6..386019ce6 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Startside" - "Kjerneapper for Android" "Appen er ikke installert." "Appen er ikke tilgjengelig" "En nedlastet app er deaktivert i sikker modus" "Moduler er deaktivert i sikker modus" - "Moduler" - "Moduler" "Vis minne" "Trykk og hold inne for å plukke opp en modul." "%1$d × %2$d" - "Kunne ikke slippe elementet på denne startsiden." - "Velg modul for oppretting" - "Mappenavn" - "Gi mappen nytt navn" - "OK" - "Avbryt" - "Legg til på startsiden" - "Apper" - "Snarveier" - "Moduler" - "Ikke mer plass på startsidene dine." "Denne startsiden er full." "Favoritter-skuffen er full" - "Denne modulen er for stor for Favoritter-skuffen." - "Snarveien «%s» er opprettet." - "Snarveien «%s» er fjernet." - "Snarveien «%s» fins allerede." - "Valg av snarvei" - "Velg app" "Apper" "Startside" - "Avinstaller" "Fjern" "Avinstaller" "App-info" - "Apper" - "Fjern" - "Avinstaller oppdateringen" - "Avinstaller appen" - "Informasjon om appen" - "Én app er valgt" - "Én modul er valgt" - "Én mappe er valgt" - "Én snarvei er valgt" "installere snarveier" "Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren." - "avinstallere snarveier" - "Lar appen fjerne snarveier uten innblanding fra brukeren." "lese startsideinnstillinger og -snarveier" "Lar appen lese innstillingene og snarveiene på startsiden." "angi startsideinnstillinger og -snarveier" @@ -77,29 +45,18 @@ "Problem ved innlasting av modul" "Konfigurering" "Dette er en systemapp som ikke kan avinstalleres." - "Rocket Launcher" "Mappe uten navn" "Startside %1$d" "Side %1$d av %2$d" "Startside %1$d av %2$d" - "Appside %1$d av %2$d" - "Modulside %1$d av %2$d" "Velkommen" - "Føl deg som hjemme." - - - "Opprett flere sider for apper og mapper" "Kopiér appikonene dine" "Vil du importere ikoner og mapper fra dine gamle startsider?" "KOPIÉR IKONENE" "START PÅ NYTT" - "Organiser plassen din" - "Trykk og hold på bakgrunnen for å administrere bakgrunnen, moduler og innstillinger." "Bakgrunner, moduler og innstillinger" "Trykk og hold på bakgrunnen for å tilpasse den" "SKJØNNER" - "Dette er en mappe" - "For å opprette en som denne, trykker og holder du på en app og flytter den over en annen." "OK" "Mappen er åpnet – %1$d ganger %2$d" "Trykk for å lukke mappen" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 41ec64596..9efc04fd6 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "गृह" - "Android मूल अनुप्रयोगहरू" "अनुप्रयोग स्थापित छैन।" "अनुप्रयोग उपलब्ध छैन" "सुरक्षित मोडमा डाउनलोड गरेको अनुप्रयोग अक्षम गरिएको छ" "सुरक्षित मोडमा विगेटहरू अक्षम गरियो" - "विजेटहरू" - "विजेटहरू" "Mem देखाउनुहोस्" "एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।" "%1$d × %2$d" - "यो गृह स्क्रिनमा वस्तु खसाउन सकिँदैन।" - "सृजना गर्नको लागि विजेट छान्नुहोस्" - "फोल्डरको नाम" - "फोल्डरलाई पुनःनामाकरण गर्नुहोस्" - "ठिक छ" - "रद्द गर्नुहोस्" - "गृह स्क्रिनमा थप्नुहोस्" - "अनुप्रयोगहरू" - "सर्टकटहरू" - "विजेटहरू" - "यो गृह स्क्रिनहरूमा कुनै थप ठाउँ छैन" "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" "मनपर्ने ट्रे अब कुनै ठाँउ छैन" - "यो विजेट मनपर्ने ट्रे को लागि निकै ठूलो छ" - "सर्टकट \"%s\" सिर्जित गरियो।" - "सर्टकट \"%s\" हटाइयो।" - "सर्टकट \"%s\" पहिल्यै अवस्थित छ।" - "सर्टकट छान्नुहोस्" - "अनुप्रयोग छनौट गर्नुहोस्" "अनुप्रयोगहरू" "गृह" - "हटाउनुहोस्" "हटाउनुहोस्" "हटाउनुहोस्" "अनुप्रयोग जानकारी" - "अनुप्रयोगहरू" - "हटाउनुहोस्" - "अद्यावधिक अस्थापित गर्नुहोस्" - "अनुप्रयोग अस्थापना गर्नुहोस्" - "अनुप्रयोग विवरणहरु" - "१ अनुप्रयोग चयन गरियो" - "१ विजेट चयन गरियो" - "१ फोल्डर चयन गरियो" - "१ सर्टकट चयन गरियो" "सर्टकट स्थापना गर्नेहोस्" "प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।" - "सर्टकटहरूको स्थापन रद्द गर्नुहोस्" - "अनुप्रयोगलाई उपयोगकर्ताको हस्तक्षेप बिना सर्टकटहरूलाई हटाउन अनुमति दिनुहोस्।" "गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्" "गृहमा एउटा अनुप्रयोगलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।" "गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्" @@ -77,31 +45,21 @@ "समस्या लोडिङ गर्ने विजेट" "सेटअप" "यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।" - "रकेट लन्चर" "बेनाम फोल्डर" "गृह स्क्रिन %1$d" "पृष्ठ %2$d को %1$d" "गृह स्क्रिन %2$d को %2$d" - "अनुप्रयोग पृष्ठ %2$d को %1$d" "स्वागतम" - "गृह आरामसँग बस्नुहोस्" - - - "अनुप्रयोगहरु र फोल्डरहरुलाई थप स्क्रीनहरु सिर्जना गर्नुहोस्" "तपाईँको अनुप्रयोग आईकनको प्रतिलिप गर्नुहोस्" "आफ्नो पुरानो गृह स्क्रीनबाट अाईकन र फोल्डरहरू आयात गर्नुहोस्?" "ICONS प्रतिलिप गर्नुहोस्" "START FRESH" - "आफ्नो ठाउँ व्यवस्थापन गर्नुहोस्" - "वालपेपर, विजेट र सेटिङ्स प्रबन्ध गर्न पृष्ठभूमिलाई टच गरेर होल्ड गर्नुहोस्।" "वालपेपरहरू, विजेट; सेटिङहरू" "छुनुहोस् ; अनुकूलन पृष्ठभूमि होल्ड गर्नुहोस्" "बुझियो" - "यहाँ एउटा फोल्डर छ" - "यस्तै एक किसिमका सिर्जना गर्न, अनुप्रयोगलाई टच गरेर होल्ड गर्नुहोस्, त्यसपछि यसलाई अर्को माथि सार्नुहोस्।" "ठिक छ" "फोल्डर खुल्यो %1$d बाट %2$d" "फोल्डर बन्द गर्नको लागि टच गर्नुहोस्" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index a3289dae7..d9b6aea2e 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Startpagina" - "Android-kernapps" "App is niet geïnstalleerd." "App is niet beschikbaar" "Gedownloade app uitgeschakeld in veilige modus" "Widgets uitgeschakeld in Veilige modus" - "Widgets" - "Widgets" "Geheugen weergeven" "Blijf aanraken om een widget toe te voegen." "%1$d × %2$d" - "Kan item niet neerzetten in dit startscherm." - "Widget selecteren om te maken" - "Mapnaam" - "Naam van map wijzigen" - "OK" - "Annuleren" - "Toevoegen aan startscherm" - "Apps" - "Snelkoppelingen" - "Widgets" - "Er is geen ruimte meer op uw startschermen." "Er is geen ruimte meer op dit startscherm." "Geen ruimte meer in het vak \'Favorieten\'" - "Deze widget is te groot voor het vak \'Favorieten\'" - "Snelkoppeling \'%s\' is gemaakt." - "Snelkoppeling \'%s\' is verwijderd." - "Snelkoppeling \'%s\' bestaat al." - "Snelkoppeling selecteren" - "App selecteren" "Apps" "Startpagina" - "Verwijderen" "Verwijderen" "Verwijderen" "App-info" - "Apps" - "Verwijderen" - "Update verwijderen" - "App verwijderen" - "App-details" - "1 app geselecteerd" - "1 widget geselecteerd" - "1 map geselecteerd" - "1 snelkoppeling geselecteerd" "snelkoppelingen installeren" "Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker." - "snelkoppelingen verwijderen" - "De app toestaan snelkoppelingen te verwijderen zonder tussenkomst van de gebruiker." "instellingen en snelkoppelingen op de startpagina lezen" "De app toestaan de instellingen en snelkoppelingen op de startpagina te lezen." "instellingen en snelkoppelingen op de startpagina schrijven" @@ -77,29 +45,18 @@ "Probleem bij het laden van widget" "Configuratie" "Dit is een systeemapp die niet kan worden verwijderd." - "Rocket Launcher" "Naamloze map" "Startscherm %1$d" "Pagina %1$d van %2$d" "Startscherm %1$d van %2$d" - "App-pagina %1$d van %2$d" - "Widgetpagina %1$d van %2$d" "Welkom" - "Personaliseer uw startscherm." - - - "Meer schermen maken voor apps en mappen" "Uw app-pictogrammen kopiëren" "Pictogrammen en mappen importeren uit uw oude startschermen?" "PICTOGRAMMEN KOPIËREN" "OPNIEUW BEGINNEN" - "Uw ruimte indelen" - "Blijf de achtergrond aanraken om de achtergrond, widgets en instellingen te beheren." "Achtergronden, widgets en instellingen" "Blijf de achtergrond aanraken om deze aan te passen" "OK" - "Dit is een map" - "Als u een map zoals deze wilt maken, blijft u een app aanraken en schuift u deze boven op een andere app." "OK" "Map geopend, %1$d bij %2$d" "Raak dit aan om de map te sluiten" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index aad731611..5506781bb 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Ekran główny" - "Główne aplikacje Androida" "Aplikacja nie jest zainstalowana." "Aplikacja niedostępna" "Pobrana aplikacja została wyłączona w trybie awaryjnym" "Widżety są wyłączone w trybie bezpiecznym" - "Widżety" - "Widżety" "Pokaż pamięć" "Aby dodać widżet, kliknij go i przytrzymaj." "%1$d × %2$d" - "Nie można upuścić elementu na tym ekranie głównym." - "Wybierz widżet, który chcesz dodać" - "Nazwa folderu" - "Zmień nazwę folderu" - "OK" - "Anuluj" - "Dodaj do ekranu głównego" - "Aplikacje" - "Skróty" - "Widżety" - "Brak miejsca na ekranach głównych." "Brak miejsca na tym ekranie głównym." "Brak miejsca w Ulubionych" - "Ten widżet jest za duży, by zmieścił się w Ulubionych" - "Skrót „%s” został utworzony." - "Skrót „%s” został usunięty." - "Skrót „%s” już istnieje." - "Wybierz skrót" - "Wybierz aplikację" "Aplikacje" "Ekran główny" - "Odinstaluj" "Usuń" "Odinstaluj" "Informacje o aplikacji" - "Aplikacje" - "Usuń" - "Odinstaluj aktualizację" - "Odinstaluj aplikację" - "Szczegóły aplikacji" - "Wybrano 1 aplikację" - "Wybrano 1 widżet" - "Wybrano 1 folder" - "Wybrano 1 skrót" "instalowanie skrótów" "Pozwala aplikacji dodawać skróty bez interwencji użytkownika." - "odinstalowywanie skrótów" - "Pozwala aplikacji usuwać skróty bez interwencji użytkownika." "odczytywanie ustawień i skrótów na ekranie głównym" "Pozwala aplikacji na odczytywanie ustawień i skrótów na ekranie głównym." "zapisywanie ustawień i skrótów na ekranie głównym" @@ -77,29 +45,18 @@ "Problem podczas ładowania widżetu" "Konfiguracja" "To aplikacja systemowa i nie można jej odinstalować." - "Wyrzutnia rakiet" "Folder bez nazwy" "Ekran główny %1$d" "Strona %1$d z %2$d" "Ekran główny %1$d z %2$d" - "Strona aplikacji: %1$d z %2$d" - "Strona widżetów: %1$d z %2$d" "Witamy" - "Poczuj się jak u siebie." - - - "Dodaj więcej ekranów na aplikacje i foldery" "Kopiuj ikony aplikacji" "Zaimportować ikony i foldery ze starych ekranów głównych?" "KOPIUJ IKONY" "ZACZNIJ OD NOWA" - "Uporządkuj obszar roboczy" - "Kliknij i przytrzymaj tło, by zmienić tapetę, widżety lub ustawienia." "Tapety, widżety i ustawienia" "Kliknij i przytrzymaj tło, by dostosować" "OK" - "Tu jest folder" - "Aby utworzyć taki sam, kliknij i przytrzymaj aplikację, a następnie przenieś ją na następną." "OK" "Folder otwarty, %1$d na %2$d" "Kliknij, by zamknąć folder" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index a08bfc7c5..db2f793ae 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Iniciador3" "Ecrã principal" - "Aplicações principais do Android" "A aplicação não está instalada." "A aplicação não está disponível" "Aplicação transferida desativada no Modo de segurança" "Widgets desativados no Modo de segurança" - "Widgets" - "Widgets" "Mostrar mem" "Prima sem soltar para escolher um widget." "%1$d × %2$d" - "Não foi possível largar o item neste Ecrã Principal." - "Escolher um widget para criar" - "Nome da pasta" - "Mudar o nome da pasta" - "OK" - "Cancelar" - "Adicionar ao Ecrã principal" - "Aplicações" - "Atalhos" - "Widgets" - "Sem espaço suficiente nos Ecrãs principais." "Sem espaço suficiente neste Ecrã principal." "Não existe mais espaço no tabuleiro de Favoritos" - "Este widget é demasiado grande para o tabuleiro de Favoritos" - "Atalho “%s” criado." - "O atalho “%s” foi removido." - "O atalho “%s” já existe." - "Escolher atalho" - "Escolher aplicação" "Aplicações" "Ecrã principal" - "Desinstalar" "Remover" "Desinstalar" "Informações da aplicação" - "Aplicações" - "Remover" - "Desinstalar atualização" - "Desinstalar a aplicação" - "Detalhes da aplicação" - "1 aplicação selecionada" - "1 widget selecionado" - "1 pasta selecionada" - "1 atalho selecionado" "instalar atalhos" "Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador." - "desinstalar atalhos" - "Permite à aplicação remover atalhos sem intervenção do utilizador." "ler definições e atalhos do Ecrã Principal" "Permite à aplicação ler as definições e os atalhos no Ecrã Principal." "escrever definições e atalhos do Ecrã principal" @@ -77,29 +45,18 @@ "Problema ao carregar o widget" "Configuração" "É uma aplicação de sistema e não pode ser desinstalada." - "Lança-mísseis" "Pasta sem nome" "Ecrã principal %1$d" "Página %1$d de %2$d" "Ecrã principal %1$d de %2$d" - "Página de aplicações %1$d de %2$d" - "Página de widgets %1$d de %2$d" "Bem-vindo(a)" - "Sinta-se em casa." - - - "Crie mais ecrãs para aplicações e pastas" "Copiar ícones das aplicações" "Importar ícones e pastas dos ecrãs principais antigos?" "COPIAR ÍCONES" "COMEÇAR DO INÍCIO" - "Organizar o seu espaço" - "Toque sem soltar no fundo para gerir a imagem de fundo, os widgets e as definições." "Imagens de fundo, widgets e definições" "Toque sem soltar no fundo para personalizar" "COMPREENDI" - "Eis uma pasta" - "Para criar uma pasta, toque sem soltar numa aplicação e arraste-a para cima de outra aplicação." "OK" "Pasta aberta, %1$d por %2$d" "Toque para fechar a pasta" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index d92f8e95d..7172b24e7 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Início" - "Principais apps do Android" "O app não está instalado." "O app não está disponível" "App transferido por download desativado no modo de segurança" "Widgets desativados no modo de segurança" - "Widgets" - "Widgets" "Mostrar memória" "Toque e pressione para selecionar um widget." "%1$d × %2$d" - "Não foi possível soltar o item nesta tela inicial." - "Selecione um widget para criar" - "Nome da pasta" - "Renomear pasta" - "Ok" - "Cancelar" - "Adicionar à tela inicial" - "Apps" - "Atalhos" - "Widgets" - "Não há mais espaço nas telas iniciais." "Não há mais espaço na tela inicial." "Sem espaço na bandeja de favoritos" - "O widget é muito grande para a bandeja de favoritos" - "Atalho \"%s\" criado." - "O atalho \"%s\" foi removido." - "O atalho \"%s\" já existe." - "Selecione um atalho" - "Selecione um app" "Apps" "Início" - "Desinstalar" "Remover" "Desinstalar" "Informações do app" - "Apps" - "Remover" - "Desinstalar atualização" - "Desinstalar app" - "Detalhes do app" - "Um app selecionado" - "Um widget selecionado" - "Uma pasta selecionada" - "Um atalho selecionado" "instalar atalhos" "Permite que um app adicione atalhos sem intervenção do usuário." - "desinstalar atalhos" - "Permite que o app remova atalhos sem a intervenção do usuário." "ler configurações e atalhos da tela inicial" "Permite que o app leia as configurações e os atalhos na tela inicial." "gravar configurações e atalhos da tela inicial" @@ -77,29 +45,18 @@ "Problema ao carregar o widget" "Configuração" "Este é um app do sistema e não pode ser desinstalado." - "Rocket Launcher" "Pasta sem nome" "Tela inicial %1$d" "Página %1$d de %2$d" "Tela inicial %1$d de %2$d" - "Página de apps, %1$d de %2$d" - "Página de widgets, %1$d de %2$d" "Bem-vindo" - "Fique à vontade." - - - "Crie mais telas para apps e pastas" "Copiar ícones de apps" "Importar ícones e pastas de suas telas iniciais antigas?" "COPIAR ÍCONES" "COMEÇAR DO ZERO" - "Organize seu espaço" - "Toque e mantenha pressionada a tela de fundo para gerenciar o plano de fundo, os widgets e as configurações." "Plano de fundo, widgets e configurações" "Toque e mantenha pressionado o segundo plano para personalizar" "ENTENDI" - "Aqui está uma pasta" - "Para criar uma pasta como esta, mantenha pressionado um app e mova-o para cima de outro." "Ok" "Pasta aberta, %1$d por %2$d" "Toque para fechar a pasta" diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml index be35c6b09..0758148f5 100644 --- a/res/values-rm/strings.xml +++ b/res/values-rm/strings.xml @@ -152,8 +152,6 @@ - - diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 832e4a5f8..b2da9fba4 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Ecran de pornire" - "Android Core Apps" "Aplicația nu este instalată." "Aplicația nu este disponibilă" "Aplicația descărcată este dezactivată în modul de siguranță" "Widgeturile sunt dezactivate în modul de siguranță" - "Widgeturi" - "Widgeturi" "Afișați memoria" "Atingeți lung un widget pentru a-l alege." "%1$d × %2$d" - "Nu se poate plasa articolul pe ecranul de pornire." - "Alegeți widgetul de creat" - "Numele dosarului" - "Redenumiți dosarul" - "OK" - "Anulați" - "Adăugați la Ecranul de pornire" - "Aplicații" - "Comenzi rapide" - "Widgeturi" - "Nu mai este loc pe ecranele de pornire." "Nu mai este loc pe acest Ecran de pornire." "Spațiu epuizat în bara Preferate" - "Acest widget este prea mare pentru bara Preferate" - "Comanda rapidă „%s\" a fost creată." - "Comanda rapidă „%s” a fost eliminată." - "Comanda rapidă „%s” există deja." - "Alegeți comanda rapidă" - "Alegeți aplicația" "Aplicații" "Ecran de pornire" - "Dezinstalați" "Eliminați" "Dezinstalați" "Informații despre aplicație" - "Aplicații" - "Eliminați" - "Dezinstalați actualizarea" - "Dezinstalați aplicația" - "Detalii despre aplicație" - "1 aplicație selectată" - "1 widget selectat" - "1 dosar selectat" - "1 comandă rapidă selectată" "instalează comenzi rapide" "Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului." - "dezinstalează comenzi rapide" - "Permite aplicației să elimine comenzi rapide fără intervenția utilizatorului." "citește setări și comenzi rapide pentru ecranul de pornire" "Permite aplicației să citească setările și comenzile rapide din ecranul de pornire." "scrie setări și comenzi rapide pentru ecranul de pornire" @@ -77,29 +45,18 @@ "Problemă la încărcarea widgetului" "Configurați" "Aceasta este o aplicație de sistem și nu poate fi dezinstalată." - "Rocket Launcher" "Dosar fără nume" "Ecran de pornire %1$d" "Pagina %1$d din %2$d" "Ecranul de pornire %1$d din %2$d" - "Pagina de aplicații %1$d din %2$d" - "Pagina de widgeturi %1$d din %2$d" "Bun venit" - "Simțiți-vă ca acasă." - - - "Creați mai multe ecrane pentru aplicații și dosare" "Copiați pictogr. aplicațiilor" "Import. pictogr. și dosare de pe ecranele de pornire anter.?" "COPIAȚI PICTOGRAMELE" "REÎNCEPEȚI" - "Organizați-vă spațiul" - "Atingeți lung fundalul pentru a gestiona imaginea de fundal, widgeturile și setările." "Imagini de fundal, widgeturi și setări" "Atingeți lung fundalul pentru a-l personaliza" "AM ÎNȚELES" - "Iată un dosar" - "Pentru a crea un dosar similar, atingeți și țineți degetul pe o aplicație, apoi mutați-o deasupra alteia." "OK" "Dosar deschis, %1$d pe %2$d" "Atingeți pentru a închide dosarul" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 0341317c0..c0a8d023b 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Главный экран" - "Основные приложения Android" "Приложение удалено" "Приложение недоступно" "Скачанное приложение отключено в безопасном режиме" "Виджеты отключены в безопасном режиме" - "Виджеты" - "Виджеты" "Сведения о памяти" "Чтобы выбрать виджет, нажмите на значок и удерживайте его." "%1$d x %2$d" - "Не удалось добавить элемент на главный экран" - "Выберите виджет" - "Название папки" - "Переименование папки" - "ОК" - "Отмена" - "Добавление на главный экран" - "Приложения" - "Ярлыки" - "Виджеты" - "На главных экранах все занято" "На этом экране все занято" "В разделе \"Избранное\" больше нет места" - "Виджет слишком велик для раздела \"Избранное\"" - "Ярлык \"%s\" создан" - "Ярлык \"%s\" удален" - "Ярлык \"%s\" уже существует" - "Выбор ярлыка" - "Выбор приложения" "Приложения" "Главный экран" - "Удалить" "Удалить" "Удалить" "О приложении" - "Приложения" - "Удалить" - "Удалить обновление" - "Удалить приложение" - "О приложении" - "Выбрано 1 приложение" - "Выбран 1 виджет" - "Выбрана 1 папка" - "Выбран 1 ярлык" "Создание ярлыков" "Приложение сможет самостоятельно добавлять ярлыки." - "Удаление ярлыков" - "Приложение сможет самостоятельно удалять ярлыки." "Доступ к настройкам и ярлыкам главного экрана" "Приложение получит доступ к данным о настройках и ярлыках на главном экране." "Изменение настроек и ярлыков главного экрана" @@ -77,29 +45,18 @@ "Не удалось загрузить виджет" "Настройка" "Это системное приложение, его нельзя удалить." - "Rocket Launcher" "Папка без названия" "Главный экран %1$d" "Стр. %1$d из %2$d" "Главные экран %1$d из %2$d" - "Приложения: стр. %1$d из %2$d" - "Виджеты: стр. %1$d из %2$d" "Добро пожаловать!" - "Будьте как дома" - - - "Создание дополнительных экранов для приложений и папок" "Копировать значки приложений" "Импортировать значки и папки со старого главного экрана?" "КОПИРОВАТЬ ЗНАЧКИ" "ИСПОЛЬЗОВАТЬ СТАНДАРТНЫЙ МАКЕТ" - "Организация рабочего пространства" - "Чтобы перейти к управлению обоями, виджетами и настройками, нажмите на фоновое изображение и удерживайте его." "Обои, виджеты и настройки" "Чтобы выполнить настройку, коснитесь фона и удерживайте его" "ОК" - "Это папка" - "Чтобы создать папку, нажмите и удерживайте значок приложения, а затем перетащите его на другой значок." "ОК" "Папка открыта, %1$d x %2$d" "Нажмите, чтобы закрыть папку" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index 486baf8a1..3b8d81134 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "මුල් පිටුව" - "Android මධ්‍ය යෙදුම්" "යෙදුම ස්ථාපනය කර නැත." "යෙදුම නොතිබේ" "ආරක්ෂිත ආකාරය තුළ බාගන්න ලද යෙදුම් අබල කරන්න" "සුරක්ෂිත ආකාරය තුළ විජටය අබල කරන ලදි" - "විජට්" - "විජට්" "Mem පෙන්වන්න" "විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න." "%1$d × %2$d" - "මෙම මුල් පිටු තිරය වෙත අයිතමය ඇද හෙළිය නොහැකි විය." - "සැදීමට විජට් එක තෝරන්න" - "ෆෝල්ඩරයේ නම" - "ෆෝල්ඩරය නැවත නම් කරන්න" - "හරි" - "අවලංගු කරන්න" - "මුල් පිටු තිරය වෙත එක් කරන්න" - "යෙදුම්" - "කෙටිමං" - "විජට්" - "මෙම මුල් පිටු තිර මත තවත් ඉඩ නැත." "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." "ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත" - "ප්‍රියතම දෑ ඇති තැටිය සඳහා මෙම විජටය ඉතා විශාලය" - "\"%s\" කෙටිමග සාදන ලදි." - "\"%s\" කෙටිමග ඉවත් කෙරිණි." - "\"%s\" කෙටිමග දැනටමත් පවතී." - "කෙටිමග තේරීම" - "යෙදුම තේරීම" "යෙදුම්" "මුල් පිටුව" - "අස්ථාපනය කරන්න" "ඉවත් කරන්න" "අස්ථාපනය කරන්න" "යෙදුම් තොරතුරු" - "යෙදුම්" - "ඉවත් කරන්න" - "යාවත්කාලිනය අස්ථාපනය කරන්න" - "යෙදුම අස්ථාපනය කරන්න" - "යෙදුම් විස්තර" - "1 යෙදුමක් තෝරා ඇත" - "1 විජටයක් තෝරා ඇත" - "1 ෆෝල්ඩරයක් තෝරා ඇත" - "1 කෙටිමඟක් තෝරා ඇත" "කෙටිමං ස්ථාපනය කරන්න" "පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි." - "කෙටිමං අස්ථාපනය කරන්න" - "පරිශීලකයාගේ මැදිහත්වීමෙන් තොරව කෙටිමං ඉවත් කිරීමට යෙදුමකට අවසර දෙයි." "මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න" "මුල් පිටුවේ ඇති සැකසීම් සහ කෙටිමං කියවීමට යෙදුමකට අවසර දෙයි." "මුල් පිටු සැකසීම් සහ කෙටිමං ලියන්න" @@ -77,29 +45,18 @@ "ගැටලු පූරණ විජට් එක" "ස්ථාපනය කරන්න" "මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක." - "රොකට් ආරම්භකය" "නම් නොකළ ෆෝල්ඩරය" "මුල් පිටු තිරය %1$d" "%2$d හි %1$d පිටුව" "මුල් පිටු තිරය %2$d හි %1$d" - "%2$d හි %1$d යෙදුම් පිටුව" - "විජට් පිටුව %2$d හි %1$d" "සාදරයෙන් පිළිගනිමු" - "ගෙදර ඉන්නවා වගේ ඉන්න." - - - "යෙදුම් සහ ෆෝල්ඩර සඳහා තවත් තිර සාදන්න" "ඔබේ යෙදුම් නිරූපක පිටපත් කිරීම" "ඔබගේ පැරණි මුල් තිර වල නිරූපක සහ ෆෝල්ඩර ආයාත කරන්නද?" "නිරූපක පිටපත් කරන්න" "අලුතින් පටන්ගන්න" - "ඔබගේ ඉඩ සංවිධානය කරගන්න" - "බිතුපත, විජට් සහ සැකසීම් කළමනාකරණය කිරීමට පසුබිම ස්පර්ශ කර අල්ලාගෙන සිටින්න." "වෝල්පේපර, විජට්, සහ සැකසීම්" "පසුබිම අභිරුචිකරණය කිරීමට ස්පර්ශ කර අල්ලා සිටින්න" "තේරුණා" - "මෙන්න ෆෝල්ඩරයක්" - "මෙවැනි එකක් තැනීමට, යෙදුමක් තට්ටු කර අල්ලාගෙන සිටින්න, අනතුරුව එය තවත් එකක් උඩින් ගෙන යන්න." "හරි" "ෆෝල්ඩරය විවෘත විය, %1$d හි %2$d" "ෆෝල්ඩරය වැසීමට ස්පර්ශ කරන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 45e0d4b45..8cfb99227 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Plocha" - "Android Core Apps" "Aplikácia nie je nainštalovaná." "Aplikácia nie je k dispozícii" "Stiahnutá aplikácia je v núdzovom režime zakázaná" "Miniaplikácie sú v núdzovom režime zakázané" - "Miniaplikácie" - "Miniaplikácie" "Zobraziť pamäť" "Miniaplikáciu pridáte stlačením a podržaním." "%1$d × %2$d" - "Položku sa nepodarilo presunúť na túto plochu." - "Zvoľte miniaplikáciu na vytvorenie" - "Názov priečinka" - "Premenovať priečinok" - "OK" - "Zrušiť" - "Pridať na plochu" - "Aplikácie" - "Skratky" - "Miniaplikácie" - "Na plochách už nie je miesto." "Na tejto ploche už nie je miesto" "Na paneli Obľúbené položky už nie je miesto" - "Táto miniaplikácia je príliš veľká pre panel Obľúbené položky" - "Odkaz %s bol vytvorený." - "Odkaz %s bol odstránený." - "Odkaz %s už existuje." - "Vybrať odkaz" - "Vybrať aplikáciu" "Aplikácie" "Domovská stránka" - "Odinštalovať" "Odstrániť" "Odinštalovať" "Informácie o aplikácii" - "Aplikácie" - "Odstrániť" - "Odinštalovať aktualizáciu" - "Odinštalovať aplikáciu" - "Podrobnosti o aplikácii" - "Vybratá 1 aplikácia" - "Vybratá 1 miniaplikácia" - "Vybratý 1 priečinok" - "Vybratý 1 odkaz" "inštalovať odkazy" "Povoľuje aplikácii pridať odkazy bez zásahu používateľa." - "odinštalovať odkazy" - "Povoľuje aplikácii odstrániť odkazy bez zásahu používateľa." "čítanie nastavení a odkazov plochy" "Povoľuje aplikácii čítať nastavenia a odkazy na ploche." "zápis nastavení a odkazov plochy" @@ -77,29 +45,18 @@ "Problém s načítaním miniaplikácií" "Nastavenie" "Toto je systémová aplikácia a nedá sa odinštalovať." - "Raketomet" "Nepomenovaný priečinok" "Plocha %1$d" "Stránka %1$d z %2$d" "Plocha %1$d z %2$d" - "Stránka aplikácií %1$d z %2$d" - "Stránka miniaplikácií %1$d z %2$d" "Vitajte!" - "Cíťte sa tu ako doma." - - - "Vytvorte viac obrazoviek pre aplikácie a priečinky" "Kopírovanie ikon aplikácií" "Chcete importovať ikony a priečinky zo starých plôch?" "SKOPÍROVAŤ IKONY" "ZAČAŤ ODZNOVA" - "Usporiadajte svoj priestor" - "Ak chcete spravovať tapetu, miniaplikácie a nastavenia, dotknite sa pozadia a podržte." "Pozadia, miniaplikácie a nastavenia" "Ak si chcete pozadie prispôsobiť, klepnite naň a podržte ho" "ROZUMIEM" - "Tu je priečinok" - "Ak chcete vytvoriť takýto priečinok, dotknite sa príslušnej aplikácie a podržte ju. Potom ju presuňte na druhú aplikáciu." "OK" "Otvorený priečinok, %1$d x %2$d" "Dotykom zavriete priečinok" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 6ab289b75..63a4bac03 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Zaganjalnik3" "Začetni zaslon" - "Osnovne aplikacije sistema Android" "Aplikacija ni nameščena." "Aplikacija ni na voljo" "Prenesena aplikacija je onemogočena v Varnem načinu" "Pripomočki so onemogočeni v varnem načinu" - "Pripomočki" - "Pripomočki" "Pokaži pomnilnik" "Za izbiro pripomočka se ga dotaknite in pridržite." "%1$d × %2$d" - "Elementa ni mogoče spustiti na začetni zaslon." - "Izberite pripomoček za ustvarjanje" - "Ime mape" - "Preimenovanje mape" - "V redu" - "Prekliči" - "Dodaj na začetni zaslon" - "Aplikacije" - "Bližnjice" - "Pripomočki" - "Na začetnih zaslonih ni več prostora." "Na tem začetnem zaslonu ni več prostora." "V vrstici za priljubljene ni več prostora" - "Ta pripomoček je prevelik za vrstico s priljubljenimi" - "Bližnjica »%s« je ustvarjena." - "Bližnjica »%s« je bila odstranjena." - "Bližnjica »%s« že obstaja." - "Izberite bližnjico" - "Izberite aplikacijo" "Aplikacije" "Začetni zaslon" - "Odstrani" "Odstrani" "Odstrani" "Podatki o aplikaciji" - "Aplikacije" - "Odstrani" - "Odstrani posodobitev" - "Odstrani aplikacijo" - "Podrobnosti o aplikaciji" - "Izbrana je 1 aplikacija" - "Izbran je 1 pripomoček" - "Izbrana je 1 mapa" - "Izbrana je 1 bližnjica" "namestitev bližnjic" "Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika." - "odstranitev bližnjic" - "Aplikaciji dovoli odstranjevanje bližnjic brez posredovanja uporabnika." "branje nastavitev in bližnjic na začetnem zaslonu" "Aplikaciji dovoli branje nastavitev in bližnjic na začetnem zaslonu." "zapis nastavitev in bližnjic na začetnem zaslonu" @@ -77,29 +45,18 @@ "Težava pri nalaganju pripomočka" "Nastavitev" "To je sistemska aplikacija in je ni mogoče odstraniti." - "Raketno izstrelišče" "Neimenovana mapa" "Začetni zaslon %1$d" "Stran %1$d od %2$d" "Začetni zaslon %1$d od %2$d" - "Stran aplikacij %1$d od %2$d" - "Stran pripomočkov %1$d od %2$d" "Pozdravljeni" - "Počutite se kot doma." - - - "Ustvarite več zaslonov za aplikacije in mape" "Kopiranje ikon aplikacij" "Želite uvoziti ikone in mape iz starih začetnih zaslonov?" "KOPIRAJ IKONE" "SVEŽ ZAČETEK" - "Organizirajte svoj prostor" - "Če želite upravljati ozadje, pripomočke in nastavitve, se dotaknite ozadja in ga pridržite." "Ozadja, pripomočki in nastavitve" "Za prilagajanje se dotaknite ozadja in ga pridržite" "V REDU" - "To je mapa" - "Če želite ustvariti mapo, podobno tej, se dotaknite aplikacije in jo pridržite, nato pa jo premaknite nad drugo." "V redu" "Mapa je odprta, %1$d krat %2$d" "Dotaknite se, da zaprete mapo" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 66ff8923d..955c44cc9 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Почетна" - "Основне Android апликације" "Апликација није инсталирана." "Апликација није доступна" "Преузета апликација је онемогућена у Безбедном режиму" "Виџети су онемогућени у Безбедном режиму" - "Виџети" - "Виџети" "Прикажи меморију" "Додирните и задржите да бисте изабрали виџет." "%1$d×%2$d" - "Није могуће отпустити ставку на почетни екран." - "Избор виџета за прављење" - "Назив директоријума" - "Преименовање директоријума" - "Потврди" - "Откажи" - "Додавање на почетни екран" - "Апликације" - "Пречице" - "Виџети" - "Нема више простора на почетним екранима." "Нема више простора на овом почетном екрану." "Нема више простора на траци Омиљено" - "Виџет је превелик за траку Омиљено" - "Пречица „%s“ је направљена." - "Пречица „%s“ је уклоњена." - "Пречица „%s“ већ постоји." - "Избор пречице" - "Избор апликације" "Апликације" "Почетна" - "Деинсталирај" "Уклони" "Деинсталирај" "Информације о апликацији" - "Апликације" - "Уклони" - "Деинсталирај ажурирање" - "Деинсталирање апликације" - "Детаљи о апликацији" - "Изабрана је 1 апликација" - "Изабран је 1 виџет" - "Изабран је 1 директоријум" - "Изабрана је 1 пречица" "инсталирање пречица" "Дозвољава апликацији да додаје пречице без интервенције корисника." - "деинсталирање пречица" - "Дозвољава апликацији да уклања пречице без интервенције корисника." "читање подешавања и пречица на почетном екрану" "Дозвољава апликацији да чита подешавања и пречице на почетном екрану." "уписивање подешавања и пречица на почетном екрану" @@ -77,29 +45,18 @@ "Проблем при учитавању виџета" "Подешавање" "Ово је системска апликација и не може да се деинсталира." - "Лансер ракета" "Неименовани директоријум" "Почетни екран %1$d" "%1$d. страница од %2$d" "%1$d. почетни екран од %2$d" - "%1$d. страница апликација од %2$d" - "%1$d. страница виџета од %2$d" "Добро дошли" - "Осећајте се као код куће." - - - "Направите још екрана за апликације и директоријуме" "Копирајте иконе апликација" "Увести иконе и директоријуме са старих почетних екрана?" "КОПИРАЈТЕ ИКОНЕ" "ПОЧНИТЕ ИСПОЧЕТКА" - "Организујте простор" - "Додирните позадину и задржите да бисте управљали позадином, виџетима и подешавањима." "Позадине, виџети и подешавања" "Додирните и задржите позадину да бисте прилагодили" "ВАЖИ" - "Ево једног директоријума" - "Да бисте направили директоријум попут овога, додирните и задржите апликацију, па је превуците преко друге." "Потврди" "Директоријум је отворен, %1$d пута %2$d" "Додирните да бисте затворили директоријум" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 13256bed7..636cabc1f 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Startskärm" - "Android Core Apps" "Appen är inte installerad." "Appen är inte tillgänglig" "Den hämtade appen inaktiverades i säkert läge" "Widgets är inaktiverade i felsäkert läge" - "Widgetar" - "Widgetar" "Visa Mem" "Tryck länge om du vill flytta en widget." "%1$d × %2$d" - "Objektet kunde inte släppas på startskärmen." - "Ange vilken widget du vill använda" - "Mappnamn" - "Byt namn på mapp" - "OK" - "Avbryt" - "Lägg till på startskärmen" - "Appar" - "Genvägar" - "Widgetar" - "Det finns inte plats för mer på dina startsidor." "Det finns inte plats för mer på den här startskärmen." "Favoritfältet är fullt" - "Denna widget är för stor för favoritfältet" - "Genvägen %s har skapats." - "Genvägen %s har tagits bort." - "Genvägen %s finns redan." - "Välj genväg" - "Välj app" "Appar" "Startskärm" - "Avinstallera" "Ta bort" "Avinstallera" "Info om appen" - "Appar" - "Ta bort" - "Avinstallera uppdatering" - "Avinstallera appen" - "Information om appen" - "En app har valts" - "En widget har valts" - "En mapp har valts" - "En genväg har valts" "installera genvägar" "Tillåter att en app lägger till genvägar utan åtgärd från användaren." - "avinstallera genvägar" - "Tillåter att appen tar bort genvägar utan åtgärd från användaren." "läsa inställningar och genvägar för startsidan" "Tillåter att appen läser inställningar och genvägar på startsidan." "skriva inställningar och genvägar för startsidan" @@ -77,29 +45,18 @@ "Det gick inte att läsa in widgeten" "Konfiguration" "Det här är en systemapp som inte kan avinstalleras." - "Rocket Launcher" "Namnlös mapp" "Startskärmen %1$d" "Sidan %1$d av %2$d" "Startskärmen %1$d av %2$d" - "Appsida %1$d av %2$d" - "Widget-sida %1$d av %2$d" "Välkommen" - "Känn dig som hemma." - - - "Skapa fler skärmar för appar och mappar" "Kopiera appikoner" "Vill du importera ikoner och mappar från gamla startskärmar?" "KOPIERA IKONER" "BÖRJA OM" - "Organisera ditt utrymme" - "Tryck länge på bakgrunden om du vill hantera bakgrundsbilder, widgetar och inställningar." "Bakgrunder, widgetar och inställningar" "Tryck länge på bakgrunden om du vill anpassa den" "OK" - "Det här är en mapp" - "Skapa en till mapp av det här slaget genom att trycka och hålla ned en app och sedan dra den ovanpå en annan." "OK" "Mappen är öppen, %1$d gånger %2$d" "Tryck om du vill stänga mappen" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 9d9e4a589..6a2abcc5f 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Kizindua3" "Mwanzo" - "Programu Msingi za Android" "Programu haijasakinishwa." "Programu haipatikani" "Programu iliyopakuliwa imezimwa katika Hali Salama" "Wijeti zimezimwa katika hali ya Usalama" - "Wijeti" - "Wijeti" "Onyesha Kumbukumbu" "Gusa na ushikilie ili kuteua wijeti." "%1$d × %2$d" - "Haikuweza kudondosha kipengee kwenye skrini hii ya Kwanza." - "Chagua wijeti ili uunde" - "Jina la folda" - "lipe folda jina jipya" - "SAWA" - "Ghairi" - "Ongeza kwenye skrini ya Mwanzo" - "Programu" - "Njia za mkato" - "Wijeti" - "Hakuna nafasi zaidi kwenye skrini zako za Nyumbani." "Hakuna nafasi katika skrini hii ya Mwanzo." "Hakuna nafasi zaidi katika treya ya Vipendeleo" - "Wijeti hii ni kubwa mno kwa treya ya Vipendeleo" - "Njia ya mkato ya \"%s\" imeundwa." - "Njia ya mkato ya \"%s\" iliondolewa." - "\"%s\" la njia ya mkato tayari lipo." - "Chagua njia ya mkato" - "Chagua programu" "Programu" "Mwanzo" - "Ondoa" "Ondoa" "Ondoa" "Maelezo ya programu" - "Programu" - "Ondoa" - "Ondoa sasisho" - "Ondoa programu" - "Maelezo ya programu" - "Programu 1 imechaguliwa" - "Wijeti 1 imechaguliwa" - "Folda 1 limechaguliwa" - "Njia 1 ya mkato imechaguliwa" "kuweka njia za mkato" "Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati." - "ondoa njia za mikato" - "Huruhusu programu kuondoa njia za mikato bila mtumiaji kuingilia kati." "soma mipangilio ya Mwanzo na njia za mkato" "Huruhusu programu kusoma mipangilio na njia za mikato zilizo katika skirini ya Mwanzo." "andika mipangilio ya skrini ya Mwanzo na njia za mkato" @@ -77,31 +45,20 @@ "Tatizo la kupakia wijeti" "Sanidi" "Hii ni programu ya mfumo na haiwezi kuondolewa." - "Kizinduzi cha Roketi" "Folda isiyo na jina" "Skrini ya mwazo %1$d" "Ukurasa%1$d wa %2$d" - "Ukurasa wa programu %1$d ya %2$d" - "Ukurasa wa wijeti %1$d ya %2$d" "Karibu" - "Jisikie huru." - - - "Unda skrini zaidi za programu na folda" "Nakili ikoni za programu yako" "Je, ungependa kuingiza ikoni na folda kutoka kwenye skrini zako za Mwanzo za zamani?" "NAKILI IKONI" "ANZA UPYA" - "Panga nafasi yako" - "Gusa na ushikilie mandharinyuma ili udhibiti mandhari, wijeti, na mipangilio." "Mandhari, wijeti, na mipangilio" "Gusa na ushikilie mandhari ili uweke mapendeleo" "NIMEELEWA" - "Hii ni folda" - "Ili kuunda kama hii, gusa na ushikilie programu, kisha ipitishe juu ya nyingine." "SAWA" "Folda imefunguliwa, %1$d kwa %2$d" "Gusa ili ufunge folda" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index 2eecc22b4..9c7e699df 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "லாஞ்சர்3" "முகப்பு" - "Android முக்கியப் பயன்பாடுகள்" "பயன்பாடு நிறுவப்படவில்லை." "பயன்பாடு இல்லை" "இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது" "பாதுகாப்புப் பயன்முறையில் விட்ஜெட்கள் முடக்கப்பட்டுள்ளன" - "ஷார்ட்கட்ஸ்" - "ஷார்ட்கட்ஸ்" "நினைவகத்தைக் காட்டு" "விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்." "%1$d × %2$d" - "உருப்படியை இந்த முகப்புத் திரையில் விட முடியவில்லை." - "உருவாக்குவதற்கு விட்ஜெட்டைத் தேர்வுசெய்யவும்" - "கோப்புறையின் பெயர்" - "கோப்புறைக்கு மறுபெயரிடு" - "சரி" - "ரத்துசெய்" - "முகப்புத் திரையில் சேர்" - "பயன்பாடுகள்" - "குறுக்குவழிகள்" - "ஷார்ட்கட்ஸ்" - "உங்கள் முகப்புத் திரைகளில் வேறு இடம் இல்லை." "முகப்புத் திரையில் இடமில்லை." "பிடித்தவை ட்ரேயில் இடமில்லை" - "பிடித்தவை ட்ரேவிற்கு விட்ஜெட் மிகவும் பெரியது" - "\"%s\" குறுக்குவழி உருவாக்கப்பட்டது." - "\"%s\" குறுக்குவழி அகற்றப்பட்டது." - "\"%s\" குறுக்குவழி ஏற்கனவே உள்ளது." - "குறுக்குவழியைத் தேர்வுசெய்யவும்" - "பயன்பாட்டைத் தேர்வுசெய்யவும்" "பயன்பாடுகள்" "முகப்பு" - "நிறுவல் நீக்கு" "அகற்று" "நிறுவல் நீக்கு" "பயன்பாட்டுத் தகவல்" - "பயன்பாடுகள்" - "அகற்று" - "புதுப்பிப்பை நிறுவல் நீக்கு" - "பயன்பாட்டை நிறுவல் நீக்கு" - "பயன்பாட்டின் விவரங்கள்" - "1 பயன்பாடு தேர்ந்தெடுக்கப்பட்டது" - "1 விட்ஜெட் தேர்ந்தெடுக்கப்பட்டது" - "1 கோப்புறை தேர்ந்தெடுக்கப்பட்டது" - "1 குறுக்குவழி தேர்ந்தெடுக்கப்பட்டது" "குறுக்குவழிகளை நிறுவுதல்" "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது." - "குறுக்குவழிகளை நிறுவல் நீக்குதல்" - "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளை அகற்ற பயன்பாட்டை அனுமதிக்கிறது." "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்" "முகப்பில் உள்ள அமைப்பு மற்றும் குறுக்குவழிகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது." "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளை எழுதுதல்" @@ -77,29 +45,18 @@ "விட்ஜெட்டை ஏற்றுவதில் சிக்கல்" "அமைவு" "இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது." - "ராக்கெட் லாஞ்சர்" "பெயரிடப்படாத கோப்புறை" "முகப்புத் திரை %1$d" "பக்கம் %1$d / %2$d" "முகப்புத் திரை %1$d of %2$d" - "பயன்பாடுகளின் பக்கம் %1$d / %2$d" - "விட்ஜெட்களின் பக்கம் %1$d / %2$d" "வரவேற்கிறோம்" - "உங்களுக்கேற்ற முறையில் உருவாக்கவும்." - - - "பயன்பாடுகள் மற்றும் கோப்புறைகளுக்காகக் கூடுதல் திரைகளை உருவாக்கவும்" "பயன்பாட்டின் ஐகான்களை நகலெடுக்கவும்" "பழைய முகப்புத் திரைகளிலிருந்து ஐகான்களையும் கோப்புறைகளையும் இறக்குமதி செய்யவா?" "ஐகான்களை நகலெடு" "புதிதாகத் தொடங்கு" - "இடத்தை ஒழுங்கமைக்கவும்" - "வால்பேப்பர், விட்ஜெட்கள், அமைப்பை நிர்வகிக்க பின்புலத்தைத் தொட்டுப் பிடிக்கவும்." "வால்பேப்பர்கள், விட்ஜெட்கள் & அமைப்புகள்" "தனிப்பயனாக்க, பின்னணியைத் தொட்டுப் பிடிக்கவும்" "புரிந்தது" - "இதோ கோப்புறை" - "இதுபோன்ற ஒன்றை உருவாக்க பயன்பாட்டைத் தொட்டுப் பிடிக்கவும், பிறகு அதை வேறொன்றிற்கு நகர்த்தவும்." "சரி" "திறக்கப்பட்டக் கோப்புறை, %1$d x %2$d" "கோப்புறையை மூட, தொடவும்" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index 2d2ac2247..19e45e799 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "లాంచర్3" "హోమ్" - "Android ప్రధాన అనువర్తనాలు" "అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు." "అనువర్తనం అందుబాటులో లేదు" "డౌన్‌లోడ్ చేసిన అనువర్తనం సురక్షిత మోడ్‌లో నిలిపివేయబడింది" "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" - "విడ్జెట్‌లు" - "విడ్జెట్‌లు" "మెమరీ చూపు" "విడ్జెట్‌ను ఎంచుకోవడానికి తాకి & నొక్కి పెట్టండి." "%1$d × %2$d" - "ఈ హోమ్ స్క్రీన్‌లో అంశాన్ని వదలడం సాధ్యపడలేదు." - "సృష్టించాల్సిన విడ్జెట్ ఎంచుకోండి" - "ఫోల్డర్ పేరు" - "ఫోల్డర్‌ పేరు మార్చండి" - "సరే" - "రద్దు చేయి" - "హోమ్ స్క్రీన్‌కు జోడించు" - "అనువర్తనాలు" - "సత్వరమార్గాలు" - "విడ్జెట్‌లు" - "మీ హోమ్ స్క్రీన్‌ల్లో ఖాళీ లేదు." "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." "ఇష్టమైనవి ట్రేలో ఖాళీ లేదు" - "ఇష్టమైనవి ట్రే కోసం ఈ విడ్జెట్ చాలా పెద్దదిగా ఉంది" - "సత్వరమార్గం \"%s\" సృష్టించబడింది." - "సత్వరమార్గం \"%s\" తీసివేయబడింది." - "సత్వరమార్గం \"%s\" ఇప్పటికే ఉంది." - "సత్వరమార్గాన్ని ఎంచుకోండి" - "అనువర్తనాన్ని ఎంచుకోండి" "అనువర్తనాలు" "హోమ్" - "అన్ఇన్‌స్టాల్ చేయి" "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" "అనువర్తన సమాచారం" - "అనువర్తనాలు" - "తీసివేయి" - "నవీకరణను అన్‌ఇన్‌స్టాల్ చేయి" - "అనువర్తనాన్ని అన్‌ఇన్‌స్టాల్ చేయి" - "అనువర్తన వివరాలు" - "1 అనువర్తనం ఎంచుకోబడింది" - "1 విడ్జెట్ ఎంచుకోబడింది" - "1 ఫోల్డర్ ఎంచుకోబడింది" - "1 సత్వరమార్గం ఎంచుకోబడింది" "సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం" "వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది." - "సత్వరమార్గాలను అన్ఇన్‌స్టాల్ చేయడం" - "వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం" "హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను వ్రాయడం" @@ -77,29 +45,18 @@ "విడ్జెట్‌ను లోడ్ చేయడంలో సమస్య" "సెటప్ చేయి" "ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." - "రాకెట్ లాంచర్" "పేరు లేని ఫోల్డర్" "హోమ్ స్క్రీన్ %1$d" "%2$dలో %1$dవ పేజీ" "%2$dలో %1$dవ హోమ్ స్క్రీన్" - "%2$dలో %1$dవ అనువర్తనాల పేజీ" - "%2$dలో %1$dవ విడ్జెట్‌‌ల పేజీ" "స్వాగతం" - "మీ స్వంత స్థలంగా భావించండి." - - - "అనువర్తనాలు మరియు ఫోల్డర్‌ల కోసం మరిన్ని స్క్రీన్‌లు సృష్టి." "మీ అనువర్తన చిహ్నాలను కాపీ చేయండి" "మీ పాత హోమ్ స్క్రీన్‌ల నుండి చిహ్నాలు మరియు ఫోల్డర్‌లను దిగుమతి చేయాలా?" "చిహ్నాలను కాపీ చేయి" "తాజాగా ప్రారంభించు" - "మీ స్థలాన్ని నిర్వహించండి" - "వాల్‌., విడ్జె., సెట్టి. నిర్వ. నేపథ్యం తాకి & నొక్కి పెట్టండి." "వాల్‌పేపర్‌లు, విడ్జెట్‌లు & సెట్టింగ్‌లు" "అనుకూలీకరించడానికి నేపథ్యాన్ని నొక్కి & ఉంచండి" "అర్థమైంది" - "ఇక్కడ ఫోల్డర్ ఉంది" - "ఇలాంటిది సృష్టించడానికి అనువర్తనాన్ని తాకి & నొక్కి పెట్టండి, ఆపై మరోదాని పైన ఉంచండి." "సరే" "ఫోల్డర్ తెరవబడింది, %1$d X %2$d" "ఫోల్డర్‌ను మూసివేయడానికి తాకండి" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 89b5b9bc0..262788544 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "หน้าแรก" - "แอปหลักของ Android" "ไม่ได้ติดตั้งแอป" "แอปไม่พร้อมใช้งาน" "แอปที่ดาวน์โหลดถูกปิดในโหมดปลอดภัย" "มีการปิดใช้งานวิดเจ็ตในเซฟโหมด" - "วิดเจ็ต" - "วิดเจ็ต" "แสดง Mem" "แตะค้างเพื่อรับวิดเจ็ต" "%1$d × %2$d" - "ไม่สามารถวางรายการลงในหน้าจอหลักนี้" - "เลือกวิดเจ็ตที่จะสร้าง" - "ชื่อโฟลเดอร์" - "เปลี่ยนชื่อโฟลเดอร์" - "ตกลง" - "ยกเลิก" - "เพิ่มลงในหน้าแรก" - "แอป" - "ทางลัด" - "วิดเจ็ต" - "ไม่มีที่ว่างในหน้าจอหลักของคุณ" "ไม่มีที่ว่างในหน้าจอหลักนี้" "ไม่มีพื้นที่เหลือในถาดรายการโปรด" - "วิดเจ็ตนี้มีขนาดใหญ่เกินไปสำหรับถาดรายการโปรด" - "สร้างทางลัด \"%s\" แล้ว" - "นำทางลัด \"%s\" ออกแล้ว" - "มีทางลัด \"%s\" อยู่แล้ว" - "เลือกทางลัด" - "เลือกแอป" "แอป" "หน้าแรก" - "ถอนการติดตั้ง" "ลบ" "ถอนการติดตั้ง" "ข้อมูลแอป" - "แอป" - "ลบ" - "ถอนการติดตั้งการอัปเดต" - "ถอนการติดตั้งแอป" - "รายละเอียดแอป" - "เลือกไว้ 1 แอป" - "เลือกไว้ 1 วิดเจ็ต" - "เลือกไว้ 1 โฟลเดอร์" - "เลือกไว้ 1 ทางลัด" "ติดตั้งทางลัด" "อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ" - "ถอนการติดตั้งทางลัด" - "อนุญาตให้แอปนำทางลัดออกโดยไม่ต้องให้ผู้ใช้จัดการ" "อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว" "อนุญาตให้แอปอ่านการตั้งค่าและทางลัดในหน้าแรก" "เขียนการตั้งค่าและทางลัดหน้าแรกแล้ว" @@ -77,29 +45,18 @@ "มีปัญหาขณะโหลดวิดเจ็ต" "ตั้งค่า" "นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้" - "Rocket Launcher" "โฟลเดอร์ที่ไม่มีชื่อ" "หน้าจอหลัก %1$d" "หน้า %1$d จาก %2$d" "หน้าจอหลัก %1$d จาก %2$d" - "แอปหน้า %1$d จาก %2$d" - "วิดเจ็ตหน้า %1$d จาก %2$d" "ยินดีต้อนรับ" - "ทำตัวตามสบาย" - - - "สร้างหน้าจอเพิ่มสำหรับแอปและโฟลเดอร์" "คัดลอกไอคอนแอปของคุณ" "นำเข้าไอคอนและโฟลเดอร์จากหน้าจอหลักเก่าของคุณ" "คัดลอกไอคอน" "เริ่มต้นใหม่" - "จัดระเบียบพื้นที่ของคุณ" - "แตะพื้นหลังค้างไว้เพื่อจัดการวอลเปเปอร์ วิดเจ็ต และการตั้งค่า" "วอลเปเปอร์ วิดเจ็ต และการตั้งค่า" "แตะพื้นหลังค้างไว้เพื่อกำหนดค่า" "รับทราบ" - "นี่คือโฟลเดอร์" - "หากต้องการสร้างโฟลเดอร์ลักษณะนี้ แตะแอปค้างไว้ แล้วย้ายไปทับอีกแอปหนึ่ง" "ตกลง" "เปิดโฟลเดอร์ %1$d x %2$d" "แตะเพื่อปิดโฟลเดอร์" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 958ae5053..5356aa596 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Home" - "Android Core Apps" "Hindi naka-install ang app." "Hindi available ang app" "Naka-disable ang na-download na app sa Safe mode" "Naka-disable ang mga widget sa Safe mode" - "Mga Widget" - "Mga Widget" "Ipakita ang Mem" "Pindutin nang matagal upang kumuha ng widget." "%1$d × %2$d" - "Hindi ma-drop ang item sa Home screen na ito." - "Pumili ng widget na gagawin" - "Pangalan ng folder" - "Palitan ang pangalan ng folder" - "OK" - "Kanselahin" - "Idagdag sa Home screen" - "Apps" - "Mga Shortcut" - "Mga Widget" - "Wala nang lugar sa iyong mga Home screen." "Wala nang lugar sa Home screen na ito." "Wala nang lugar sa tray ng Mga Paborito" - "Masyadong malaki ang widget na ito para sa tray ng Mga Paborito" - "Nagawa ang shortcut na \"%s.\"" - "Inalis ang shortcut na \"%s.\"" - "Umiiral na ang shortcut na \"%s.\"" - "Pumili ng shortcut" - "Pumili ng app" "Apps" "Home" - "I-uninstall" "Alisin" "I-uninstall" "Impormasyon ng app" - "Apps" - "Alisin" - "I-uninstall ang update" - "I-uninstall ang app" - "Mga detalye ng app" - "1 app ang napili" - "1 widget ang napili" - "1 folder ang napili" - "1 shortcut ang napili" "i-install ang mga shortcut" "Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user." - "i-uninstall ang mga shortcut" - "Pinapayagan ang app na mag-alis ng mga shortcut nang walang panghihimasok ng user." "basahin ang mga setting at shortcut ng Home" "Pinapayagan ang app na basahin ang mga setting at shortcut sa Home." "magsulat ng mga setting at shortcut ng Home" @@ -77,29 +45,18 @@ "Problema sa pag-load ng widget" "I-setup" "Isa itong app ng system at hindi maaaring i-uninstall." - "Rocket Launcher" "Walang Pangalang Folder" "Home screen %1$d" "Pahina %1$d ng %2$d" "Home screen %1$d ng %2$d" - "Pahina ng apps %1$d ng %2$d" - "Pahina ng widget %1$d ng %2$d" "Maligayang Pagdating" - "Gawing kumportable ang iyong sarili." - - - "Gumawa ng higit pang mga screen para sa apps at mga folder" "Kopyahin ang mga icon ng app" "I-import ang icon at folder mula sa luma mong Home screen?" "KOPYAHIN ANG MGA ICON" "MAGSIMULA NANG BAGO" - "Ayusin ang iyong espasyo" - "Pindutin nang matagal ang background upang pamahalaan ang wallpaper, mga widget at setting" "Mga wallpaper, widget at setting" "Pindutin nang matagal ang background upang i-customize" "NAKUHA KO" - "Narito ang isang folder" - "Upang gumawa ng katulad nito, pindutin nang matagal ang isang app, pagkatapos ay ilipat ito sa isa pang folder." "OK" "Binuksan ang folder, %1$d by %2$d" "Pindutin upang isara ang folder" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 97f5c1eb3..24e7815d5 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Ana ekran" - "Android Çekirdek Uygulamaları" "Uygulama yüklü değil." "Uygulama kullanılamıyor" "İndirilen uygulama Güvenli modda devre dışı bırakıldı" "Güvenli modda widget\'lar devre dışı" - "Widget\'lar" - "Widget\'lar" "Belleği Göster" "Widget seçmek için dokunun ve basılı tutun." "%1$d × %2$d" - "Öğe bu Ana ekrana bırakılamadı." - "Oluşturmak için widget seçin" - "Klasör adı" - "Klasörü yeniden adlandırın" - "Tamam" - "İptal" - "Ana ekrana ekleyin" - "Uygulamalar" - "Kısayollar" - "Widget\'lar" - "Ana ekranlarınızda yer kalmadı." "Bu Ana ekranda yer kalmadı." "Favoriler tepsisinde başka yer kalmadı" - "Bu widget, Favoriler tepsisi için çok geniş" - "\"%s\" kısayolu oluşturuldu." - "\"%s\" kısayolu kaldırıldı." - "\"%s\" kısayolu zaten var." - "Kısayolu seçin" - "Uygulama seçin" "Uygulamalar" "Ana ekran" - "Yüklemeyi kaldır" "Kaldır" "Yüklemeyi kaldır" "Uygulama bilgileri" - "Uygulamalar" - "Kaldır" - "Güncellemeyi kaldır" - "Uygulamanın yüklemesini kaldır" - "Uygulama ayrıntıları" - "1 uygulama seçildi" - "1 widget seçildi" - "1 klasör seçildi" - "1 kısayol seçildi" "kısayolları yükle" "Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir." - "kısayolların yüklemesini kaldır" - "Uygulamaya, kullanıcı müdahalesi olmadan kısayolları kaldırma izni verir." "Ana ekran ayarlarını ve kısayollarını oku" "Uygulamaya, Ana ekrandaki ayarları ve kısayolları okuma izni verir." "Ana ekran ayarlarını ve kısayollarını yaz" @@ -77,29 +45,18 @@ "Widget yüklenirken sorun oluştu" "Kurulum" "Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz." - "Roket Fırlatıcı" "Adsız Klasör" "Ana ekran %1$d" "Sayfa %1$d / %2$d" "Ana ekran %1$d / %2$d" - "Uygulama sayfası %1$d / %2$d" - "Widget sayfası %1$d / %2$d" "Hoş geldiniz" - "Rahatınıza bakın." - - - "Uygulamalar ve klasörler için daha fazla ekran oluşturun" "Uygulama simgelerini kopyala" "Eski Ana ekranlarınızdaki simgeler ve klasörler içe aktarılsın mı?" "SİMGELERİ KOPYALA" "VARSAYILANI KULLAN" - "Alanınızı düzenleyin" - "Duvar kağıdını, widget\'ları ve ayarları yönetmek için arka plana uzun basın." "Duvar kağıtları, widget\'lar ve ayarlar" "Özelleştirmek için arka plana dokunun ve basılı tutun" "TAMAM" - "İşte bir klasör" - "Buna benzer bir klasör oluşturmak için uygulamaya uzun basın ve sonra uygulamayı başka bir uygulamanın üzerine taşıyın." "Tamam" "Klasör açıldı, %1$d x %2$d" "Klasörü kapatmak için dokunun" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index f3fbd9ea9..75a2a6091 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Головний екран" - "Базові програми Android" "Додаток видалено." "Додаток недоступний" "Завантажений додаток вимкнено в безпечному режимі" "У безпечному режимі віджети вимкнено" - "Віджети" - "Віджети" "Показати пам’ять" "Натисніть і утримуйте, щоб вибрати віджет." "%1$d × %2$d" - "Не вдалося додати елемент на цей головний екран." - "Вибрати віджет для створення" - "Назва папки" - "Перейменувати папку" - "OК" - "Скасувати" - "Додати на головний екран" - "Додатки" - "Ярлики" - "Віджети" - "На головних екранах більше немає місця." "На цьому головному екрані більше немає місця." "В області \"Вибране\" немає місця" - "Цей віджет завеликий для області \"Вибране\"" - "Ярлик \"%s\" створено." - "Ярлик \"%s\" вилучено." - "Ярлик \"%s\" уже існує." - "Вибрати ярлик" - "Вибрати програму" "Додатки" "Головний екран" - "Видалити" "Вилучити" "Видалити" "Про програму" - "Додатки" - "Вилучити" - "Видалити оновлення" - "Видалити програму" - "Відомості про програму" - "Вибрано 1 програму" - "Вибрано 1 віджет" - "Вибрано 1 папку" - "Вибрано 1 ярлик" "установлювати ярлики" "Дозволяє програмі самостійно додавати ярлики." - "видаляти ярлики" - "Дозволяє програмі самостійно вилучати ярлики." "читати налаштування та ярлики головного екрана" "Дозволяє програмі читати налаштування та ярлики на головному екрані." "записувати налаштування та ярлики головного екрана" @@ -77,29 +45,18 @@ "Проблема із завантаженням віджета" "Налаштування" "Це системна програма, її неможливо видалити." - "Rocket Launcher" "Папка без назви" "Головний екран %1$d" "Сторінка %1$d з %2$d" "Головний екран %1$d з %2$d" - "Сторінка програм %1$d з %2$d" - "Сторінка віджетів %1$d з %2$d" "Вітаємо" - "Будьте як удома." - - - "Створюйте нові екрани для програм і папок" "Копіювати значки програм" "Імпортувати значки та папки зі старих головних екранів?" "КОПІЮВАТИ ЗНАЧКИ" "ПАНЕЛЬ ЗАПУСКУ ЗА УМОВЧАННЯМ" - "Організуйте робочий простір" - "Натисніть і утримуйте фон, щоб керувати фоновим малюнком, віджетами та налаштуваннями." "Фонові малюнки, віджети й налаштування" "Натисніть і втримуйте фон, щоб налаштувати робочу область" "ЗРОЗУМІЛО" - "Це папка" - "Щоб створити папку, натисніть і утримуйте програму, а потім перетягніть її на іншу." "OК" "Папку відкрито (%1$d х %2$d)" "Торкніться, щоб закрити папку" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index 5f227db05..70549246e 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "ہوم" - "‏Android کور ایپس" "ایپ انسٹال نہیں ہے۔" "ایپ دستیاب نہیں ہے" "ڈاؤن لوڈ کردہ ایپ کو محفوظ وضع میں غیر فعال کر دیا گیا" "ویجیٹس کو محفوظ وضع میں غیر فعال کر دیا گیا" - "ویجیٹس" - "ویجیٹس" "‏Mem دکھائیں" "کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔" "%1$d × %2$d" - "آئٹم کو اس ہوم اسکرین پر ڈراپ نہیں کیا جا سکا۔" - "بنانے کیلئے ویجیٹ منتخب کریں" - "فولڈر کا نام" - "فولڈر کا نام تبدیل کریں" - "ٹھیک ہے" - "منسوخ کریں" - "ہوم اسکرین میں شامل کریں" - "ایپس" - "شارٹ کٹس" - "ویجیٹس" - "آپ کی ہوم اسکرینوں پر مزید کوئی گنجائش نہیں ہے۔" "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" "پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے" - "یہ ویجیٹ پسندیدہ ٹرے کیلئے کافی بڑا ہے" - "شارٹ کٹ \"%s\" بنایا گیا۔" - "شارٹ کٹ \"%s\" ہٹا دیا گیا۔" - "شارٹ کٹ \"%s\" پہلے سے موجود ہے۔" - "شارٹ کٹ منتخب کریں" - "ایپ منتخب کریں" "ایپس" "ہوم" - "اَن انسٹال کریں" "ہٹائیں" "اَن انسٹال کریں" "ایپ کی معلومات" - "ایپس" - "ہٹائیں" - "اپ ڈیٹ اَن انسٹال کریں" - "ایپ کو اَن انسٹال کریں" - "ایپ کی تفصیلات" - "1 ایپ منتخب کی گئی" - "1 ویجیٹ منتخب کیا گیا" - "1 فولڈر منتخب کیا گیا" - "1 شارٹ کٹ منتخب کیا گیا" "شارٹ کٹس انسٹال کریں" "کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔" - "شارٹ کٹس کو اَن انسٹال کریں" - "ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس ہٹانے کی اجازت دیتا ہے۔" "ہوم ترتیبات اور شارٹ کٹس کو پڑھیں" "ایپ کو ہوم میں ترتیبات اور شارٹ کٹس کو پڑھنے کی اجازت دیتا ہے۔" "ہوم ترتیبات اور شارٹ کٹس کو لکھیں" @@ -77,29 +45,18 @@ "ویجیٹ کو لوڈ کرنے میں مسئلہ" "ترتیب دیں" "یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔" - "راکٹ لانچر" "بلا نام فولڈر" "‏ہوم اسکرین ‎%1$d" "‏صفحہ ‎%1$d از ‎%2$d" "‏ہوم اسکرین ‎%1$d از ‎%2$d" - "‏ایپس کا صفحہ ‎%1$d از ‎%2$d" - "‏ویجیٹس کا صفحہ ‎%1$d از ‎%2$d" "خوش آمدید" - "ہوم سکرین مرضی کے مطابق بنائیں۔" - - - "ایپس اور فولڈرز کیلئے مزید اسکرینیں بنائیں" "اپنے ایپ آئیکنز کو کاپی کریں" "اپنی پرانی ہوم اسکرینوں سے آئیکنز اور فولڈرز درآمد کریں؟" "آئیکنز کاپی کریں" "نئے سرے سے شروع کریں" - "اپنی جگہ کو منظم کریں" - "وال پیپر، ویجیٹس اور ترتیبات کا نظم کرنے کیلئے پس منظر کو ٹچ کریں اور پکڑ کر رکھیں۔" "وال پیپرز، ویجیٹس اور ترتیبات" "حسب ضرورت بنانے کیلئے پس منظر کو ٹچ کریں اور دبائے رکھیں" "سمجھ آ گئی" - "یہ ہے ایک فولڈر" - "اس طرح کا ایک بنانے کیلئے، کسی ایپ کو ٹچ کریں اور پکڑ کر رکھیں، پھر اسے کسی دوسرے میں منتقل کریں۔" "ٹھیک ہے" "فولڈر کھولا گیا، %1$d × %2$d" "فولڈر بند کرنے کیلئے ٹچ کریں" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index 6f298e67a..a5c461321 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Ishga tushirgich3" "Uy" - "Androidga asoslangan dasturlar" "Ilova o‘rnatilmadi." "Ilova mavjud emas" "Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi" "Xavfsiz rejimda vidjetlar o‘chirib qo‘yilgan" - "Vidjetlar" - "Vidjetlar" "Xotirani ko‘rsatish" "Vidjetni tanlash uchun bosib turing." "%1$d × %2$d" - "Elementni ushbu \"Uy\" ekraniga tashlab bo‘lmadi." - "Yaratish uchun vidjet tanlang" - "Jild nomi" - "Jild nomini o‘zgartirish" - "OK" - "Bekor qilish" - "Uy ekraniga qo‘shish" - "Ilovalar" - "Yorliqlar" - "Vidjetlar" - "Uy ekraningizda birorta ham xona yo‘q." "Uy ekranida bitta ham xona yo‘q." "Ajratilganlarda birorta ham xona yo‘q" - "Ajratilganlar uchun ushbu vidjet juda katta" - "\"%s\" yorlig‘i yaratildi." - "\"%s\" yorlig‘i o‘chirildi." - "\"%s\" yorlig‘i allaqachon mavjud." - "Yorliqni tanlash" - "Ilovani tanlash" "Ilovalar" "Uy" - "O‘chirish" "O‘chirish" "O‘chirish" "Ilova ma’lumoti" - "Ilovalar" - "O‘chirish" - "Yangilashni o‘chirish" - "Ilovani o‘chirish" - "Ilova ma’lumotlari" - "1 ta ilova tanlandi" - "1 ta vidjet tanlandi" - "1 ta jild tanlandi" - "1 ta yorliq tanlandi" "yorliqlar o‘rnatish" "Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi." - "yorliqlarni o‘chirish" - "Ilovaga foydalanuvchiga bildirmasdan yorliqlarni o‘chirish uchun ruxsat beradi." "Uy sozlamalari va yorliqlarini o‘qish" "Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalarni o‘qish uchun ruxsat beradi." "Uy sozlamalari va yorliqlarini yozish" @@ -77,29 +45,18 @@ "Vidjetni yuklashda muammo" "Sozlash" "Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi." - "Rocket Launcher" "Nomsiz jild" "Uy ekrani %1$d" "%2$ddan %1$d ta sahifa" "Uy ekrani %2$ddan %1$d" - "Ilovalar sahifasi %2$ddan %1$d" - "Vidjetlar sahifasi %2$ddan %1$d" "Xush kelibsiz" - "O‘zingizni uyingizdagidek his qiling." - - - "Ilovalar va jildlar uchun ko‘proq ekranlar yaratish" "Ilovangiz nishonchalaridan nusxa olish" "Eski \"Uy\" ekranlaringizdan jildlar va nishonchalar import qilinsinmi?" "NISHONCHALARNI NUSXALASH" "YANGIDAN BOSHLASH" - "Joylaringizni boshqaring" - "Fon rasmi, vidjet va sozlamalarni boshqarish uchun orqa fonga bosib turing" "Orqa fon rasmlari, vidjet va sozlamalar" "Orqa fonni moslashtirish uchun uni bosing va ushlab turing" "OK" - "Mana sizga jild" - "Bunga o‘xshaganini yaratish uchun bosib turing, keyin boshqasiga o‘ting." "OK" "Jild ochildi, %1$d ga %2$d" "Jildni yopish uchun bosing" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index bf8a202ac..45bfa4cd2 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "Màn hình chính" - "Ứng dụng lõi Android" "Ứng dụng chưa được cài đặt." "Ứng dụng không có sẵn" "Ứng dụng đã tải xuống bị tắt ở chế độ An toàn" "Tiện ích con bị vô hiệu hóa ở chế độ an toàn" - "Tiện ích con" - "Tiện ích con" "Hiển thị bộ nhớ" "Chạm và giữ để chọn tiện ích con." "%1$d × %2$d" - "Không thể thả mục vào Màn hình chính này." - "Chọn tiện ích con để tạo" - "Tên thư mục" - "Đổi tên thư mục" - "OK" - "Hủy" - "Thêm vào Màn hình chính" - "Ứng dụng" - "Lối tắt" - "Tiện ích con" - "Không còn chỗ trên Màn hình chính của bạn." "Không còn chỗ trên Màn hình chính này." "Không còn chỗ trong khay Mục yêu thích" - "Tiện ích con này có kích thước quá lớn để đặt vào khay Mục yêu thích" - "Lối tắt \"%s\" đã được tạo." - "Lối tắt \"%s\" đã bị xóa." - "Lối tắt \"%s\" đã tồn tại." - "Chọn lối tắt" - "Chọn ứng dụng" "Ứng dụng" "Màn hình chính" - "Gỡ cài đặt" "Xóa" "Gỡ cài đặt" "Thông tin ứng dụng" - "Ứng dụng" - "Xóa" - "Gỡ cài đặt cập nhật" - "Gỡ cài đặt ứng dụng" - "Thông tin chi tiết về ứng dụng" - "Đã chọn 1 ứng dụng" - "Đã chọn 1 tiện ích con" - "Đã chọn 1 thư mục" - "Đã chọn 1 lối tắt" "cài đặt lối tắt" "Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng." - "gỡ cài đặt lối tắt" - "Cho phép ứng dụng xóa lối tắt mà không cần sự can thiệp của người dùng." "đọc cài đặt và lối tắt trên Màn hình chính" "Cho phép ứng dụng đọc cài đặt và lối tắt trên Màn hình chính." "ghi cài đặt và lối tắt trên Màn hình chính" @@ -77,29 +45,18 @@ "Sự cố khi tải tiện ích con" "Thiết lập" "Đây là ứng dụng hệ thống và không thể gỡ cài đặt." - "Rocket Launcher" "Thư mục chưa đặt tên" "Màn hình chính %1$d" "Trang %1$d / %2$d" "Màn hình chính %1$d / %2$d" - "Trang ứng dụng %1$d / %2$d" - "Trang tiện ích con %1$d / %2$d" "Chào mừng" - "Tự nhiên như ở nhà." - - - "Tạo thêm màn hình cho ứng dụng và thư mục" "Sao chép biểu tượng ứng dụng của bạn" "Nhập biểu tượng và thư mục từ Màn hình chính cũ của bạn?" "BIỂU TƯỢNG SAO CHÉP" "BẮT ĐẦU LÀM MỚI" - "Sắp xếp không gian của bạn" - "Chạm và giữ nền để quản lý hình nền, tiện ích con và cài đặt." "Hình nền, tiện ích và cài đặt" "Chạm và giữ nền để tùy chỉnh" "OK" - "Đây là một thư mục" - "Để tạo thư mục như thế này, hãy chạm và giữ một ứng dụng, sau đó di chuyển ứng dụng đó lên trên một ứng dụng khác." "OK" "Đã mở thư mục, %1$d x %2$d" "Chạm để đóng thư mục" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 7be54fbe8..c8ca12d08 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "主屏" - "Android 核心应用" "未安装该应用。" "应用不可用" "安全模式下不允许使用下载的此应用" "安全模式下不允许使用小部件" - "小部件" - "小部件" "显示内存空间" "触摸并按住小部件即可选择。" "%1$d × %2$d" - "无法将相关内容拖放到此主屏幕上。" - "选择要创建的小部件" - "文件夹名称" - "重命名文件夹" - "确定" - "取消" - "添加到主屏幕" - "应用" - "快捷方式" - "小部件" - "您的主屏幕上没有空间了。" "此主屏幕上已没有空间。" "收藏栏已满" - "该小部件太大,收藏栏中放不下" - "已创建“%s”快捷方式。" - "已删除“%s”快捷方式。" - "“%s”快捷方式已存在。" - "选择快捷方式" - "选择应用" "应用" "主屏幕" - "卸载" "删除" "卸载" "应用信息" - "应用" - "删除" - "卸载更新内容" - "卸载应用" - "应用详情" - "已选择1个应用" - "已选择1个小部件" - "已选择1个文件夹" - "已选择1个快捷方式" "安装快捷方式" "允许应用自行添加快捷方式。" - "卸载快捷方式" - "允许应用自行删除快捷方式。" "读取主屏幕设置和快捷方式" "允许应用读取主屏幕中的设置和快捷方式。" "写入主屏幕设置和快捷方式" @@ -77,29 +45,18 @@ "加载小部件时出现问题" "设置" "这是系统应用,无法卸载。" - "火箭发射器" "未命名文件夹" "主屏幕%1$d" "第%1$d页,共%2$d页" "主屏幕:第%1$d屏,共%2$d屏" - "应用:第%1$d页,共%2$d页" - "小部件:第%1$d页,共%2$d页" "欢迎使用" - "您的主屏幕您做主。" - - - "添加更多屏幕来容纳应用和文件夹" "复制应用图标" "要导入旧的主屏幕中的图标和文件夹吗?" "复制图标" "使用全新配置" - "整理您的空间" - "触摸并按住背景,即可管理壁纸、小部件和设置。" "壁纸、小部件和设置" "触摸并按住背景,即可进行个性化设置" "知道了" - "这是一个文件夹" - "要创建一个类似的文件夹,请触摸并按住某个应用,然后将其移至另一个应用上。" "确定" "文件夹已打开,大小为%1$d×%2$d" "触摸可关闭文件夹" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index b7a92656f..132df1417 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "主畫面" - "Android 核心應用程式" "尚未安裝應用程式。" "目前無法使用這個應用程式" "在安全模式中無法使用「已下載的應用程式」功能" "在安全模式中無法使用小工具" - "小工具" - "小工具" "顯示記憶體" "輕觸並按住小工具即可選取。" "%1$d × %2$d" - "無法將項目拖放至主畫面。" - "選擇要建立的小工具" - "資料夾名稱" - "重新命名資料夾" - "確定" - "取消" - "新增至主畫面" - "應用程式" - "捷徑" - "小工具" - "主畫面已無空間。" "主畫面已無空間。" "我的收藏寄存區沒有足夠空間" - "這個小工具過大,我的收藏寄存區沒有足夠空間" - "已建立「%s」捷徑。" - "已移除「%s」捷徑。" - "「%s」捷徑已存在。" - "選擇捷徑" - "選擇應用程式" "應用程式" "主畫面" - "解除安裝" "移除" "解除安裝" "應用程式資料" - "應用程式" - "移除" - "解除安裝更新" - "解除安裝應用程式" - "應用程式詳細資料" - "已選取 1 個應用程式" - "已選取 1 個小工具" - "已選取 1 個資料夾" - "已選取 1 個捷徑" "安裝捷徑" "允許應用程式無需使用者許可也可新增捷徑。" - "解除安裝捷徑" - "允許應用程式無需使用者許可也可移除捷徑。" "讀取主畫面的設定和捷徑" "允許應用程式讀取主畫面中的設定和捷徑。" "寫入主畫面的設定和捷徑" @@ -77,29 +45,18 @@ "載入小工具時發生問題" "設定" "這是系統應用程式,無法將其解除安裝。" - "Rocket Launcher" "未命名的資料夾" "主畫面 %1$d" "第 %1$d 頁,共 %2$d 頁" "主畫面 %1$d,共 %2$d 個" - "第 %1$d 個應用程式頁面,共 %2$d 頁" - "第 %1$d 個小工具頁面,共 %2$d 頁" "歡迎" - "自訂主畫面。" - - - "建立更多應用程式和資料夾的畫面" "複製您的應用程式圖示" "要從舊主畫面匯入圖示和資料夾嗎?" "複製圖示" "重新開始" - "管理您的空間" - "輕觸並按住背景,即可管理桌布、小工具和設定。" "桌布、小工具和設定" "輕觸並按住背景即可自訂" "知道了" - "資料夾顯示如下" - "如要建立類似的資料夾,請輕觸並按住某個應用程式,然後疊到另一個應用程式之上。" "確定" "資料夾已開啟 (%1$d x %2$d)" "輕觸即可關閉資料夾" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index ecfd4c38a..d1e41fb6b 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" "主螢幕" - "Android 核心應用程式" "應用程式未安裝。" "應用程式目前無法使用" "在安全模式中無法使用「已下載的應用程式」功能" "在安全模式下無法使用小工具" - "小工具" - "小工具" "顯示記憶體" "輕觸並按住小工具即可選取。" "%1$d × %2$d" - "無法將項目拖放至這個主螢幕上。" - "選擇要建立的小工具" - "資料夾名稱" - "重新命名資料夾" - "確定" - "取消" - "新增至主螢幕" - "應用程式" - "捷徑" - "小工具" - "主螢幕已無空間。" "這個主螢幕已無空間。" "「我的最愛」匣已無可用空間" - "這個小工具過大,「我的最愛」匣無法容納" - "已建立「%s」捷徑。" - "已移除「%s」捷徑。" - "「%s」捷徑已存在。" - "選擇捷徑" - "選擇應用程式" "應用程式" "主螢幕" - "解除安裝" "移除" "解除安裝" "應用程式資訊" - "應用程式" - "移除" - "解除安裝更新" - "解除安裝應用程式" - "應用程式詳細資料" - "已選取 1 個應用程式" - "已選取 1 個小工具" - "已選取 1 個資料夾" - "已選取 1 個捷徑" "安裝捷徑" "允許應用程式自動新增捷徑。" - "解除安裝捷徑" - "允許應用程式自動移除捷徑。" "讀取主螢幕的設定和捷徑" "允許應用程式讀取主螢幕中的設定和捷徑。" "寫入主螢幕設定和捷徑" @@ -77,29 +45,18 @@ "載入小工具時發生問題" "設定" "這是系統應用程式,不可解除安裝。" - "Rocket Launcher" "未命名的資料夾" "主螢幕 %1$d" "第 %1$d 頁,共 %2$d 頁" "主螢幕:第 %1$d 頁,共 %2$d 頁" - "應用程式:第 %1$d 頁,共 %2$d 頁" - "小工具:第 %1$d 頁,共 %2$d 頁" "歡迎使用" - "主螢幕由您作主。" - - - "建立更多畫面容納應用程式和資料夾" "複製您的應用程式圖示" "要從舊的主螢幕匯入圖示和資料夾嗎?" "複製圖示" "重新開始" - "管理您的空間" - "輕觸並按住背景,即可管理桌布、小工具和設定。" "桌布、小工具和設定" "輕觸並按住背景即可自訂" "知道了" - "資料夾顯示如下" - "如要建立類似的資料夾,請輕觸並按住應用程式,然後將應用程式疊放在另一個應用程式上。" "確定" "資料夾已開啟 (%1$d x %2$d)" "輕觸即可關閉資料夾" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 3b38e6a50..73852bfc6 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -21,55 +21,23 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Isiqalisi3" "Ikhaya" - "Izinhlelo zokusebenza ze-Android Core" "Uhlelo lokusebenza alufakiwe." "Uhlelo lokusebenza alutholakali" "Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile" "Amawijethi akhutshaziwe kwimodi yokuphepha" - "Amawijethi" - "Amawijethi" "Bonisa i-Mem" "Thinta uphinde ubambe ukuze uphakamise iwijethi." "%1$d × %2$d" - "Ayikwazanga ukwehlisela into kulesi sikrini se-Ikhaya." - "Khetha iwijethi ongayidala" - "Igama lefolda" - "Qamba kabusha ifolda" - "KULUNGILE" - "Khansela" - "Faka kwisikrini saseKhaya" - "Izinhlelo zokusebenza" - "Izinqamuleli" - "Amawijethi" - "Akusenagumbi ezikrinini zakho Zekhaya." "Asisekho isikhala kulesi sikrini Sasekhaya." "Asisekho isikhala kwitreyi lezintandokazi" - "Le wijethi inkulu kakhulu ukuba kwitreyi lezintandokazi." - "Isinqamuleli esithi \"%s\" sidaliwe." - "Isinqamuleli esithi \"%s\" sisusiwe." - "Isinqamuleli esithi \"%s\" sesivele sikhona." - "Khetha isinqamulelo" - "Khetha uhlelo lokusebenza" "Izinhlelo zokusebenza" "Ikhaya" - "Khipha" "Susa" "Khipha" "Ulwazi lohlelo lokusebenza" - "Izinhlelo zokusebenza" - "Susa" - "Khipha isibuyekezo" - "Khipha uhlelo lokusebenza" - "Imininingwane yohlelo lokusebenza" - "1 uhlelo lokusebenza olukhethiwe" - "1 iwijethi ekhethiwe" - "1 ifolda ekhethiwe" - "1 isinqamuleli esikhethiwe" "faka izinqamuleli" "Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi." - "khipha izinqamuleli" - "Ivumela uhlelo lokusebenza ukuthi lisuse izinqamuleli ngaphandle kokungenelela komsebenzisi." "funda izilungiselelo zokuthi Ikhaya nezinqamuleli" "Ivumela uhlelo lokusebenza ukuthi lifunde izilungiselelo nezinqamuleli ekhaya." "bhala izilungiselelo zokuthi Ikhaya nezinqamuleli" @@ -77,29 +45,18 @@ "Inkinga yokulayisha iwijethi" "Ukumisa" "Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa." - "Isiqalisi se-Rocket" "Ifolda engenagama" "Isikrini sasekhaya esingu-%1$d" "Ikhasi elingu-%1$d kwangu-%2$d" "Isikrini sasekhaya esingu-%1$d se-%2$d" - "Ikhasi lezinhlelo zokusebenza elingu-%1$d le-%2$d" - "Ikhasi lamawijethi elingu-%1$d le-%2$d" "Siyakwamukela" - "Zizwe usekhaya." - - - "Dala izikrini eziningi zezinhlelo zokusebenza namafolda" "Kopisha izithonjana zakho zohlelo lokusebenza" "Ngenisa izithonjana namafolda kusukela kuzikrini zakho ezindala zasekhaya?" "KOPISHA IZITHONJANA" "QALISA KABUSHA" - "Hlela isikhala sakho" - "Thinta uphinde ubambe okungemuva ukuze uphathe isithombe sangemuva, amawijethi nezilungiselelo." "Izithombe zangemuva, amawijethi, nezilungiselelo" "Thinta uphinde ubambe ingemuva ukuze wenze ngokwezifiso" "NGIYITHOLILE" - "Nayi ifolda" - "Ukuze udale eyodwa efana nale, thinta uphinde ubambe uhlelo lokusebenza, bese ulidlulisa ngaphezulu kwelinye." "KULUNGILE" "Ifolda ivuliwe, %1$d nge-%2$d" "Thinta ukuze uvale ifolda" -- cgit v1.2.3 From a3499dc019a677efb008d926b2cd9a5ad0bc0ca0 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 11 May 2015 22:12:38 -0700 Subject: Tweaking section processing for different languages - Ensuring that apps with non-letter/digit characters are ordered last in the misc bucket - Removing duplicate latin-alphabet sections for Simplified Chinese - Adding more appropriate misc bucket label for Japanese Bug 21022854 Change-Id: I62c7b219820ef88787fcfa83f1bd4202f16f9c0c --- .../android/launcher3/AlphabeticalAppsList.java | 117 +++++++++++++++++---- src/com/android/launcher3/AppsGridAdapter.java | 17 +-- src/com/android/launcher3/DeviceProfile.java | 7 -- .../launcher3/compat/AlphabeticIndexCompat.java | 27 ++++- 4 files changed, 122 insertions(+), 46 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index de4edcb7b..dc75637e5 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -14,23 +14,28 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; /** * A private class to manage access to an app name comparator. */ class AppNameComparator { - private UserManagerCompat mUserManager; - private Comparator mAppNameComparator; + private final UserManagerCompat mUserManager; + private final Collator mCollator; + private final Comparator mAppInfoComparator; + private final Comparator mSectionNameComparator; private HashMap mUserSerialCache = new HashMap<>(); public AppNameComparator(Context context) { - final Collator collator = Collator.getInstance(); + mCollator = Collator.getInstance(); mUserManager = UserManagerCompat.getInstance(context); - mAppNameComparator = new Comparator() { + mAppInfoComparator = new Comparator() { public final int compare(AppInfo a, AppInfo b) { - // Order by the title - int result = collator.compare(a.title.toString(), b.title.toString()); + // Order by the title in the current locale + int result = compareTitles(a.title.toString(), b.title.toString()); if (result == 0) { // If two apps have the same title, then order by the component name result = a.componentName.compareTo(b.componentName); @@ -49,15 +54,45 @@ class AppNameComparator { return result; } }; + mSectionNameComparator = new Comparator() { + @Override + public int compare(String o1, String o2) { + return compareTitles(o1, o2); + } + }; } /** * Returns a locale-aware comparator that will alphabetically order a list of applications. */ - public Comparator getComparator() { + public Comparator getAppInfoComparator() { // Clear the user serial cache so that we get serials as needed in the comparator mUserSerialCache.clear(); - return mAppNameComparator; + return mAppInfoComparator; + } + + /** + * Returns a locale-aware comparator that will alphabetically order a list of section names. + */ + public Comparator getSectionNameComparator() { + return mSectionNameComparator; + } + + /** + * Compares two titles with the same return value semantics as Comparator. + */ + private int compareTitles(String titleA, String titleB) { + // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit + boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0)); + boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0)); + if (aStartsWithLetter && !bStartsWithLetter) { + return -1; + } else if (!aStartsWithLetter && bStartsWithLetter) { + return 1; + } + + // Order by the title in the current locale + return mCollator.compare(titleA, titleB); } /** @@ -219,6 +254,7 @@ public class AlphabeticalAppsList { private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3; private static final int MAX_NUM_MERGES_PHONE = 2; + private Context mContext; private List mApps = new ArrayList<>(); private List mFilteredApps = new ArrayList<>(); private List mSectionedFilteredApps = new ArrayList<>(); @@ -234,6 +270,7 @@ public class AlphabeticalAppsList { private int mNumAppsPerRow; public AlphabeticalAppsList(Context context, int numAppsPerRow) { + mContext = context; mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppNameComparator(context); setNumAppsPerRow(numAppsPerRow); @@ -400,7 +437,7 @@ public class AlphabeticalAppsList { * Implementation to actually add an app to the alphabetic list, but does not notify. */ private void addApp(AppInfo info) { - int index = Collections.binarySearch(mApps, info, mAppNameComparator.getComparator()); + int index = Collections.binarySearch(mApps, info, mAppNameComparator.getAppInfoComparator()); if (index < 0) { mApps.add(-(index + 1), info); } @@ -411,7 +448,44 @@ public class AlphabeticalAppsList { */ private void onAppsUpdated() { // Sort the list of apps - Collections.sort(mApps, mAppNameComparator.getComparator()); + Collections.sort(mApps, mAppNameComparator.getAppInfoComparator()); + + // As a special case for some languages (currently only Simplified Chinese), we may need to + // coalesce sections + Locale curLocale = mContext.getResources().getConfiguration().locale; + TreeMap> sectionMap = null; + boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE); + if (localeRequiresSectionSorting) { + // Compute the section headers. We use a TreeMap with the section name comparator to + // ensure that the sections are ordered when we iterate over it later + sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator()); + for (AppInfo info : mApps) { + // Add the section to the cache + String sectionName = mCachedSectionNames.get(info.title); + if (sectionName == null) { + sectionName = mIndexer.computeSectionName(info.title); + mCachedSectionNames.put(info.title, sectionName); + } + + // Add it to the mapping + ArrayList sectionApps = sectionMap.get(sectionName); + if (sectionApps == null) { + sectionApps = new ArrayList<>(); + sectionMap.put(sectionName, sectionApps); + } + sectionApps.add(info); + } + } else { + // Just compute the section headers for use below + for (AppInfo info : mApps) { + // Add the section to the cache + String sectionName = mCachedSectionNames.get(info.title); + if (sectionName == null) { + sectionName = mIndexer.computeSectionName(info.title); + mCachedSectionNames.put(info.title, sectionName); + } + } + } // Prepare to update the list of sections, filtered apps, etc. mFilteredApps.clear(); @@ -444,23 +518,22 @@ public class AlphabeticalAppsList { } // Add all the other apps to the combined list - allApps.addAll(mApps); + if (localeRequiresSectionSorting) { + for (Map.Entry> entry : sectionMap.entrySet()) { + allApps.addAll(entry.getValue()); + } + } else { + allApps.addAll(mApps); + } // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the - // combined list + // ordered set of sections int numApps = allApps.size(); for (int i = 0; i < numApps; i++) { boolean isPredictedApp = i < numPredictedApps; AppInfo info = allApps.get(i); - String sectionName = ""; - if (!isPredictedApp) { - // Only cache section names from non-predicted apps - sectionName = mCachedSectionNames.get(info.title); - if (sectionName == null) { - sectionName = mIndexer.computeSectionName(info.title); - mCachedSectionNames.put(info.title, sectionName); - } - } + // The section name was computed above so this should be find + String sectionName = isPredictedApp ? "" : mCachedSectionNames.get(info.title); // Check if we want to retain this app if (mFilter != null && !mFilter.retainApp(info, sectionName)) { @@ -478,7 +551,7 @@ public class AlphabeticalAppsList { mFastScrollerSections.add(lastFastScrollerSectionInfo); // Create a new section item to break the flow of items in the list - if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) { + if (!hasFilter()) { AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); mSectionedFilteredApps.add(sectionItem); } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 563044916..a6902d5d3 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -63,11 +63,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (mApps.getAdapterItems().get(position).isSectionHeader) { // Section break spans full width - if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) { - return 0; - } else { - return mAppsPerRow; - } + return mAppsPerRow; } else { return 1; } @@ -290,10 +286,8 @@ class AppsGridAdapter extends RecyclerView.Adapter { mTouchListener = touchListener; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; - if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) { - mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); - mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset); - } + mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); + mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset); mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset); mSectionTextPaint = new Paint(); @@ -342,10 +336,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { */ public RecyclerView.ItemDecoration getItemDecoration() { // We don't draw any headers when we are uncomfortably dense - if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) { - return mItemDecoration; - } - return null; + return mItemDecoration; } /** diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 7a6e9a20a..3bbf0e7d8 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -437,13 +437,6 @@ public class DeviceProfile { } public boolean updateAppsViewNumCols(Resources res, int containerWidth) { - if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) { - if (appsViewNumCols != allAppsNumCols) { - appsViewNumCols = allAppsNumCols; - return true; - } - return false; - } int appsViewLeftMarginPx = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java index 18cdc81f3..ec1fb669f 100644 --- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -52,12 +52,15 @@ class BaseAlphabeticIndex { */ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { + private static final String MID_DOT = "\u2219"; + private Object mAlphabeticIndex; private Method mAddLabelsMethod; private Method mSetMaxLabelCountMethod; private Method mGetBucketIndexMethod; private Method mGetBucketLabelMethod; private boolean mHasValidAlphabeticIndex; + private String mDefaultMiscLabel; public AlphabeticIndexCompat(Context context) { super(); @@ -72,12 +75,20 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { mAlphabeticIndex = ctor.newInstance(curLocale); try { // Ensure we always have some base English locale buckets - if (!curLocale.getLanguage().equals(new Locale("en").getLanguage())) { + if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { mAddLabelsMethod.invoke(mAlphabeticIndex, Locale.ENGLISH); } } catch (Exception e) { e.printStackTrace(); } + if (curLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) { + // Japanese character 他 ("misc") + mDefaultMiscLabel = "\u4ed6"; + // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji + } else { + // Dot + mDefaultMiscLabel = MID_DOT; + } mHasValidAlphabeticIndex = true; } catch (Exception e) { mHasValidAlphabeticIndex = false; @@ -107,13 +118,21 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { String s = Utilities.trim(cs); String sectionName = getBucketLabel(getBucketIndex(s)); if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) { - boolean startsWithDigit = Character.isDigit(s.codePointAt(0)); + int c = s.codePointAt(0); + boolean startsWithDigit = Character.isDigit(c); if (startsWithDigit) { // Digit section return "#"; } else { - // Unknown section - return "\u2022"; + boolean startsWithLetter = Character.isLetter(c); + if (startsWithLetter) { + return mDefaultMiscLabel; + } else { + // In languages where these differ, this ensures that we differentiate + // between the misc section in the native language and a misc section + // for everything else. + return MID_DOT; + } } } return sectionName; -- cgit v1.2.3 From 8af0cd808e62973889cd43e2c989220f27d70dd4 Mon Sep 17 00:00:00 2001 From: Jun Mukai Date: Tue, 12 May 2015 12:32:12 -0700 Subject: Introduce LauncherSearchCallback to handle search overlay status. Bug: 20011047 Change-Id: I93cab4e0614b9658b4b657dd98dca68d42580e63 --- src/com/android/launcher3/Launcher.java | 41 ++++++++++++++++++++++++ src/com/android/launcher3/LauncherCallbacks.java | 7 ++++ src/com/android/launcher3/LauncherExtension.java | 5 +++ 3 files changed, 53 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 16e4ce644..3d8890642 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -546,6 +546,35 @@ public class Launcher extends Activity } } }); + mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() { + private boolean mImportanceStored = false; + private int mWorkspaceImportanceForAccessibility = + View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; + private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; + + @Override + public void onSearchOverlayOpened() { + if (mImportanceStored) { + return; + } + // The underlying workspace and hotseat are temporarily suppressed by the search + // overlay. So they sholudn't be accessible. + mWorkspaceImportanceForAccessibility = mWorkspace.getImportantForAccessibility(); + mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility(); + mWorkspace.setImportantForAccessibility( + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + mHotseat.setImportantForAccessibility( + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + mImportanceStored = true; + } + + @Override + public void onSearchOverlayClosed() { + mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility); + mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility); + mImportanceStored = false; + } + }); return true; } @@ -1192,6 +1221,18 @@ public class Launcher extends Activity public void dismissAllApps(); } + public interface LauncherSearchCallbacks { + /** + * Called when the search overlay is shown. + */ + public void onSearchOverlayOpened(); + + /** + * Called when the search overlay is dismissed. + */ + public void onSearchOverlayClosed(); + } + public interface LauncherOverlayCallbacks { /** * This method indicates whether a call to {@link #enterFullImmersion()} will succeed, diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 0124d1f28..a5f36ba93 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -119,4 +119,11 @@ public interface LauncherCallbacks { */ public void setLauncherAppsCallback(Object callbacks); + /** + * Sets the callbacks to allow reacting the actions of search overlays of the launcher. + * + * @param callbacks A set of callbacks to the Launcher, is actually a LauncherSearchCallback, + * but for implementation purposes is passed around as an object. + */ + public void setLauncherSearchCallback(Object callbacks); } diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index 14ad6016c..09a105bcc 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -289,6 +289,11 @@ public class LauncherExtension extends Launcher { // Do nothing } + @Override + public void setLauncherSearchCallback(Object callbacks) { + // Do nothing + } + class LauncherExtensionOverlay implements LauncherOverlay { LauncherOverlayCallbacks mLauncherOverlayCallbacks; ViewGroup mOverlayView; -- cgit v1.2.3 From 5cd1d92f6c40b47eff12859d4e13b6431aa778c0 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 12 May 2015 15:36:20 -0700 Subject: Fixing several layout issues. - N7/N9 indented area should not block scroll view - separator at the end of the widget cell should not show up on the last item. Not covered in this CL: - Guarantee that last item is showing only 20~40%. Right now, the hand picked constant works for N5,6,7,9 b/20338324 b/20763871 Change-Id: I274ab95c6c3e2fc8be8ceafb2e8172c0174a41a5 --- res/drawable/widgets_row_divider.xml | 20 ++++++ res/layout/widget_cell.xml | 12 ++-- res/layout/widgets_list_row_view.xml | 37 +++++------ res/values/dimens.xml | 1 - src/com/android/launcher3/widget/WidgetCell.java | 30 ++------- .../android/launcher3/widget/WidgetRowView.java | 74 ---------------------- .../launcher3/widget/WidgetsListAdapter.java | 39 +++++++++--- 7 files changed, 74 insertions(+), 139 deletions(-) create mode 100644 res/drawable/widgets_row_divider.xml delete mode 100644 src/com/android/launcher3/widget/WidgetRowView.java diff --git a/res/drawable/widgets_row_divider.xml b/res/drawable/widgets_row_divider.xml new file mode 100644 index 000000000..46caacbbe --- /dev/null +++ b/res/drawable/widgets_row_divider.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml index 196dfca66..a85f0aa16 100644 --- a/res/layout/widget_cell.xml +++ b/res/layout/widget_cell.xml @@ -16,13 +16,12 @@ - - + \ No newline at end of file diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index 8bc8be44a..2cbdb5cff 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -48,26 +48,19 @@ launcher:iconSizeOverride="@dimen/widget_section_icon_size" launcher:layoutHorizontal="true" /> - - - - - - + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 6280e5247..f944d4b3c 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -101,7 +101,6 @@ 146dp - 150dp 8dp 2dp diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 877937151..7ca4df979 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -51,14 +51,13 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final int FADE_IN_DURATION_MS = 90; /** Widget cell width is calculated by multiplying this factor to grid cell width. */ - private static final float WIDTH_SCALE = 2.8f; + private static final float WIDTH_SCALE = 2.6f; /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ - private static final float PREVIEW_SCALE = 0.9f; + private static final float PREVIEW_SCALE = 0.8f; - private static int mPresetPreviewSize; - private static int mSize; - private static int mDividerWidth; + private int mPresetPreviewSize; + int cellSize; private ImageView mWidgetImage; private TextView mWidgetName; @@ -93,14 +92,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } private void setContainerWidth() { - // Do nothing if already set - if (mSize > 0) { - return; - } DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); - mSize = (int) (profile.cellWidthPx * WIDTH_SCALE); + cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE); mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE); - mDividerWidth = getResources().getDimensionPixelSize(R.dimen.widget_row_divider); } @Override @@ -117,12 +111,6 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY)); - } - /** * Called to clear the view and free attached resources. (e.g., {@link Bitmap} */ @@ -133,7 +121,6 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetImage.setImageDrawable(null); mWidgetName.setText(null); mWidgetDims.setText(null); - setSeparator(true); if (mActiveRequest != null) { mActiveRequest.cleanup(); @@ -254,11 +241,4 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } return ""; } - - public void setSeparator(boolean enable) { - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams(); - lp.setMarginEnd(enable? mDividerWidth : 0); - setLayoutParams(lp); - requestLayout(); - } } diff --git a/src/com/android/launcher3/widget/WidgetRowView.java b/src/com/android/launcher3/widget/WidgetRowView.java deleted file mode 100644 index 05760ae48..000000000 --- a/src/com/android/launcher3/widget/WidgetRowView.java +++ /dev/null @@ -1,74 +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.widget; - -import android.content.Context; -import android.content.res.Resources; -import android.util.AttributeSet; -import android.widget.LinearLayout; - - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.DynamicGrid; -import com.android.launcher3.LauncherAppState; - -/** - * Represents the individual cell of the widget inside the widget tray. - */ -public class WidgetRowView extends LinearLayout { - - private static final int PRESET_INDENT_SIZE_TABLET = 56; - - /** Widget row width is calculated by multiplying this factor to grid cell width. */ - private static final float HEIGHT_SCALE = 2.8f; - - static int sIndent = 0; - static int sHeight = 0; - - public WidgetRowView(Context context) { - this(context, null); - } - - public WidgetRowView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WidgetRowView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setContainerHeight(); - setWillNotDraw(false); - setClipToPadding(false); - setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); - } - - /** - * Sets the widget cell container size based on the physical dimension of the device. - */ - private void setContainerHeight() { - // Do nothing if already set - if (sHeight > 0) { - return; - } - - Resources r = getResources(); - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); - if (profile.isLargeTablet || profile.isTablet) { - sIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); - } - sHeight = (int) (profile.cellWidthPx * HEIGHT_SCALE); - } -} diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index d114883ad..d45ff1d46 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -18,14 +18,19 @@ package com.android.launcher3.widget; import android.content.Context; import android.content.pm.ResolveInfo; import android.support.v7.widget.RecyclerView; +import android.content.res.Resources; import android.support.v7.widget.RecyclerView.Adapter; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DynamicGrid; import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; @@ -46,7 +51,7 @@ import java.util.List; public class WidgetsListAdapter extends Adapter { private static final String TAG = "WidgetsListAdapter"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private Context mContext; private Launcher mLauncher; @@ -59,6 +64,9 @@ public class WidgetsListAdapter extends Adapter { private View.OnClickListener mIconClickListener; private View.OnLongClickListener mIconLongClickListener; + private static final int PRESET_INDENT_SIZE_TABLET = 56; + private int mIndent = 0; + public WidgetsListAdapter(Context context, View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener, @@ -71,6 +79,8 @@ public class WidgetsListAdapter extends Adapter { mLauncher = launcher; mIconCache = LauncherAppState.getInstance().getIconCache(); + + setContainerHeight(); } public void setWidgetsModel(WidgetsModel w) { @@ -96,6 +106,7 @@ public class WidgetsListAdapter extends Adapter { // Add more views. // if there are too many, hide them. int diff = infoList.size() - row.getChildCount(); + if (diff > 0) { for (int i = 0; i < diff; i++) { WidgetCell widget = new WidgetCell(mContext); @@ -105,10 +116,11 @@ public class WidgetsListAdapter extends Adapter { // set up touch. widget.setOnClickListener(mIconClickListener); widget.setOnLongClickListener(mIconLongClickListener); - // Add a devider if it is not the last item. - if (i == diff - 1) { - widget.setSeparator(false); - } + LayoutParams lp = widget.getLayoutParams(); + lp.height = widget.cellSize; + lp.width = widget.cellSize; + widget.setLayoutParams(lp); + row.addView(widget); } } else if (diff < 0) { @@ -152,11 +164,10 @@ public class WidgetsListAdapter extends Adapter { ViewGroup container = (ViewGroup) mLayoutInflater.inflate( R.layout.widgets_list_row_view, parent, false); - WidgetRowView row = (WidgetRowView) container.findViewById(R.id.widget_row); - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) row.getLayoutParams(); - lp.setMarginStart(WidgetRowView.sIndent); - lp.height = WidgetRowView.sHeight; - row.setLayoutParams(lp); + View cellList = container.findViewById(R.id.widgets_cell_list); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) cellList.getLayoutParams(); + lp.setMarginStart(mIndent); + return new WidgetsRowViewHolder(container); } @@ -181,4 +192,12 @@ public class WidgetsListAdapter extends Adapter { } return mWidgetPreviewLoader; } + + private void setContainerHeight() { + Resources r = mContext.getResources(); + DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + if (profile.isLargeTablet || profile.isTablet) { + mIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); + } + } } -- cgit v1.2.3 From 1e62c1594acba67486ec2ded56aed00f5cfaff75 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 12 May 2015 15:58:09 -0700 Subject: Unbreak the build. Remove extra xml version definition. Change-Id: I42f1c21143bef6181828d1135deaaf153acbf984 --- res/drawable/widgets_row_divider.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/res/drawable/widgets_row_divider.xml b/res/drawable/widgets_row_divider.xml index 46caacbbe..074d60167 100644 --- a/res/drawable/widgets_row_divider.xml +++ b/res/drawable/widgets_row_divider.xml @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - -- cgit v1.2.3 From 426b94b207f0a09df5fd0826891ca2a4732aff30 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 12 May 2015 16:27:50 -0700 Subject: Typecast to correct LayoutParams Change-Id: Ifdd04923563a0ef57bd281d1b55b8af0e4cc59f0 --- src/com/android/launcher3/widget/WidgetsListAdapter.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index d45ff1d46..d6e062874 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -25,6 +25,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewGroup.MarginLayoutParams; import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; @@ -164,10 +165,10 @@ public class WidgetsListAdapter extends Adapter { ViewGroup container = (ViewGroup) mLayoutInflater.inflate( R.layout.widgets_list_row_view, parent, false); - View cellList = container.findViewById(R.id.widgets_cell_list); - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) cellList.getLayoutParams(); + LinearLayout cellList = (LinearLayout) container.findViewById(R.id.widgets_cell_list); + MarginLayoutParams lp = (MarginLayoutParams) cellList.getLayoutParams(); lp.setMarginStart(mIndent); - + cellList.setLayoutParams(lp); return new WidgetsRowViewHolder(container); } -- cgit v1.2.3 From a6095969bb5938ad95ee9f858970d5eb54eb124d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 May 2015 13:30:07 -0700 Subject: Preventing NullPointerException when cancelling a shortcut addition Bug: 21024018 Change-Id: Iac09d9e4f1411aa9fe1ec89fbfe749b009c7d457 (cherry picked from commit f044bb1edd8670fbc2cf9eeb1a51c31b91f0889c) --- src/com/android/launcher3/Launcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5b8eaef50..2fc3b75ea 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1319,7 +1319,9 @@ public class Launcher extends Activity mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); AppWidgetProviderInfo info = savedState.getParcelable( RUNTIME_STATE_PENDING_ADD_WIDGET_INFO); - mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this, info); + mPendingAddWidgetInfo = info == null ? + null : LauncherAppWidgetProviderInfo.fromProviderInfo(this, info); + mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID); setWaitingForResult(true); mRestoring = true; -- cgit v1.2.3 From ed0c1cc739d3b3d82c301299db8abc3146de49bb Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 12 May 2015 13:01:54 -0700 Subject: Minor visual tweaks. - Drawing full divider under predicted apps - Enabling dynamic search bar elevation - Increasing fast scroller pop-up size - Insetting the padding to prevent the scroll bar from overlapping with app titles Bug: 20299865 Change-Id: I48ffde43c2158c23b54cd43fede722da41ccc111 --- res/values-sw600dp/dimens.xml | 1 - res/values/dimens.xml | 9 +- .../launcher3/AppsContainerRecyclerView.java | 102 ++++++++++++++++----- src/com/android/launcher3/AppsContainerView.java | 68 ++++++++------ src/com/android/launcher3/AppsGridAdapter.java | 11 +-- .../launcher3/BaseContainerRecyclerView.java | 30 +++++- 6 files changed, 154 insertions(+), 67 deletions(-) diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 2bdd4f0fe..4a869e5aa 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -21,7 +21,6 @@ 18dp 0dp 26sp - 72dp 12dp diff --git a/res/values/dimens.xml b/res/values/dimens.xml index f944d4b3c..90d709887 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -50,15 +50,14 @@ 0dp 0dp 8dp - 52dp + 56dp 8dp - 64dp 24sp - 6dp + 4dp 64dp -16dp - 64dp - 40dp + 72dp + 48dp 52dp 8dp diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index edb6f0c6e..e918bc2ee 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -38,6 +38,23 @@ import java.util.List; */ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { + /** + * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() + * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so + * that we can calculate what the scroll bar looks like, and where to jump to from the fast + * scroller. + */ + private static class ScrollPositionState { + // The index of the first app in the row (Note that is this not the position) + int rowFirstAppIndex; + // The index of the first visible row + int rowIndex; + // The offset of the first visible row + int rowTopOffset; + // The height of a given row (they are currently all the same height) + int rowHeight; + } + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; private AlphabeticalAppsList mApps; @@ -58,6 +75,8 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { private int mScrollbarWidth; private int mScrollbarMinHeight; private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); + private ScrollPositionState mScrollPosState = new ScrollPositionState(); public AppsContainerRecyclerView(Context context) { this(context, null); @@ -91,6 +110,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mScrollbarInset = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(getFastScrollerAlpha()); + setOverScrollMode(View.OVER_SCROLL_NEVER); } /** @@ -107,6 +127,12 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mNumAppsPerRow = rowSize; } + @Override + public void setBackground(Drawable background) { + super.setBackground(background); + background.getPadding(mBackgroundPadding); + } + /** * Sets the fast scroller alpha. */ @@ -129,6 +155,14 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { return mScrollbarWidth; } + /** + * Scrolls this recycler view to the top. + */ + public void scrollToTop() { + scrollToPosition(0); + updateScrollY(0); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -238,7 +272,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { // Calculate the position for the fast scroller popup Rect bgBounds = mFastScrollerBg.getBounds(); if (isRtl) { - x = getPaddingLeft() + getScrollBarSize(); + x = mBackgroundPadding.left + getScrollBarSize(); } else { x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); } @@ -281,7 +315,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { * Invalidates the fast scroller popup. */ private void invalidateFastScroller() { - invalidate(getWidth() - getPaddingRight() - getScrollBarSize() - + invalidate(getWidth() - mBackgroundPadding.right - getScrollBarSize() - mFastScrollerBg.getIntrinsicWidth(), 0, getWidth(), getHeight()); } @@ -312,6 +346,15 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { stopScroll(); layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); + // We need to workaround the RecyclerView to get the right scroll position after scrolling + List items = mApps.getAdapterItems(); + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex != -1) { + int rowIndex = findRowForAppIndex(mScrollPosState.rowFirstAppIndex); + int y = (rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset; + updateScrollY(y); + } + return lastScrollSection.sectionName; } @@ -332,44 +375,29 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { int y; boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL); - int rowIndex = -1; - int rowTopOffset = -1; - int rowHeight = -1; int rowCount = getNumRows(); - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - int position = getChildPosition(child); - if (position != NO_POSITION) { - AlphabeticalAppsList.AdapterItem item = items.get(position); - if (!item.isSectionHeader) { - rowIndex = findRowForAppIndex(item.appIndex); - rowTopOffset = getLayoutManager().getDecoratedTop(child); - rowHeight = child.getHeight(); - break; - } - } - } + getCurScrollState(mScrollPosState, items); - if (rowIndex != -1) { + if (mScrollPosState.rowIndex != -1) { int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * rowHeight; + int totalScrollHeight = rowCount * mScrollPosState.rowHeight; if (totalScrollHeight > height) { int scrollbarHeight = Math.max(mScrollbarMinHeight, (int) (height / ((float) totalScrollHeight / height))); // Calculate the position and size of the scroll bar if (isRtl) { - x = getPaddingLeft(); + x = mBackgroundPadding.left; } else { - x = getWidth() - getPaddingRight() - mScrollbarWidth; + x = getWidth() - mBackgroundPadding.right - mScrollbarWidth; } // To calculate the offset, we compute the percentage of the total scrollable height // that the user has already scrolled and then map that to the scroll bar bounds int availableY = totalScrollHeight - height; int availableScrollY = height - scrollbarHeight; - y = (rowIndex * rowHeight) - rowTopOffset; + y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - + mScrollPosState.rowTopOffset; y = getPaddingTop() + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); @@ -410,4 +438,30 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } return rowCount; } + + /** + * Returns the current scroll state. + */ + private void getCurScrollState(ScrollPositionState stateOut, + List items) { + stateOut.rowFirstAppIndex = -1; + stateOut.rowIndex = -1; + stateOut.rowTopOffset = -1; + stateOut.rowHeight = -1; + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + int position = getChildPosition(child); + if (position != NO_POSITION) { + AlphabeticalAppsList.AdapterItem item = items.get(position); + if (!item.isSectionHeader) { + stateOut.rowFirstAppIndex = item.appIndex; + stateOut.rowIndex = findRowForAppIndex(item.appIndex); + stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); + stateOut.rowHeight = child.getHeight(); + break; + } + } + } + } } diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 8a5c6605e..5dac9f1e8 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -51,9 +51,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource, public static final boolean GRID_HIDE_SECTION_HEADERS = false; private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; - private static final boolean DYNAMIC_HEADER_ELEVATION = false; + private static final boolean DYNAMIC_HEADER_ELEVATION = true; private static final boolean DISMISS_SEARCH_ON_BACK = true; private static final float HEADER_ELEVATION_DP = 4; + // How far the user has to scroll in order to reach the full elevation + private static final float HEADER_SCROLL_TO_ELEVATION_DP = 16; private static final int FADE_IN_DURATION = 175; private static final int FADE_OUT_DURATION = 100; private static final int SEARCH_TRANSLATION_X_DP = 18; @@ -159,8 +161,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, * Scrolls this list view to the top. */ public void scrollToTop() { - mAppsRecyclerView.scrollToPosition(0); - mRecyclerViewScrollY = 0; + mAppsRecyclerView.scrollToTop(); } /** @@ -230,18 +231,14 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); mAppsRecyclerView.setHasFixedSize(true); - mAppsRecyclerView.setOnScrollListenerProxy(new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - // Do nothing - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mRecyclerViewScrollY += dy; - onRecyclerViewScrolled(); - } - }); + mAppsRecyclerView.setOnScrollListenerProxy( + new BaseContainerRecyclerView.OnScrollToListener() { + @Override + public void onScrolledTo(int x, int y) { + mRecyclerViewScrollY = y; + onRecyclerViewScrolled(); + } + }); if (mItemDecoration != null) { mAppsRecyclerView.addItemDecoration(mItemDecoration); } @@ -291,9 +288,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource, int startMargin = grid.isPhone() ? mContentMarginStart : 0; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; if (isRtl) { - mAppsRecyclerView.setPadding(inset, inset, inset + startMargin, inset); + mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset, + inset + startMargin, inset); } else { - mAppsRecyclerView.setPadding(inset + startMargin, inset, inset, inset); + mAppsRecyclerView.setPadding(inset + startMargin, inset, + inset + mAppsRecyclerView.getScrollbarWidth(), inset); } // Update the header bar @@ -456,7 +455,10 @@ public class AppsContainerView extends BaseContainerView implements DragSource, String formatStr = getResources().getString(R.string.apps_view_no_search_results); mAdapter.setEmptySearchText(String.format(formatStr, queryText)); + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. final String queryTextLower = queryText.toLowerCase(); + final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); mApps.setFilter(new AlphabeticalAppsList.Filter() { @Override public boolean retainApp(AppInfo info, String sectionName) { @@ -465,12 +467,21 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } String title = info.title.toString(); String[] words = SPLIT_PATTERN.split(title.toLowerCase()); - for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(queryTextLower)) { - return true; + for (int qi = 0; qi < queryWords.length; qi++) { + boolean foundMatch = false; + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(queryWords[qi])) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + // If there is a word in the query that does not match any words in this + // title, so skip it. + return false; } } - return false; + return true; } }); } @@ -531,11 +542,16 @@ public class AppsContainerView extends BaseContainerView implements DragSource, * Updates the container when the recycler view is scrolled. */ private void onRecyclerViewScrolled() { - if (DYNAMIC_HEADER_ELEVATION) { - int elevation = Math.min(mRecyclerViewScrollY, DynamicGrid.pxFromDp(HEADER_ELEVATION_DP, - getContext().getResources().getDisplayMetrics())); - if (Float.compare(mHeaderView.getElevation(), elevation) != 0) { - mHeaderView.setElevation(elevation); + if (DYNAMIC_HEADER_ELEVATION && Utilities.isLmpOrAbove()) { + int elevation = DynamicGrid.pxFromDp(HEADER_ELEVATION_DP, + getContext().getResources().getDisplayMetrics()); + int scrollToElevation = DynamicGrid.pxFromDp(HEADER_SCROLL_TO_ELEVATION_DP, + getContext().getResources().getDisplayMetrics()); + float elevationPct = (float) Math.min(mRecyclerViewScrollY, scrollToElevation) / + scrollToElevation; + float newElevation = elevation * elevationPct; + if (Float.compare(mHeaderView.getElevation(), newElevation) != 0) { + mHeaderView.setElevation(newElevation); } } } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index a6902d5d3..4014e3804 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -101,11 +101,10 @@ class AppsGridAdapter extends RecyclerView.Adapter { if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) { // Draw the divider under the predicted app + parent.getBackground().getPadding(mTmpBounds); int top = child.getTop() + child.getHeight(); - int left = parent.getPaddingLeft(); - int right = parent.getWidth() - parent.getPaddingRight(); - int iconInset = (((right - left) / mAppsPerRow) - grid.allAppsIconSizePx) / 2; - c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint); + c.drawLine(mTmpBounds.left, top, parent.getWidth() - mTmpBounds.right, top, + mPredictedAppsDividerPaint); hasDrawnPredictedAppDivider = true; } else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) { @@ -297,8 +296,8 @@ class AppsGridAdapter extends RecyclerView.Adapter { mSectionTextPaint.setAntiAlias(true); mPredictedAppsDividerPaint = new Paint(); - mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1.5f, res.getDisplayMetrics())); - mPredictedAppsDividerPaint.setColor(0x10000000); + mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1f, res.getDisplayMetrics())); + mPredictedAppsDividerPaint.setColor(0x1E000000); mPredictedAppsDividerPaint.setAntiAlias(true); } diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java index 5b30e3df6..59e20ca2f 100644 --- a/src/com/android/launcher3/BaseContainerRecyclerView.java +++ b/src/com/android/launcher3/BaseContainerRecyclerView.java @@ -29,12 +29,20 @@ import com.android.launcher3.util.Thunk; public class BaseContainerRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { + /** + * Listener to get notified when the absolute scroll changes. + */ + public interface OnScrollToListener { + void onScrolledTo(int x, int y); + } + private static final int SCROLL_DELTA_THRESHOLD_DP = 4; /** Keeps the last known scrolling delta/velocity along y-axis. */ @Thunk int mDy = 0; + @Thunk int mScrollY; private float mDeltaThreshold; - private RecyclerView.OnScrollListener mScrollListenerProxy; + private OnScrollToListener mScrollToListener; public BaseContainerRecyclerView(Context context) { this(context, null); @@ -60,8 +68,9 @@ public class BaseContainerRecyclerView extends RecyclerView @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { mDy = dy; - if (mScrollListenerProxy != null) { - mScrollListenerProxy.onScrolled(recyclerView, dx, dy); + mScrollY += dy; + if (mScrollToListener != null) { + mScrollToListener.onScrolledTo(0, mScrollY); } } } @@ -69,8 +78,8 @@ public class BaseContainerRecyclerView extends RecyclerView /** * Sets an additional scroll listener, only needed for LMR1 version of the support lib. */ - public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) { - mScrollListenerProxy = listener; + public void setOnScrollListenerProxy(OnScrollToListener listener) { + mScrollToListener = listener; } @Override @@ -96,6 +105,17 @@ public class BaseContainerRecyclerView extends RecyclerView // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS } + /** + * Updates the scroll position, used to workaround a RecyclerView issue with scrolling to + * position. + */ + protected void updateScrollY(int scrollY) { + mScrollY = scrollY; + if (mScrollToListener != null) { + mScrollToListener.onScrolledTo(0, mScrollY); + } + } + /** * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. */ -- cgit v1.2.3 From 11509ad61afb7424ce83057b0d2a4b09f853651f Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 12 May 2015 18:55:26 -0700 Subject: Fixing regression from g/649060 regarding building layers for detached views. Bug: 21063014 Change-Id: Iff0dba8be5fe7ef5dce4fc65fb6391c33c50b253 --- src/com/android/launcher3/LauncherStateTransitionAnimation.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 51f84bfcd..73ae51c3e 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -378,11 +378,12 @@ public class LauncherStateTransitionAnimation { dispatchOnLauncherTransitionStart(toView, animated, false); // Enable all necessary layers + boolean isLmpOrAbove = Utilities.isLmpOrAbove(); for (View v : layerViews.keySet()) { if (layerViews.get(v) == BUILD_AND_SET_LAYER) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - if (Utilities.isViewAttachedToWindow(v)) { + if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) { v.buildLayer(); } } @@ -697,11 +698,12 @@ public class LauncherStateTransitionAnimation { dispatchOnLauncherTransitionStart(toView, animated, false); // Enable all necessary layers + boolean isLmpOrAbove = Utilities.isLmpOrAbove(); for (View v : layerViews.keySet()) { if (layerViews.get(v) == BUILD_AND_SET_LAYER) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - if (Utilities.isLmpOrAbove()) { + if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) { v.buildLayer(); } } -- cgit v1.2.3 From 208ed75cfdb02e571273d73d056d8ed7f6f756eb Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 12 May 2015 19:05:30 -0700 Subject: Pulling out predictions into another row view. Change-Id: Iba0d74457a1314cf0c00a88f9b07df049334e542 --- res/layout/all_apps_button.xml | 2 +- res/layout/application.xml | 2 +- res/layout/apps_grid_icon_view.xml | 30 ++++ res/layout/apps_grid_row_icon_view.xml | 30 ---- res/layout/apps_list_view.xml | 43 +++--- res/layout/apps_prediction_bar_icon_view.xml | 29 ++++ res/layout/folder_application.xml | 2 +- res/layout/folder_icon.xml | 2 +- res/values/dimens.xml | 1 + res/values/styles.xml | 10 +- .../android/launcher3/AlphabeticalAppsList.java | 160 +++++++++++++-------- .../launcher3/AppsContainerRecyclerView.java | 65 ++++++--- src/com/android/launcher3/AppsContainerView.java | 117 +++++++++++++-- src/com/android/launcher3/AppsGridAdapter.java | 102 ++++++++----- src/com/android/launcher3/DeviceProfile.java | 12 +- 15 files changed, 414 insertions(+), 193 deletions(-) create mode 100644 res/layout/apps_grid_icon_view.xml delete mode 100644 res/layout/apps_grid_row_icon_view.xml create mode 100644 res/layout/apps_prediction_bar_icon_view.xml diff --git a/res/layout/all_apps_button.xml b/res/layout/all_apps_button.xml index 9d6d82bb2..68cc10932 100644 --- a/res/layout/all_apps_button.xml +++ b/res/layout/all_apps_button.xml @@ -15,5 +15,5 @@ --> diff --git a/res/layout/application.xml b/res/layout/application.xml index c21dea070..831cee5b0 100644 --- a/res/layout/application.xml +++ b/res/layout/application.xml @@ -15,5 +15,5 @@ --> diff --git a/res/layout/apps_grid_icon_view.xml b/res/layout/apps_grid_icon_view.xml new file mode 100644 index 000000000..67d7d50e7 --- /dev/null +++ b/res/layout/apps_grid_icon_view.xml @@ -0,0 +1,30 @@ + + + + diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml deleted file mode 100644 index acb3da334..000000000 --- a/res/layout/apps_grid_row_icon_view.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index ddcb639b8..ef20323c1 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -13,20 +13,37 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + + + + - - \ No newline at end of file + \ No newline at end of file diff --git a/res/layout/apps_prediction_bar_icon_view.xml b/res/layout/apps_prediction_bar_icon_view.xml new file mode 100644 index 000000000..4a6f1574b --- /dev/null +++ b/res/layout/apps_prediction_bar_icon_view.xml @@ -0,0 +1,29 @@ + + + + diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml index b48b61331..4d003313e 100644 --- a/res/layout/folder_application.xml +++ b/res/layout/folder_application.xml @@ -15,5 +15,5 @@ --> diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml index fd45d7685..d9a7671af 100644 --- a/res/layout/folder_icon.xml +++ b/res/layout/folder_icon.xml @@ -28,7 +28,7 @@ android:antialias="true" android:src="@drawable/portal_ring_inner_holo"/> 48dp 52dp 8dp + 12dp 8dp diff --git a/res/values/styles.xml b/res/values/styles.xml index 16d4cbeed..78cc0837f 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -19,7 +19,7 @@ - - - - - - - - - - - + - diff --git a/res/values/styles.xml b/res/values/styles.xml index f95debeab..1496da994 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -86,9 +86,4 @@ 4dp - - - diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index f5c29ae0a..2191455d5 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -174,19 +174,18 @@ public class DragController { * @param dragInfo The data associated with the object that is being dragged * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or * {@link #DRAG_ACTION_COPY} + * @param viewImageBounds the position of the image inside the view * @param dragRegion Coordinates within the bitmap b for the position of item being dragged. * Makes dragging feel more precise, e.g. you can clip out a transparent border */ - public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction, - Point extraPadding, float initialDragViewScale) { + public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, + Rect viewImageBounds, int dragAction, float initialDragViewScale) { int[] loc = mCoordinatesTemp; mLauncher.getDragLayer().getLocationInDragLayer(v, loc); - int viewExtraPaddingLeft = extraPadding != null ? extraPadding.x : 0; - int viewExtraPaddingTop = extraPadding != null ? extraPadding.y : 0; - int dragLayerX = loc[0] + v.getPaddingLeft() + viewExtraPaddingLeft + - (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2); - int dragLayerY = loc[1] + v.getPaddingTop() + viewExtraPaddingTop + - (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2); + int dragLayerX = loc[0] + viewImageBounds.left + + (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2); + int dragLayerY = loc[1] + viewImageBounds.top + + (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2); startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null, null, initialDragViewScale, false); diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index e3eb76c25..dcaf1f211 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -21,17 +21,14 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.View.OnLayoutChangeListener; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; @@ -41,7 +38,13 @@ import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; import com.android.launcher3.compat.AppWidgetManagerCompat; /** - * Represents the individual cell of the widget inside the widget tray. + * Represents the individual cell of the widget inside the widget tray. The preview is drawn + * horizontally centered, and scaled down if needed. + * + * This view does not support padding. Since the image is scaled down to fit the view, padding will + * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth + * transition from the view to drag view, so when adding padding support, DnD would need to + * consider the appropriate scaling factor. */ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { @@ -59,13 +62,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private int mPresetPreviewSize; int cellSize; - private ImageView mWidgetImage; + private WidgetImageView mWidgetImage; private TextView mWidgetName; private TextView mWidgetDims; - private final Rect mOrigImgPadding = new Rect(); private String mDimensionsFormatString; - private boolean mIsAppWidget; private Object mInfo; private WidgetPreviewLoader mWidgetPreviewLoader; @@ -101,12 +102,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { protected void onFinishInflate() { super.onFinishInflate(); - mWidgetImage = (ImageView) findViewById(R.id.widget_preview); - mOrigImgPadding.left = mWidgetImage.getPaddingLeft(); - mOrigImgPadding.top = mWidgetImage.getPaddingTop(); - mOrigImgPadding.right = mWidgetImage.getPaddingRight(); - mOrigImgPadding.bottom = mWidgetImage.getPaddingBottom(); - + mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview); mWidgetName = ((TextView) findViewById(R.id.widget_name)); mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } @@ -119,7 +115,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { Log.d(TAG, "reset called on:" + mWidgetName.getText()); } mWidgetImage.animate().cancel(); - mWidgetImage.setImageDrawable(null); + mWidgetImage.setBitmap(null); mWidgetName.setText(null); mWidgetDims.setText(null); @@ -133,15 +129,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { * Apply the widget provider info to the view. */ public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, - int maxWidth, WidgetPreviewLoader loader) { + WidgetPreviewLoader loader) { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mIsAppWidget = true; mInfo = info; - if (maxWidth > -1) { - mWidgetImage.setMaxWidth(maxWidth); - } // TODO(hyunyoungs): setup a cache for these labels. mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); int hSpan = Math.min(info.spanX, grid.numColumns); @@ -155,7 +147,6 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { */ public void applyFromResolveInfo( PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) { - mIsAppWidget = false; mInfo = info; CharSequence label = info.loadLabel(pm); mWidgetName.setText(label); @@ -172,20 +163,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } public void applyPreview(Bitmap bitmap) { - FastBitmapDrawable preview = new FastBitmapDrawable(bitmap); - - if (preview != null) { - mWidgetImage.setImageDrawable(preview); - - if (mIsAppWidget) { - // center horizontally - int[] imageSize = getPreviewSize(); - int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2; - mWidgetImage.setPadding(mOrigImgPadding.left + centerAmount, - mOrigImgPadding.top, - mOrigImgPadding.right, - mOrigImgPadding.bottom); - } + if (bitmap != null) { + mWidgetImage.setBitmap(bitmap); mWidgetImage.setAlpha(0f); mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS); } diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java index f1eaf6488..6f8fd897b 100644 --- a/src/com/android/launcher3/widget/WidgetImageView.java +++ b/src/com/android/launcher3/widget/WidgetImageView.java @@ -17,25 +17,73 @@ package com.android.launcher3.widget; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; import android.util.AttributeSet; -import android.widget.ImageView; +import android.view.View; -public class WidgetImageView extends ImageView { +/** + * View that draws a bitmap horizontally centered. If the image width is greater than the view + * width, the image is scaled down appropriately. + */ +public class WidgetImageView extends View { + + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final RectF mDstRectF = new RectF(); + private Bitmap mBitmap; + + public WidgetImageView(Context context) { + super(context); + } public WidgetImageView(Context context, AttributeSet attrs) { super(context, attrs); } + public WidgetImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setBitmap(Bitmap bitmap) { + mBitmap = bitmap; + invalidate(); + } + + public Bitmap getBitmap() { + return mBitmap; + } + @Override protected void onDraw(Canvas canvas) { - canvas.save(); - canvas.clipRect(getScrollX() + getPaddingLeft(), - getScrollY() + getPaddingTop(), - getScrollX() + getRight() - getLeft() - getPaddingRight(), - getScrollY() + getBottom() - getTop() - getPaddingBottom()); - - super.onDraw(canvas); - canvas.restore(); + if (mBitmap != null) { + updateDstRectF(); + canvas.drawBitmap(mBitmap, null, mDstRectF, mPaint); + } + } + + private void updateDstRectF() { + if (mBitmap.getWidth() > getWidth()) { + float scale = ((float) getWidth()) / mBitmap.getWidth(); + mDstRectF.set(0, 0, getWidth(), scale * mBitmap.getHeight()); + } else { + mDstRectF.set( + (getWidth() - mBitmap.getWidth()) * 0.5f, + 0, + (getWidth() + mBitmap.getWidth()) * 0.5f, + mBitmap.getHeight()); + } + } + + /** + * @return the bounds where the image was drawn. + */ + public Rect getBitmapBounds() { + updateDstRectF(); + Rect rect = new Rect(); + mDstRectF.round(rect); + return rect; } } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 00fb225ec..181c08a40 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -19,7 +19,6 @@ package com.android.launcher3.widget; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; @@ -28,8 +27,8 @@ import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; import android.util.Log; import android.view.View; -import android.widget.ImageView; import android.widget.Toast; + import com.android.launcher3.BaseContainerView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; @@ -37,10 +36,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragController; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.Folder; import com.android.launcher3.IconCache; -import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; @@ -222,20 +219,19 @@ public class WidgetsContainerView extends BaseContainerView private boolean beginDraggingWidget(WidgetCell v) { // Get the widget preview as the drag representation - ImageView image = (ImageView) v.findViewById(R.id.widget_preview); + WidgetImageView image = (WidgetImageView) v.findViewById(R.id.widget_preview); PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and // we abort the drag. - if (image.getDrawable() == null) { + if (image.getBitmap() == null) { return false; } // Compose the drag image Bitmap preview; - Bitmap outline; float scale = 1f; - Point previewPadding = null; + final Rect bounds = image.getBitmapBounds(); if (createItemInfo instanceof PendingAddWidgetInfo) { // This can happen in some weird cases involving multi-touch. We can't start dragging @@ -244,25 +240,25 @@ public class WidgetsContainerView extends BaseContainerView PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo; int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true); - FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); + Bitmap icon = image.getBitmap(); float minScale = 1.25f; - int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); + int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]); int[] previewSizeBeforeScale = new int[1]; preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale); - // Compare the size of the drag preview to the preview in the AppsCustomize tray - int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], - v.getActualItemWidth()); - scale = previewWidthInAppsCustomize / (float) preview.getWidth(); - - // The bitmap in the AppsCustomize tray is always the the same size, so there - // might be extra pixels around the preview itself - this accounts for that - if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) { - int padding = - (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2; - previewPadding = new Point(padding, 0); + + if (previewSizeBeforeScale[0] < icon.getWidth()) { + // The icon has extra padding around it. + int padding = (icon.getWidth() - previewSizeBeforeScale[0]) / 2; + if (icon.getWidth() > image.getWidth()) { + padding = padding * image.getWidth() / icon.getWidth(); + } + + bounds.left += padding; + bounds.right -= padding; } + scale = bounds.width() / (float) preview.getWidth(); } else { PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag(); Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo); @@ -274,16 +270,12 @@ public class WidgetsContainerView extends BaseContainerView boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo && (((PendingAddWidgetInfo) createItemInfo).previewImage == 0)); - // Save the preview for the outline generation, then dim the preview - outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(), - false); - // Start the drag mLauncher.lockScreenOrientation(); - mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha); + mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha); mDragController.startDrag(image, preview, this, createItemInfo, - DragController.DRAG_ACTION_COPY, previewPadding, scale); - outline.recycle(); + bounds, DragController.DRAG_ACTION_COPY, scale); + preview.recycle(); return true; } diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 397d17799..2f733dcbc 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -18,9 +18,9 @@ package com.android.launcher3.widget; import android.annotation.TargetApi; import android.content.Context; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.os.Build; import android.support.v7.widget.RecyclerView; -import android.content.res.Resources; import android.support.v7.widget.RecyclerView.Adapter; import android.util.Log; import android.view.LayoutInflater; @@ -29,15 +29,16 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.widget.LinearLayout; + import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DynamicGrid; -import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; + import java.util.List; /** @@ -56,7 +57,6 @@ public class WidgetsListAdapter extends Adapter { private Context mContext; private Launcher mLauncher; private LayoutInflater mLayoutInflater; - private IconCache mIconCache; private WidgetsModel mWidgetsModel; private WidgetPreviewLoader mWidgetPreviewLoader; @@ -76,9 +76,7 @@ public class WidgetsListAdapter extends Adapter { mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; - mLauncher = launcher; - mIconCache = LauncherAppState.getInstance().getIconCache(); setContainerHeight(); } @@ -143,7 +141,7 @@ public class WidgetsListAdapter extends Adapter { LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i); PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null); widget.setTag(pawi); - widget.applyFromAppWidgetProviderInfo(info, -1, mWidgetPreviewLoader); + widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader); } else if (infoList.get(i) instanceof ResolveInfo) { ResolveInfo info = (ResolveInfo) infoList.get(i); PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo); -- cgit v1.2.3 From 096e4d5355054cbc62d5cc33fdf445e54d3bf198 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 11:42:45 -0700 Subject: Updating the widget error view Change-Id: I3ed6336bd9f5a795204a87feedc83663355abcf2 --- res/drawable-hdpi/bg_appwidget_error.9.png | Bin 1285 -> 0 bytes res/drawable-mdpi/bg_appwidget_error.9.png | Bin 794 -> 0 bytes res/drawable-xhdpi/bg_appwidget_error.9.png | Bin 2387 -> 0 bytes res/layout/appwidget_error.xml | 13 +++++-------- res/values/colors.xml | 2 -- 5 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 res/drawable-hdpi/bg_appwidget_error.9.png delete mode 100644 res/drawable-mdpi/bg_appwidget_error.9.png delete mode 100644 res/drawable-xhdpi/bg_appwidget_error.9.png diff --git a/res/drawable-hdpi/bg_appwidget_error.9.png b/res/drawable-hdpi/bg_appwidget_error.9.png deleted file mode 100644 index 4da3195d4..000000000 Binary files a/res/drawable-hdpi/bg_appwidget_error.9.png and /dev/null differ diff --git a/res/drawable-mdpi/bg_appwidget_error.9.png b/res/drawable-mdpi/bg_appwidget_error.9.png deleted file mode 100644 index 493c0d454..000000000 Binary files a/res/drawable-mdpi/bg_appwidget_error.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/bg_appwidget_error.9.png b/res/drawable-xhdpi/bg_appwidget_error.9.png deleted file mode 100644 index b792cc847..000000000 Binary files a/res/drawable-xhdpi/bg_appwidget_error.9.png and /dev/null differ diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml index 03d4ae424..708ece4e2 100644 --- a/res/layout/appwidget_error.xml +++ b/res/layout/appwidget_error.xml @@ -15,15 +15,12 @@ --> diff --git a/res/values/colors.xml b/res/values/colors.xml index 0ba55f326..1e89615af 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -27,8 +27,6 @@ #80c6c5c5 - #FCCC - #FFF #FF666666 -- cgit v1.2.3 From 66056b8075bb816da7f6d9e90ff3d2a3cf05beea Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Sat, 16 May 2015 12:51:03 -0700 Subject: Enabling translation for accessibility strings > All the action strings are set to char limit 30 so that they play nice with accessibility UI. Bug: 19776741 Change-Id: I74612cc34a3a458541106319b3f8c36d54cc1138 --- res/values/strings.xml | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 51ad51f12..0fb5e3ad3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -182,76 +182,76 @@ - - Add to home screen + + Add to Home screen - + Move here - + Item added to home screen - + Item removed - - Move Item + + Move item - + Move to row %1$s column %2$s - + Move to position %1$s - + Move to favorites position %1$s - + Item moved - + Add to folder: %1$s - + Add to folder with %1$s - + Item added to folder - + Create folder with: %1$s - + Folder created - - Move to home screen + + Move to Home screen - + Move screen to left - + Move screen to right - + Screen moved - + Resize - + Increase width - + Increase height - + Decrease width - + Decrease height - + Widget resized to width %1$s height %2$s -- cgit v1.2.3 From 9ff980833d392244586ead8c27bbbba114e11ebb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 15 May 2015 16:59:36 -0700 Subject: Avoiding blocking worker thread when adding icons to DB Bug: 20945600 Change-Id: If12b21ac62c55c769838ce17d5421f147c396256 --- src/com/android/launcher3/IconCache.java | 70 ++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index fff07c6ed..1fac5a1c2 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -46,12 +46,14 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PackageItemInfo; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map.Entry; +import java.util.Stack; /** * Cache of application icons. Icons can be made from any thread. @@ -281,7 +283,7 @@ public class IconCache { itemsToRemove.add(c.getInt(rowIndex)); continue; } - ContentValues values = updateCacheAndGetContentValues(app); + ContentValues values = updateCacheAndGetContentValues(app, true); mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", new String[] {cn, Long.toString(userSerial)}); @@ -296,21 +298,19 @@ public class IconCache { } // Insert remaining apps. - for (LauncherActivityInfoCompat app : componentMap.values()) { - PackageInfo info = pkgInfoMap.get(app.getComponentName().getPackageName()); - if (info == null) { - continue; - } - addIconToDBAndMemCache(app, info, userSerial); + if (!componentMap.isEmpty()) { + mWorkerHandler.post(new SerializedIconAdditionTask(userSerial, pkgInfoMap, + componentMap.values())); } return updatedPackages; } private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, long userSerial) { - ContentValues values = updateCacheAndGetContentValues(app); + // Reuse the existing entry if it already exists in the DB. This ensures that we do not + // create bitmap if it was already created during loader. + ContentValues values = updateCacheAndGetContentValues(app, false); addIconToDB(values, app.getComponentName(), info, userSerial); - values.put(IconDB.COLUMN_COMPONENT, app.getComponentName().flattenToString()); } /** @@ -327,9 +327,21 @@ public class IconCache { SQLiteDatabase.CONFLICT_REPLACE); } - private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app) { - CacheEntry entry = new CacheEntry(); - entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext); + private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app, + boolean replaceExisting) { + final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser()); + CacheEntry entry = null; + if (!replaceExisting) { + entry = mCache.get(key); + // We can't reuse the entry if the high-res icon is not present. + if (entry == null || entry.isLowResIcon || entry.icon == null) { + entry = null; + } + } + if (entry == null) { + entry = new CacheEntry(); + entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext); + } entry.title = app.getLabel(); entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry); @@ -671,6 +683,40 @@ public class IconCache { } } + /** + * A runnable that adds icons in the DB for the provided LauncherActivityInfoCompat list. + * Items are added one at a time, to that the worker thread does not get blocked. + */ + private class SerializedIconAdditionTask implements Runnable { + private final long mUserSerial; + private final HashMap mPkgInfoMap; + private final Stack mAppsToAdd; + + private SerializedIconAdditionTask(long userSerial, HashMap pkgInfoMap, + Collection appsToAdd) { + mUserSerial = userSerial; + mPkgInfoMap = pkgInfoMap; + mAppsToAdd = new Stack(); + mAppsToAdd.addAll(appsToAdd); + } + + @Override + public void run() { + if (!mAppsToAdd.isEmpty()) { + LauncherActivityInfoCompat app = mAppsToAdd.pop(); + PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName()); + if (info != null) { + synchronized (IconCache.this) { + addIconToDBAndMemCache(app, info, mUserSerial); + } + } + } + if (!mAppsToAdd.isEmpty()) { + mWorkerHandler.post(this); + } + } + } + private static final class IconDB extends SQLiteOpenHelper { private final static int DB_VERSION = 4; -- cgit v1.2.3 From 83a8f042adda926489494dff217c15ab696139b4 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 12:52:12 -0700 Subject: Moving LauncherAccessibilityDelegate to accessibility package Change-Id: I510204a5a12abf2da2757f3e3f8b0e8869a6b04a --- src/com/android/launcher3/CellLayout.java | 4 +- src/com/android/launcher3/DragLayer.java | 1 + src/com/android/launcher3/Folder.java | 6 +- src/com/android/launcher3/Launcher.java | 5 +- .../launcher3/LauncherAccessibilityDelegate.java | 412 -------------------- src/com/android/launcher3/LauncherAppState.java | 1 + src/com/android/launcher3/LauncherModel.java | 4 +- src/com/android/launcher3/PagedView.java | 4 +- src/com/android/launcher3/SmoothPagedView.java | 2 +- src/com/android/launcher3/Workspace.java | 9 +- .../DragAndDropAccessibilityDelegate.java | 1 - .../LauncherAccessibilityDelegate.java | 430 +++++++++++++++++++++ .../WorkspaceAccessibilityHelper.java | 3 +- 13 files changed, 453 insertions(+), 429 deletions(-) delete mode 100644 src/com/android/launcher3/LauncherAccessibilityDelegate.java create mode 100644 src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index a348008d8..a6e11b683 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -3010,7 +3010,7 @@ public class CellLayout extends ViewGroup { // 2. When long clicking on an empty cell in a CellLayout, we save information about the // cellX and cellY coordinates and which page was clicked. We then set this as a tag on // the CellLayout that was long clicked - static final class CellInfo { + public static final class CellInfo { View cell; int cellX = -1; int cellY = -1; @@ -3019,7 +3019,7 @@ public class CellLayout extends ViewGroup { long screenId; long container; - CellInfo(View v, ItemInfo info) { + public CellInfo(View v, ItemInfo info) { cell = v; cellX = info.cellX; cellY = info.cellY; diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index e25e6152c..e8661e331 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -39,6 +39,7 @@ import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.TextView; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.util.Thunk; import java.util.ArrayList; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 69b18374a..d0a7ba3f8 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -49,13 +49,15 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; + import com.android.launcher3.CellLayout.CellInfo; import com.android.launcher3.DragController.DragListener; import com.android.launcher3.FolderInfo.FolderListener; -import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.util.Thunk; + import java.util.ArrayList; import java.util.Collections; @@ -352,7 +354,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList /** * @return the FolderInfo object associated with this folder */ - FolderInfo getInfo() { + public FolderInfo getInfo() { return mInfo; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 51ba2dfc9..7b7b61795 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -96,6 +96,7 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; @@ -3184,7 +3185,7 @@ public class Launcher extends Activity } } - void closeFolder(Folder folder) { + public void closeFolder(Folder folder) { folder.getInfo().opened = false; ViewGroup parent = (ViewGroup) folder.getParent().getParent(); @@ -3337,7 +3338,7 @@ public class Launcher extends Activity true); } - protected void showWorkspace(boolean animated, Runnable onCompleteRunnable) { + public void showWorkspace(boolean animated, Runnable onCompleteRunnable) { showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, onCompleteRunnable, true); } diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java deleted file mode 100644 index 3992e6390..000000000 --- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java +++ /dev/null @@ -1,412 +0,0 @@ -package com.android.launcher3; - -import android.annotation.TargetApi; -import android.app.AlertDialog; -import android.appwidget.AppWidgetProviderInfo; -import android.content.DialogInterface; -import android.graphics.Rect; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.util.Log; -import android.util.SparseArray; -import android.view.View; -import android.view.View.AccessibilityDelegate; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; - -import com.android.launcher3.util.Thunk; - -import java.util.ArrayList; - -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class LauncherAccessibilityDelegate extends AccessibilityDelegate { - - private static final String TAG = "LauncherAccessibilityDelegate"; - - private static final int REMOVE = R.id.action_remove; - private static final int INFO = R.id.action_info; - private static final int UNINSTALL = R.id.action_uninstall; - private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; - private static final int MOVE = R.id.action_move; - private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; - private static final int RESIZE = R.id.action_resize; - - public enum DragType { - ICON, - FOLDER, - WIDGET - } - - public static class DragInfo { - public DragType dragType; - public ItemInfo info; - public View item; - } - - private final SparseArray mActions = new SparseArray<>(); - @Thunk final Launcher mLauncher; - - private DragInfo mDragInfo = null; - private AccessibilityDragSource mDragSource = null; - - public LauncherAccessibilityDelegate(Launcher launcher) { - mLauncher = launcher; - - mActions.put(REMOVE, new AccessibilityAction(REMOVE, - launcher.getText(R.string.delete_target_label))); - mActions.put(INFO, new AccessibilityAction(INFO, - launcher.getText(R.string.info_target_label))); - mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL, - launcher.getText(R.string.delete_target_uninstall_label))); - mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE, - launcher.getText(R.string.action_add_to_workspace))); - mActions.put(MOVE, new AccessibilityAction(MOVE, - launcher.getText(R.string.action_move))); - mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE, - launcher.getText(R.string.action_move_to_workspace))); - mActions.put(RESIZE, new AccessibilityAction(RESIZE, - launcher.getText(R.string.action_resize))); - } - - @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(host, info); - if (!(host.getTag() instanceof ItemInfo)) return; - ItemInfo item = (ItemInfo) host.getTag(); - - if (DeleteDropTarget.supportsDrop(item)) { - info.addAction(mActions.get(REMOVE)); - } - if (UninstallDropTarget.supportsDrop(host.getContext(), item)) { - info.addAction(mActions.get(UNINSTALL)); - } - if (InfoDropTarget.supportsDrop(host.getContext(), item)) { - info.addAction(mActions.get(INFO)); - } - - if ((item instanceof ShortcutInfo) - || (item instanceof LauncherAppWidgetInfo) - || (item instanceof FolderInfo)) { - info.addAction(mActions.get(MOVE)); - - if (item.container >= 0) { - info.addAction(mActions.get(MOVE_TO_WORKSPACE)); - } else if (item instanceof LauncherAppWidgetInfo) { - if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) { - info.addAction(mActions.get(RESIZE)); - } - } - } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { - info.addAction(mActions.get(ADD_TO_WORKSPACE)); - } - } - - @Override - public boolean performAccessibilityAction(View host, int action, Bundle args) { - if ((host.getTag() instanceof ItemInfo) - && performAction(host, (ItemInfo) host.getTag(), action)) { - return true; - } - return super.performAccessibilityAction(host, action, args); - } - - public boolean performAction(final View host, final ItemInfo item, int action) { - if (action == REMOVE) { - if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) { - announceConfirmation(R.string.item_removed); - return true; - } - return false; - } else if (action == INFO) { - InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); - return true; - } else if (action == UNINSTALL) { - return UninstallDropTarget.startUninstallActivity(mLauncher, item); - } else if (action == MOVE) { - beginAccessibleDrag(host, item); - } else if (action == ADD_TO_WORKSPACE) { - final int[] coordinates = new int[2]; - final long screenId = findSpaceOnWorkspace(item, coordinates); - mLauncher.showWorkspace(true, new Runnable() { - - @Override - public void run() { - if (item instanceof AppInfo) { - ShortcutInfo info = ((AppInfo) item).makeShortcut(); - LauncherModel.addItemToDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, - screenId, coordinates[0], coordinates[1]); - - ArrayList itemList = new ArrayList<>(); - itemList.add(info); - mLauncher.bindItems(itemList, 0, itemList.size(), true); - } else if (item instanceof PendingAddItemInfo) { - PendingAddItemInfo info = (PendingAddItemInfo) item; - Workspace workspace = mLauncher.getWorkspace(); - workspace.snapToPage(workspace.getPageIndexForScreenId(screenId)); - mLauncher.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, - screenId, coordinates, info.spanX, info.spanY); - } - announceConfirmation(R.string.item_added_to_workspace); - } - }); - return true; - } else if (action == MOVE_TO_WORKSPACE) { - Folder folder = mLauncher.getWorkspace().getOpenFolder(); - mLauncher.closeFolder(folder); - ShortcutInfo info = (ShortcutInfo) item; - folder.getInfo().remove(info); - - final int[] coordinates = new int[2]; - final long screenId = findSpaceOnWorkspace(item, coordinates); - LauncherModel.moveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, - screenId, coordinates[0], coordinates[1]); - - // Bind the item in next frame so that if a new workspace page was created, - // it will get laid out. - new Handler().post(new Runnable() { - - @Override - public void run() { - ArrayList itemList = new ArrayList<>(); - itemList.add(item); - mLauncher.bindItems(itemList, 0, itemList.size(), true); - announceConfirmation(R.string.item_moved); - } - }); - } else if (action == RESIZE) { - final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item; - final ArrayList actions = getSupportedResizeActions(host, info); - CharSequence[] labels = new CharSequence[actions.size()]; - for (int i = 0; i < actions.size(); i++) { - labels[i] = mLauncher.getText(actions.get(i)); - } - - new AlertDialog.Builder(mLauncher) - .setTitle(R.string.action_resize) - .setItems(labels, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - performResizeAction(actions.get(which), host, info); - dialog.dismiss(); - } - }) - .show(); - } - return false; - } - - private ArrayList getSupportedResizeActions(View host, LauncherAppWidgetInfo info) { - AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo(); - ArrayList actions = new ArrayList<>(); - - CellLayout layout = (CellLayout) host.getParent().getParent(); - if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) { - if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) || - layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) { - actions.add(R.string.action_increase_width); - } - - if (info.spanX > info.minSpanX && info.spanX > 1) { - actions.add(R.string.action_decrease_width); - } - } - - if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { - if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) || - layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) { - actions.add(R.string.action_increase_height); - } - - if (info.spanY > info.minSpanY && info.spanY > 1) { - actions.add(R.string.action_decrease_height); - } - } - return actions; - } - - private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams(); - CellLayout layout = (CellLayout) host.getParent().getParent(); - layout.markCellsAsUnoccupiedForView(host); - - if (action == R.string.action_increase_width) { - if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) - && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) - || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) { - lp.cellX --; - info.cellX --; - } - lp.cellHSpan ++; - info.spanX ++; - } else if (action == R.string.action_decrease_width) { - lp.cellHSpan --; - info.spanX --; - } else if (action == R.string.action_increase_height) { - if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) { - lp.cellY --; - info.cellY --; - } - lp.cellVSpan ++; - info.spanY ++; - } else if (action == R.string.action_decrease_height) { - lp.cellVSpan --; - info.spanY --; - } - - layout.markCellsAsOccupiedForView(host); - Rect sizeRange = new Rect(); - AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange); - ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null, - sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom); - host.requestLayout(); - LauncherModel.updateItemInDatabase(mLauncher, info); - announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY)); - } - - @Thunk void announceConfirmation(int resId) { - announceConfirmation(mLauncher.getResources().getString(resId)); - } - - @Thunk void announceConfirmation(String confirmation) { - mLauncher.getDragLayer().announceForAccessibility(confirmation); - - } - - public boolean isInAccessibleDrag() { - return mDragInfo != null; - } - - public DragInfo getDragInfo() { - return mDragInfo; - } - - /** - * @param clickedTarget the actual view that was clicked - * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used - * as the actual drop location otherwise the views center is used. - */ - public void handleAccessibleDrop(View clickedTarget, Rect dropLocation, - String confirmation) { - if (!isInAccessibleDrag()) return; - - int[] loc = new int[2]; - if (dropLocation == null) { - loc[0] = clickedTarget.getWidth() / 2; - loc[1] = clickedTarget.getHeight() / 2; - } else { - loc[0] = dropLocation.centerX(); - loc[1] = dropLocation.centerY(); - } - - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc); - mLauncher.getDragController().completeAccessibleDrag(loc); - - endAccessibleDrag(); - if (!TextUtils.isEmpty(confirmation)) { - announceConfirmation(confirmation); - } - } - - public void beginAccessibleDrag(View item, ItemInfo info) { - mDragInfo = new DragInfo(); - mDragInfo.info = info; - mDragInfo.item = item; - mDragInfo.dragType = DragType.ICON; - if (info instanceof FolderInfo) { - mDragInfo.dragType = DragType.FOLDER; - } else if (info instanceof LauncherAppWidgetInfo) { - mDragInfo.dragType = DragType.WIDGET; - } - - CellLayout.CellInfo cellInfo = new CellLayout.CellInfo(item, info); - - Rect pos = new Rect(); - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos); - mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY()); - - Workspace workspace = mLauncher.getWorkspace(); - - Folder folder = workspace.getOpenFolder(); - if (folder != null) { - if (folder.getItemsInReadingOrder().contains(item)) { - mDragSource = folder; - } else { - mLauncher.closeFolder(); - } - } - if (mDragSource == null) { - mDragSource = workspace; - } - mDragSource.enableAccessibleDrag(true); - mDragSource.startDrag(cellInfo, true); - } - - public boolean onBackPressed() { - if (isInAccessibleDrag()) { - cancelAccessibleDrag(); - return true; - } - return false; - } - - private void cancelAccessibleDrag() { - mLauncher.getDragController().cancelDrag(); - endAccessibleDrag(); - } - - private void endAccessibleDrag() { - mDragInfo = null; - if (mDragSource != null) { - mDragSource.enableAccessibleDrag(false); - mDragSource = null; - } - } - - public static interface AccessibilityDragSource { - void startDrag(CellLayout.CellInfo cellInfo, boolean accessible); - - void enableAccessibleDrag(boolean enable); - } - - /** - * Find empty space on the workspace and returns the screenId. - */ - private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) { - Workspace workspace = mLauncher.getWorkspace(); - ArrayList workspaceScreens = workspace.getScreenOrder(); - long screenId; - - // First check if there is space on the current screen. - int screenIndex = workspace.getCurrentPage(); - screenId = workspaceScreens.get(screenIndex); - CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex); - - boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); - screenIndex = workspace.hasCustomContent() ? 1 : 0; - while (!found && screenIndex < workspaceScreens.size()) { - screenId = workspaceScreens.get(screenIndex); - layout = (CellLayout) workspace.getPageAt(screenIndex); - found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); - screenIndex++; - } - - if (found) { - return screenId; - } - - workspace.addExtraEmptyScreen(); - screenId = workspace.commitExtraEmptyScreen(); - layout = workspace.getScreenWithId(screenId); - found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); - - if (!found) { - Log.wtf(TAG, "Not enough space on an empty screen"); - } - return screenId; - } -} diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index bde54c335..f540eb47d 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -31,6 +31,7 @@ import android.util.Log; import android.view.Display; import android.view.WindowManager; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.util.Thunk; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 0b3049c2e..635c86320 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -793,7 +793,7 @@ public class LauncherModel extends BroadcastReceiver /** * Move an item in the DB to a new */ - static void moveItemInDatabase(Context context, final ItemInfo item, final long container, + public static void moveItemInDatabase(Context context, final ItemInfo item, final long container, final long screenId, final int cellX, final int cellY) { item.container = container; item.cellX = cellX; @@ -889,7 +889,7 @@ public class LauncherModel extends BroadcastReceiver /** * Update an item to the database in a specified container. */ - static void updateItemInDatabase(Context context, final ItemInfo item) { + public static void updateItemInDatabase(Context context, final ItemInfo item) { final ContentValues values = new ContentValues(); item.onAddToDatabase(context, values); updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase"); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 554a975e7..62f1bc9d7 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -407,7 +407,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** * Returns the index of the currently displayed page. */ - int getCurrentPage() { + public int getCurrentPage() { return mCurrentPage; } @@ -422,7 +422,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return getChildCount(); } - View getPageAt(int index) { + public View getPageAt(int index) { return getChildAt(index); } diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java index 4e331aa2c..0f9b23cda 100644 --- a/src/com/android/launcher3/SmoothPagedView.java +++ b/src/com/android/launcher3/SmoothPagedView.java @@ -152,7 +152,7 @@ public abstract class SmoothPagedView extends PagedView { } @Override - protected void snapToPage(int whichPage) { + public void snapToPage(int whichPage) { if (mScrollMode == X_LARGE_MODE) { super.snapToPage(whichPage); } else { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 58e5877a6..8c1c7d689 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -57,19 +57,22 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.TextView; + import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; -import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; + import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -494,7 +497,7 @@ public class Workspace extends SmoothPagedView /** * @return The open folder on the current screen, or null if there is none */ - Folder getOpenFolder() { + public Folder getOpenFolder() { DragLayer dragLayer = mLauncher.getDragLayer(); int count = dragLayer.getChildCount(); for (int i = 0; i < count; i++) { @@ -857,7 +860,7 @@ public class Workspace extends SmoothPagedView return -1; } - ArrayList getScreenOrder() { + public ArrayList getScreenOrder() { return mScreenOrder; } diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java index 0f1724155..78accf720 100644 --- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java @@ -26,7 +26,6 @@ import android.view.View.OnClickListener; import android.view.accessibility.AccessibilityEvent; import com.android.launcher3.CellLayout; -import com.android.launcher3.LauncherAccessibilityDelegate; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java new file mode 100644 index 000000000..eeec8c580 --- /dev/null +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -0,0 +1,430 @@ +package com.android.launcher3.accessibility; + +import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.appwidget.AppWidgetProviderInfo; +import android.content.DialogInterface; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseArray; +import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import com.android.launcher3.AppInfo; +import com.android.launcher3.AppWidgetResizeFrame; +import com.android.launcher3.CellLayout; +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.Folder; +import com.android.launcher3.FolderInfo; +import com.android.launcher3.InfoDropTarget; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppWidgetHostView; +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.PendingAddItemInfo; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.UninstallDropTarget; +import com.android.launcher3.Workspace; +import com.android.launcher3.util.Thunk; + +import java.util.ArrayList; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class LauncherAccessibilityDelegate extends AccessibilityDelegate { + + private static final String TAG = "LauncherAccessibilityDelegate"; + + private static final int REMOVE = R.id.action_remove; + private static final int INFO = R.id.action_info; + private static final int UNINSTALL = R.id.action_uninstall; + private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; + private static final int MOVE = R.id.action_move; + private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; + private static final int RESIZE = R.id.action_resize; + + public enum DragType { + ICON, + FOLDER, + WIDGET + } + + public static class DragInfo { + public DragType dragType; + public ItemInfo info; + public View item; + } + + private final SparseArray mActions = new SparseArray<>(); + @Thunk final Launcher mLauncher; + + private DragInfo mDragInfo = null; + private AccessibilityDragSource mDragSource = null; + + public LauncherAccessibilityDelegate(Launcher launcher) { + mLauncher = launcher; + + mActions.put(REMOVE, new AccessibilityAction(REMOVE, + launcher.getText(R.string.delete_target_label))); + mActions.put(INFO, new AccessibilityAction(INFO, + launcher.getText(R.string.info_target_label))); + mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL, + launcher.getText(R.string.delete_target_uninstall_label))); + mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE, + launcher.getText(R.string.action_add_to_workspace))); + mActions.put(MOVE, new AccessibilityAction(MOVE, + launcher.getText(R.string.action_move))); + mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE, + launcher.getText(R.string.action_move_to_workspace))); + mActions.put(RESIZE, new AccessibilityAction(RESIZE, + launcher.getText(R.string.action_resize))); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + if (!(host.getTag() instanceof ItemInfo)) return; + ItemInfo item = (ItemInfo) host.getTag(); + + if (DeleteDropTarget.supportsDrop(item)) { + info.addAction(mActions.get(REMOVE)); + } + if (UninstallDropTarget.supportsDrop(host.getContext(), item)) { + info.addAction(mActions.get(UNINSTALL)); + } + if (InfoDropTarget.supportsDrop(host.getContext(), item)) { + info.addAction(mActions.get(INFO)); + } + + if ((item instanceof ShortcutInfo) + || (item instanceof LauncherAppWidgetInfo) + || (item instanceof FolderInfo)) { + info.addAction(mActions.get(MOVE)); + + if (item.container >= 0) { + info.addAction(mActions.get(MOVE_TO_WORKSPACE)); + } else if (item instanceof LauncherAppWidgetInfo) { + if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) { + info.addAction(mActions.get(RESIZE)); + } + } + } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) { + info.addAction(mActions.get(ADD_TO_WORKSPACE)); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if ((host.getTag() instanceof ItemInfo) + && performAction(host, (ItemInfo) host.getTag(), action)) { + return true; + } + return super.performAccessibilityAction(host, action, args); + } + + public boolean performAction(final View host, final ItemInfo item, int action) { + if (action == REMOVE) { + if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) { + announceConfirmation(R.string.item_removed); + return true; + } + return false; + } else if (action == INFO) { + InfoDropTarget.startDetailsActivityForInfo(item, mLauncher); + return true; + } else if (action == UNINSTALL) { + return UninstallDropTarget.startUninstallActivity(mLauncher, item); + } else if (action == MOVE) { + beginAccessibleDrag(host, item); + } else if (action == ADD_TO_WORKSPACE) { + final int[] coordinates = new int[2]; + final long screenId = findSpaceOnWorkspace(item, coordinates); + mLauncher.showWorkspace(true, new Runnable() { + + @Override + public void run() { + if (item instanceof AppInfo) { + ShortcutInfo info = ((AppInfo) item).makeShortcut(); + LauncherModel.addItemToDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates[0], coordinates[1]); + + ArrayList itemList = new ArrayList<>(); + itemList.add(info); + mLauncher.bindItems(itemList, 0, itemList.size(), true); + } else if (item instanceof PendingAddItemInfo) { + PendingAddItemInfo info = (PendingAddItemInfo) item; + Workspace workspace = mLauncher.getWorkspace(); + workspace.snapToPage(workspace.getPageIndexForScreenId(screenId)); + mLauncher.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates, info.spanX, info.spanY); + } + announceConfirmation(R.string.item_added_to_workspace); + } + }); + return true; + } else if (action == MOVE_TO_WORKSPACE) { + Folder folder = mLauncher.getWorkspace().getOpenFolder(); + mLauncher.closeFolder(folder); + ShortcutInfo info = (ShortcutInfo) item; + folder.getInfo().remove(info); + + final int[] coordinates = new int[2]; + final long screenId = findSpaceOnWorkspace(item, coordinates); + LauncherModel.moveItemInDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates[0], coordinates[1]); + + // Bind the item in next frame so that if a new workspace page was created, + // it will get laid out. + new Handler().post(new Runnable() { + + @Override + public void run() { + ArrayList itemList = new ArrayList<>(); + itemList.add(item); + mLauncher.bindItems(itemList, 0, itemList.size(), true); + announceConfirmation(R.string.item_moved); + } + }); + } else if (action == RESIZE) { + final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item; + final ArrayList actions = getSupportedResizeActions(host, info); + CharSequence[] labels = new CharSequence[actions.size()]; + for (int i = 0; i < actions.size(); i++) { + labels[i] = mLauncher.getText(actions.get(i)); + } + + new AlertDialog.Builder(mLauncher) + .setTitle(R.string.action_resize) + .setItems(labels, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + performResizeAction(actions.get(which), host, info); + dialog.dismiss(); + } + }) + .show(); + } + return false; + } + + private ArrayList getSupportedResizeActions(View host, LauncherAppWidgetInfo info) { + AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo(); + ArrayList actions = new ArrayList<>(); + + CellLayout layout = (CellLayout) host.getParent().getParent(); + if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) { + if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) || + layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) { + actions.add(R.string.action_increase_width); + } + + if (info.spanX > info.minSpanX && info.spanX > 1) { + actions.add(R.string.action_decrease_width); + } + } + + if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { + if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) || + layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) { + actions.add(R.string.action_increase_height); + } + + if (info.spanY > info.minSpanY && info.spanY > 1) { + actions.add(R.string.action_decrease_height); + } + } + return actions; + } + + private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams(); + CellLayout layout = (CellLayout) host.getParent().getParent(); + layout.markCellsAsUnoccupiedForView(host); + + if (action == R.string.action_increase_width) { + if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) + && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) + || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) { + lp.cellX --; + info.cellX --; + } + lp.cellHSpan ++; + info.spanX ++; + } else if (action == R.string.action_decrease_width) { + lp.cellHSpan --; + info.spanX --; + } else if (action == R.string.action_increase_height) { + if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) { + lp.cellY --; + info.cellY --; + } + lp.cellVSpan ++; + info.spanY ++; + } else if (action == R.string.action_decrease_height) { + lp.cellVSpan --; + info.spanY --; + } + + layout.markCellsAsOccupiedForView(host); + Rect sizeRange = new Rect(); + AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange); + ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null, + sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom); + host.requestLayout(); + LauncherModel.updateItemInDatabase(mLauncher, info); + announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY)); + } + + @Thunk void announceConfirmation(int resId) { + announceConfirmation(mLauncher.getResources().getString(resId)); + } + + @Thunk void announceConfirmation(String confirmation) { + mLauncher.getDragLayer().announceForAccessibility(confirmation); + + } + + public boolean isInAccessibleDrag() { + return mDragInfo != null; + } + + public DragInfo getDragInfo() { + return mDragInfo; + } + + /** + * @param clickedTarget the actual view that was clicked + * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used + * as the actual drop location otherwise the views center is used. + */ + public void handleAccessibleDrop(View clickedTarget, Rect dropLocation, + String confirmation) { + if (!isInAccessibleDrag()) return; + + int[] loc = new int[2]; + if (dropLocation == null) { + loc[0] = clickedTarget.getWidth() / 2; + loc[1] = clickedTarget.getHeight() / 2; + } else { + loc[0] = dropLocation.centerX(); + loc[1] = dropLocation.centerY(); + } + + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc); + mLauncher.getDragController().completeAccessibleDrag(loc); + + endAccessibleDrag(); + if (!TextUtils.isEmpty(confirmation)) { + announceConfirmation(confirmation); + } + } + + public void beginAccessibleDrag(View item, ItemInfo info) { + mDragInfo = new DragInfo(); + mDragInfo.info = info; + mDragInfo.item = item; + mDragInfo.dragType = DragType.ICON; + if (info instanceof FolderInfo) { + mDragInfo.dragType = DragType.FOLDER; + } else if (info instanceof LauncherAppWidgetInfo) { + mDragInfo.dragType = DragType.WIDGET; + } + + CellLayout.CellInfo cellInfo = new CellLayout.CellInfo(item, info); + + Rect pos = new Rect(); + mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos); + mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY()); + + Workspace workspace = mLauncher.getWorkspace(); + + Folder folder = workspace.getOpenFolder(); + if (folder != null) { + if (folder.getItemsInReadingOrder().contains(item)) { + mDragSource = folder; + } else { + mLauncher.closeFolder(); + } + } + if (mDragSource == null) { + mDragSource = workspace; + } + mDragSource.enableAccessibleDrag(true); + mDragSource.startDrag(cellInfo, true); + } + + public boolean onBackPressed() { + if (isInAccessibleDrag()) { + cancelAccessibleDrag(); + return true; + } + return false; + } + + private void cancelAccessibleDrag() { + mLauncher.getDragController().cancelDrag(); + endAccessibleDrag(); + } + + private void endAccessibleDrag() { + mDragInfo = null; + if (mDragSource != null) { + mDragSource.enableAccessibleDrag(false); + mDragSource = null; + } + } + + public static interface AccessibilityDragSource { + void startDrag(CellLayout.CellInfo cellInfo, boolean accessible); + + void enableAccessibleDrag(boolean enable); + } + + /** + * Find empty space on the workspace and returns the screenId. + */ + private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) { + Workspace workspace = mLauncher.getWorkspace(); + ArrayList workspaceScreens = workspace.getScreenOrder(); + long screenId; + + // First check if there is space on the current screen. + int screenIndex = workspace.getCurrentPage(); + screenId = workspaceScreens.get(screenIndex); + CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex); + + boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); + screenIndex = workspace.hasCustomContent() ? 1 : 0; + while (!found && screenIndex < workspaceScreens.size()) { + screenId = workspaceScreens.get(screenIndex); + layout = (CellLayout) workspace.getPageAt(screenIndex); + found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); + screenIndex++; + } + + if (found) { + return screenId; + } + + workspace.addExtraEmptyScreen(); + screenId = workspace.commitExtraEmptyScreen(); + layout = workspace.getScreenWithId(screenId); + found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); + + if (!found) { + Log.wtf(TAG, "Not enough space on an empty screen"); + } + return screenId; + } +} diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java index 6f89d0eb0..80ddc13b7 100644 --- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java +++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java @@ -23,8 +23,7 @@ import com.android.launcher3.AppInfo; import com.android.launcher3.CellLayout; import com.android.launcher3.FolderInfo; import com.android.launcher3.ItemInfo; -import com.android.launcher3.LauncherAccessibilityDelegate; -import com.android.launcher3.LauncherAccessibilityDelegate.DragType; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; -- cgit v1.2.3 From 0b493c86c5d0803f11095b71396ffac0097d26ef Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 19 May 2015 13:39:44 -0700 Subject: Unify sorting between all apps and widget tray - selected locale names are shown before latin - case independent sorting - main app > enterprise app Future possible refactoring: - Move all the *ItemInfo data structures to model package - Rename the comparator based on NOT what data structure it supports but what functionality it supports (locale? case independent? main app > enterprise app?) b/21271658 b/20339403 Change-Id: I8a776467392e21d5014e85cd3f51931a3ef89724 --- .../android/launcher3/AlphabeticalAppsList.java | 91 +----------------- src/com/android/launcher3/LauncherModel.java | 34 ------- .../android/launcher3/model/AppNameComparator.java | 105 +++++++++++++++++++++ .../model/WidgetsAndShortcutNameComparator.java | 63 +++++++++++++ src/com/android/launcher3/widget/WidgetsModel.java | 18 ++-- 5 files changed, 175 insertions(+), 136 deletions(-) create mode 100644 src/com/android/launcher3/model/AppNameComparator.java create mode 100644 src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index eff7b0625..82aaeb99a 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -7,6 +7,7 @@ import android.util.Log; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.AppNameComparator; import java.text.Collator; import java.util.ArrayList; @@ -18,96 +19,6 @@ import java.util.Locale; import java.util.Map; import java.util.TreeMap; - -/** - * A private class to manage access to an app name comparator. - */ -class AppNameComparator { - private final UserManagerCompat mUserManager; - private final Collator mCollator; - private final Comparator mAppInfoComparator; - private final Comparator mSectionNameComparator; - private HashMap mUserSerialCache = new HashMap<>(); - - public AppNameComparator(Context context) { - mCollator = Collator.getInstance(); - mUserManager = UserManagerCompat.getInstance(context); - mAppInfoComparator = new Comparator() { - public final int compare(AppInfo a, AppInfo b) { - // Order by the title in the current locale - int result = compareTitles(a.title.toString(), b.title.toString()); - if (result == 0) { - // If two apps have the same title, then order by the component name - result = a.componentName.compareTo(b.componentName); - if (result == 0) { - // If the two apps are the same component, then prioritize by the order that - // the app user was created (prioritizing the main user's apps) - if (UserHandleCompat.myUserHandle().equals(a.user)) { - return -1; - } else { - Long aUserSerial = getAndCacheUserSerial(a.user); - Long bUserSerial = getAndCacheUserSerial(b.user); - return aUserSerial.compareTo(bUserSerial); - } - } - } - return result; - } - }; - mSectionNameComparator = new Comparator() { - @Override - public int compare(String o1, String o2) { - return compareTitles(o1, o2); - } - }; - } - - /** - * Returns a locale-aware comparator that will alphabetically order a list of applications. - */ - public Comparator getAppInfoComparator() { - // Clear the user serial cache so that we get serials as needed in the comparator - mUserSerialCache.clear(); - return mAppInfoComparator; - } - - /** - * Returns a locale-aware comparator that will alphabetically order a list of section names. - */ - public Comparator getSectionNameComparator() { - return mSectionNameComparator; - } - - /** - * Compares two titles with the same return value semantics as Comparator. - */ - private int compareTitles(String titleA, String titleB) { - // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit - boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0)); - boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0)); - if (aStartsWithLetter && !bStartsWithLetter) { - return -1; - } else if (!aStartsWithLetter && bStartsWithLetter) { - return 1; - } - - // Order by the title in the current locale - return mCollator.compare(titleA, titleB); - } - - /** - * Returns the user serial for this user, using a cached serial if possible. - */ - private Long getAndCacheUserSerial(UserHandleCompat user) { - Long userSerial = mUserSerialCache.get(user); - if (userSerial == null) { - userSerial = mUserManager.getSerialNumberForUser(user); - mUserSerialCache.put(user, userSerial); - } - return userSerial; - } -} - /** * The alphabetically sorted list of applications. */ diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 0b3049c2e..58e899aec 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -66,7 +66,6 @@ import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; import java.net.URISyntaxException; import java.security.InvalidParameterException; -import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -3665,39 +3664,6 @@ public class LauncherModel extends BroadcastReceiver return folderInfo; } - public static class WidgetAndShortcutNameComparator implements Comparator { - private final AppWidgetManagerCompat mManager; - private final PackageManager mPackageManager; - private final HashMap mLabelCache; - private final Collator mCollator; - - public WidgetAndShortcutNameComparator(Context context) { - mManager = AppWidgetManagerCompat.getInstance(context); - mPackageManager = context.getPackageManager(); - mLabelCache = new HashMap(); - mCollator = Collator.getInstance(); - } - public final int compare(Object a, Object b) { - String labelA, labelB; - if (mLabelCache.containsKey(a)) { - labelA = mLabelCache.get(a); - } else { - labelA = (a instanceof LauncherAppWidgetProviderInfo) - ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a)) - : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager)); - mLabelCache.put(a, labelA); - } - if (mLabelCache.containsKey(b)) { - labelB = mLabelCache.get(b); - } else { - labelB = (b instanceof LauncherAppWidgetProviderInfo) - ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b)) - : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager)); - mLabelCache.put(b, labelB); - } - return mCollator.compare(labelA, labelB); - } - }; static boolean isValidProvider(AppWidgetProviderInfo provider) { return (provider != null) && (provider.provider != null) diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java new file mode 100644 index 000000000..706f7515d --- /dev/null +++ b/src/com/android/launcher3/model/AppNameComparator.java @@ -0,0 +1,105 @@ +package com.android.launcher3.model; + +import android.content.Context; + +import com.android.launcher3.AppInfo; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; +import java.text.Collator; +import java.util.Comparator; +import java.util.HashMap; + +/** + * Class to manage access to an app name comparator. + *

+ * Used to sort application name in all apps view and widget tray view. + */ +public class AppNameComparator { + private final UserManagerCompat mUserManager; + private final Collator mCollator; + private final Comparator mAppInfoComparator; + private final Comparator mSectionNameComparator; + private HashMap mUserSerialCache = new HashMap<>(); + + public AppNameComparator(Context context) { + mCollator = Collator.getInstance(); + mUserManager = UserManagerCompat.getInstance(context); + mAppInfoComparator = new Comparator() { + + public final int compare(ItemInfo a, ItemInfo b) { + // Order by the title in the current locale + int result = compareTitles(a.title.toString(), b.title.toString()); + if (result == 0 && a instanceof AppInfo && b instanceof AppInfo) { + AppInfo aAppInfo = (AppInfo) a; + AppInfo bAppInfo = (AppInfo) b; + // If two apps have the same title, then order by the component name + result = aAppInfo.componentName.compareTo(bAppInfo.componentName); + if (result == 0) { + // If the two apps are the same component, then prioritize by the order that + // the app user was created (prioritizing the main user's apps) + if (UserHandleCompat.myUserHandle().equals(a.user)) { + return -1; + } else { + Long aUserSerial = getAndCacheUserSerial(a.user); + Long bUserSerial = getAndCacheUserSerial(b.user); + return aUserSerial.compareTo(bUserSerial); + } + } + } + return result; + } + }; + mSectionNameComparator = new Comparator() { + @Override + public int compare(String o1, String o2) { + return compareTitles(o1, o2); + } + }; + } + + /** + * Returns a locale-aware comparator that will alphabetically order a list of applications. + */ + public Comparator getAppInfoComparator() { + // Clear the user serial cache so that we get serials as needed in the comparator + mUserSerialCache.clear(); + return mAppInfoComparator; + } + + /** + * Returns a locale-aware comparator that will alphabetically order a list of section names. + */ + public Comparator getSectionNameComparator() { + return mSectionNameComparator; + } + + /** + * Compares two titles with the same return value semantics as Comparator. + */ + private int compareTitles(String titleA, String titleB) { + // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit + boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0)); + boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0)); + if (aStartsWithLetter && !bStartsWithLetter) { + return -1; + } else if (!aStartsWithLetter && bStartsWithLetter) { + return 1; + } + + // Order by the title in the current locale + return mCollator.compare(titleA, titleB); + } + + /** + * Returns the user serial for this user, using a cached serial if possible. + */ + private Long getAndCacheUserSerial(UserHandleCompat user) { + Long userSerial = mUserSerialCache.get(user); + if (userSerial == null) { + userSerial = mUserManager.getSerialNumberForUser(user); + mUserSerialCache.put(user, userSerial); + } + return userSerial; + } +} diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java new file mode 100644 index 000000000..7c4e80651 --- /dev/null +++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java @@ -0,0 +1,63 @@ +package com.android.launcher3.model; + +import android.appwidget.AppWidgetProviderInfo; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; + +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AppWidgetManagerCompat; + +import java.text.Collator; +import java.util.Comparator; +import java.util.HashMap; + +public class WidgetsAndShortcutNameComparator implements Comparator { + private final AppWidgetManagerCompat mManager; + private final PackageManager mPackageManager; + private final HashMap mLabelCache; + private final Collator mCollator; + + public WidgetsAndShortcutNameComparator(Context context) { + mManager = AppWidgetManagerCompat.getInstance(context); + mPackageManager = context.getPackageManager(); + mLabelCache = new HashMap(); + mCollator = Collator.getInstance(); + } + + @Override + public final int compare(Object a, Object b) { + String labelA, labelB; + if (mLabelCache.containsKey(a)) { + labelA = mLabelCache.get(a); + } else { + labelA = (a instanceof LauncherAppWidgetProviderInfo) + ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a)) + : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager)); + mLabelCache.put(a, labelA); + } + if (mLabelCache.containsKey(b)) { + labelB = mLabelCache.get(b); + } else { + labelB = (b instanceof LauncherAppWidgetProviderInfo) + ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b)) + : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager)); + mLabelCache.put(b, labelB); + } + int result = mCollator.compare(labelA, labelB); + if (result == 0 && a instanceof AppWidgetProviderInfo && + b instanceof AppWidgetProviderInfo) { + AppWidgetProviderInfo aInfo = (AppWidgetProviderInfo) a; + AppWidgetProviderInfo bInfo = (AppWidgetProviderInfo) b; + + // prioritize main user's widgets against work profile widgets. + if (aInfo.getProfile().equals(android.os.Process.myUserHandle())) { + return -1; + } else if (bInfo.getProfile().equals(android.os.Process.myUserHandle())) { + return 1; + } + } + return result; + } +}; diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java index 71a7b9446..5a920e8d4 100644 --- a/src/com/android/launcher3/widget/WidgetsModel.java +++ b/src/com/android/launcher3/widget/WidgetsModel.java @@ -10,8 +10,9 @@ import android.util.Log; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.model.AppNameComparator; +import com.android.launcher3.model.WidgetsAndShortcutNameComparator; import java.util.ArrayList; import java.util.Collections; @@ -40,12 +41,14 @@ public class WidgetsModel { private RecyclerView.Adapter mAdapter; private Comparator mWidgetAndShortcutNameComparator; + private Comparator mAppNameComparator; private IconCache mIconCache; public WidgetsModel(Context context, RecyclerView.Adapter adapter) { mAdapter = adapter; - mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context); + mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); + mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); mIconCache = LauncherAppState.getInstance().getIconCache(); } @@ -108,7 +111,7 @@ public class WidgetsModel { } // sort. - sortPackageItemInfos(); + Collections.sort(mPackageItemInfos, mAppNameComparator); for (PackageItemInfo p: mPackageItemInfos) { Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator); } @@ -116,13 +119,4 @@ public class WidgetsModel { // notify. mAdapter.notifyDataSetChanged(); } - - private void sortPackageItemInfos() { - Collections.sort(mPackageItemInfos, new Comparator() { - @Override - public int compare(PackageItemInfo lhs, PackageItemInfo rhs) { - return lhs.title.toString().compareTo(rhs.title.toString()); - } - }); - } } \ No newline at end of file -- cgit v1.2.3 From cd4f4138f05be3a1daab5035c59e97a1a82431ac Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 19 May 2015 14:19:37 -0700 Subject: Removing itemCount, fromIndex and toIndex from AccessibilityEvent.TYPE_VIEW_SCROLLED sent by PagedView. This causes an additional reduntant voice message on scroll (see the bug). Also, setting these attributes violate rules set here: http://developer.android.com/reference/android/view/accessibility/AccessibilityEvent.html i.e. that these fields should be set only for descendants of AdapterView. Note that we can't just stop sending TYPE_VIEW_SCROLLED, because in this case, accessibility focus won't be set after scrolling. Bug: 21304383 Change-Id: I84f8e064d8209c0e09d6827551e00c9913829b57 --- src/com/android/launcher3/PagedView.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index c8e7d9c0b..e6dc59cfa 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -639,9 +639,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (mCurrentPage != getNextPage()) { AccessibilityEvent ev = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); - ev.setItemCount(getChildCount()); - ev.setFromIndex(getNextPage()); - ev.setToIndex(getNextPage()); sendAccessibilityEventUnchecked(ev); } -- cgit v1.2.3 From 7a89eec7e09ac23d50475ff311cd52019c0797cd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 14:48:52 -0700 Subject: Fixing accessibility string Bug: 19776741 Change-Id: Ie020911bd17aa102eddfc69d6b3db5c19db9c6ac --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 0fb5e3ad3..18f97c84d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -186,7 +186,7 @@ Add to Home screen - Move here + Move item here Item added to home screen -- cgit v1.2.3 From ba776d57f4cd7d08644a60fe88e9f6e91ba44849 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 18 May 2015 20:52:57 -0700 Subject: Updating widget resize frame assets. > Using a shadow as background and a frame as foreground. > The handle has baked in shadow which gets wrapped by the frame shadow Change-Id: Ib0e305ea735304236b3319e4fde22fb442f5dc0f --- res/drawable-hdpi/ic_widget_resize_handle.png | Bin 0 -> 1492 bytes res/drawable-hdpi/widget_resize_frame.9.png | Bin 0 -> 522 bytes res/drawable-hdpi/widget_resize_frame_holo.9.png | Bin 880 -> 0 bytes res/drawable-hdpi/widget_resize_handle_bottom.png | Bin 1080 -> 0 bytes res/drawable-hdpi/widget_resize_handle_left.png | Bin 1097 -> 0 bytes res/drawable-hdpi/widget_resize_handle_right.png | Bin 1085 -> 0 bytes res/drawable-hdpi/widget_resize_handle_top.png | Bin 1085 -> 0 bytes res/drawable-hdpi/widget_resize_shadow.9.png | Bin 0 -> 681 bytes res/drawable-mdpi/ic_widget_resize_handle.png | Bin 0 -> 896 bytes res/drawable-mdpi/widget_resize_frame.9.png | Bin 0 -> 444 bytes res/drawable-mdpi/widget_resize_frame_holo.9.png | Bin 619 -> 0 bytes res/drawable-mdpi/widget_resize_handle_bottom.png | Bin 745 -> 0 bytes res/drawable-mdpi/widget_resize_handle_left.png | Bin 762 -> 0 bytes res/drawable-mdpi/widget_resize_handle_right.png | Bin 772 -> 0 bytes res/drawable-mdpi/widget_resize_handle_top.png | Bin 750 -> 0 bytes res/drawable-mdpi/widget_resize_shadow.9.png | Bin 0 -> 486 bytes res/drawable-xhdpi/ic_widget_resize_handle.png | Bin 0 -> 2144 bytes res/drawable-xhdpi/widget_resize_frame.9.png | Bin 0 -> 670 bytes res/drawable-xhdpi/widget_resize_frame_holo.9.png | Bin 1323 -> 0 bytes res/drawable-xhdpi/widget_resize_handle_bottom.png | Bin 1434 -> 0 bytes res/drawable-xhdpi/widget_resize_handle_left.png | Bin 1467 -> 0 bytes res/drawable-xhdpi/widget_resize_handle_right.png | Bin 1483 -> 0 bytes res/drawable-xhdpi/widget_resize_handle_top.png | Bin 1463 -> 0 bytes res/drawable-xhdpi/widget_resize_shadow.9.png | Bin 0 -> 918 bytes res/drawable-xxhdpi/ic_widget_resize_handle.png | Bin 0 -> 2928 bytes res/drawable-xxhdpi/widget_resize_frame.9.png | Bin 0 -> 953 bytes res/drawable-xxhdpi/widget_resize_frame_holo.9.png | Bin 4396 -> 0 bytes .../widget_resize_handle_bottom.png | Bin 4470 -> 0 bytes res/drawable-xxhdpi/widget_resize_handle_left.png | Bin 4428 -> 0 bytes res/drawable-xxhdpi/widget_resize_handle_right.png | Bin 4438 -> 0 bytes res/drawable-xxhdpi/widget_resize_handle_top.png | Bin 4473 -> 0 bytes res/drawable-xxhdpi/widget_resize_shadow.9.png | Bin 0 -> 1320 bytes res/drawable-xxxhdpi/ic_widget_resize_handle.png | Bin 0 -> 2930 bytes res/drawable-xxxhdpi/widget_resize_frame.9.png | Bin 0 -> 2052 bytes res/drawable-xxxhdpi/widget_resize_shadow.9.png | Bin 0 -> 2512 bytes res/values/dimens.xml | 3 + .../android/launcher3/AppWidgetResizeFrame.java | 115 ++++++++++----------- 37 files changed, 56 insertions(+), 62 deletions(-) create mode 100644 res/drawable-hdpi/ic_widget_resize_handle.png create mode 100644 res/drawable-hdpi/widget_resize_frame.9.png delete mode 100644 res/drawable-hdpi/widget_resize_frame_holo.9.png delete mode 100644 res/drawable-hdpi/widget_resize_handle_bottom.png delete mode 100644 res/drawable-hdpi/widget_resize_handle_left.png delete mode 100644 res/drawable-hdpi/widget_resize_handle_right.png delete mode 100644 res/drawable-hdpi/widget_resize_handle_top.png create mode 100644 res/drawable-hdpi/widget_resize_shadow.9.png create mode 100644 res/drawable-mdpi/ic_widget_resize_handle.png create mode 100644 res/drawable-mdpi/widget_resize_frame.9.png delete mode 100644 res/drawable-mdpi/widget_resize_frame_holo.9.png delete mode 100644 res/drawable-mdpi/widget_resize_handle_bottom.png delete mode 100644 res/drawable-mdpi/widget_resize_handle_left.png delete mode 100644 res/drawable-mdpi/widget_resize_handle_right.png delete mode 100644 res/drawable-mdpi/widget_resize_handle_top.png create mode 100644 res/drawable-mdpi/widget_resize_shadow.9.png create mode 100644 res/drawable-xhdpi/ic_widget_resize_handle.png create mode 100644 res/drawable-xhdpi/widget_resize_frame.9.png delete mode 100644 res/drawable-xhdpi/widget_resize_frame_holo.9.png delete mode 100644 res/drawable-xhdpi/widget_resize_handle_bottom.png delete mode 100644 res/drawable-xhdpi/widget_resize_handle_left.png delete mode 100644 res/drawable-xhdpi/widget_resize_handle_right.png delete mode 100644 res/drawable-xhdpi/widget_resize_handle_top.png create mode 100644 res/drawable-xhdpi/widget_resize_shadow.9.png create mode 100644 res/drawable-xxhdpi/ic_widget_resize_handle.png create mode 100644 res/drawable-xxhdpi/widget_resize_frame.9.png delete mode 100644 res/drawable-xxhdpi/widget_resize_frame_holo.9.png delete mode 100644 res/drawable-xxhdpi/widget_resize_handle_bottom.png delete mode 100644 res/drawable-xxhdpi/widget_resize_handle_left.png delete mode 100644 res/drawable-xxhdpi/widget_resize_handle_right.png delete mode 100644 res/drawable-xxhdpi/widget_resize_handle_top.png create mode 100644 res/drawable-xxhdpi/widget_resize_shadow.9.png create mode 100644 res/drawable-xxxhdpi/ic_widget_resize_handle.png create mode 100644 res/drawable-xxxhdpi/widget_resize_frame.9.png create mode 100644 res/drawable-xxxhdpi/widget_resize_shadow.9.png diff --git a/res/drawable-hdpi/ic_widget_resize_handle.png b/res/drawable-hdpi/ic_widget_resize_handle.png new file mode 100644 index 000000000..844f3cf09 Binary files /dev/null and b/res/drawable-hdpi/ic_widget_resize_handle.png differ diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png new file mode 100644 index 000000000..5772672b1 Binary files /dev/null and b/res/drawable-hdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-hdpi/widget_resize_frame_holo.9.png b/res/drawable-hdpi/widget_resize_frame_holo.9.png deleted file mode 100644 index 2d6fcf51f..000000000 Binary files a/res/drawable-hdpi/widget_resize_frame_holo.9.png and /dev/null differ diff --git a/res/drawable-hdpi/widget_resize_handle_bottom.png b/res/drawable-hdpi/widget_resize_handle_bottom.png deleted file mode 100644 index f0afd6172..000000000 Binary files a/res/drawable-hdpi/widget_resize_handle_bottom.png and /dev/null differ diff --git a/res/drawable-hdpi/widget_resize_handle_left.png b/res/drawable-hdpi/widget_resize_handle_left.png deleted file mode 100644 index 47613b29f..000000000 Binary files a/res/drawable-hdpi/widget_resize_handle_left.png and /dev/null differ diff --git a/res/drawable-hdpi/widget_resize_handle_right.png b/res/drawable-hdpi/widget_resize_handle_right.png deleted file mode 100644 index acc28be83..000000000 Binary files a/res/drawable-hdpi/widget_resize_handle_right.png and /dev/null differ diff --git a/res/drawable-hdpi/widget_resize_handle_top.png b/res/drawable-hdpi/widget_resize_handle_top.png deleted file mode 100644 index 2c60be00c..000000000 Binary files a/res/drawable-hdpi/widget_resize_handle_top.png and /dev/null differ diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png new file mode 100644 index 000000000..a67da6e04 Binary files /dev/null and b/res/drawable-hdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-mdpi/ic_widget_resize_handle.png b/res/drawable-mdpi/ic_widget_resize_handle.png new file mode 100644 index 000000000..c3b287ff0 Binary files /dev/null and b/res/drawable-mdpi/ic_widget_resize_handle.png differ diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png new file mode 100644 index 000000000..8bc8a5c12 Binary files /dev/null and b/res/drawable-mdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-mdpi/widget_resize_frame_holo.9.png b/res/drawable-mdpi/widget_resize_frame_holo.9.png deleted file mode 100644 index 028bd6248..000000000 Binary files a/res/drawable-mdpi/widget_resize_frame_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/widget_resize_handle_bottom.png b/res/drawable-mdpi/widget_resize_handle_bottom.png deleted file mode 100644 index c838bf405..000000000 Binary files a/res/drawable-mdpi/widget_resize_handle_bottom.png and /dev/null differ diff --git a/res/drawable-mdpi/widget_resize_handle_left.png b/res/drawable-mdpi/widget_resize_handle_left.png deleted file mode 100644 index ff0b0d357..000000000 Binary files a/res/drawable-mdpi/widget_resize_handle_left.png and /dev/null differ diff --git a/res/drawable-mdpi/widget_resize_handle_right.png b/res/drawable-mdpi/widget_resize_handle_right.png deleted file mode 100644 index fc4808e3a..000000000 Binary files a/res/drawable-mdpi/widget_resize_handle_right.png and /dev/null differ diff --git a/res/drawable-mdpi/widget_resize_handle_top.png b/res/drawable-mdpi/widget_resize_handle_top.png deleted file mode 100644 index 3b1df0170..000000000 Binary files a/res/drawable-mdpi/widget_resize_handle_top.png and /dev/null differ diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png new file mode 100644 index 000000000..2bae2b603 Binary files /dev/null and b/res/drawable-mdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-xhdpi/ic_widget_resize_handle.png b/res/drawable-xhdpi/ic_widget_resize_handle.png new file mode 100644 index 000000000..f445a1c08 Binary files /dev/null and b/res/drawable-xhdpi/ic_widget_resize_handle.png differ diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png new file mode 100644 index 000000000..e6cf0afde Binary files /dev/null and b/res/drawable-xhdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-xhdpi/widget_resize_frame_holo.9.png b/res/drawable-xhdpi/widget_resize_frame_holo.9.png deleted file mode 100644 index 76cec606d..000000000 Binary files a/res/drawable-xhdpi/widget_resize_frame_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/widget_resize_handle_bottom.png b/res/drawable-xhdpi/widget_resize_handle_bottom.png deleted file mode 100644 index 19437d7dc..000000000 Binary files a/res/drawable-xhdpi/widget_resize_handle_bottom.png and /dev/null differ diff --git a/res/drawable-xhdpi/widget_resize_handle_left.png b/res/drawable-xhdpi/widget_resize_handle_left.png deleted file mode 100644 index 28c5487ec..000000000 Binary files a/res/drawable-xhdpi/widget_resize_handle_left.png and /dev/null differ diff --git a/res/drawable-xhdpi/widget_resize_handle_right.png b/res/drawable-xhdpi/widget_resize_handle_right.png deleted file mode 100644 index 4f672a602..000000000 Binary files a/res/drawable-xhdpi/widget_resize_handle_right.png and /dev/null differ diff --git a/res/drawable-xhdpi/widget_resize_handle_top.png b/res/drawable-xhdpi/widget_resize_handle_top.png deleted file mode 100644 index e866c008c..000000000 Binary files a/res/drawable-xhdpi/widget_resize_handle_top.png and /dev/null differ diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png new file mode 100644 index 000000000..99e9e78cd Binary files /dev/null and b/res/drawable-xhdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-xxhdpi/ic_widget_resize_handle.png b/res/drawable-xxhdpi/ic_widget_resize_handle.png new file mode 100644 index 000000000..144cac996 Binary files /dev/null and b/res/drawable-xxhdpi/ic_widget_resize_handle.png differ diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png new file mode 100644 index 000000000..7a49d01da Binary files /dev/null and b/res/drawable-xxhdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-xxhdpi/widget_resize_frame_holo.9.png b/res/drawable-xxhdpi/widget_resize_frame_holo.9.png deleted file mode 100644 index 1681387a1..000000000 Binary files a/res/drawable-xxhdpi/widget_resize_frame_holo.9.png and /dev/null differ diff --git a/res/drawable-xxhdpi/widget_resize_handle_bottom.png b/res/drawable-xxhdpi/widget_resize_handle_bottom.png deleted file mode 100644 index d549fcd91..000000000 Binary files a/res/drawable-xxhdpi/widget_resize_handle_bottom.png and /dev/null differ diff --git a/res/drawable-xxhdpi/widget_resize_handle_left.png b/res/drawable-xxhdpi/widget_resize_handle_left.png deleted file mode 100644 index dd56dad15..000000000 Binary files a/res/drawable-xxhdpi/widget_resize_handle_left.png and /dev/null differ diff --git a/res/drawable-xxhdpi/widget_resize_handle_right.png b/res/drawable-xxhdpi/widget_resize_handle_right.png deleted file mode 100644 index 296a1c166..000000000 Binary files a/res/drawable-xxhdpi/widget_resize_handle_right.png and /dev/null differ diff --git a/res/drawable-xxhdpi/widget_resize_handle_top.png b/res/drawable-xxhdpi/widget_resize_handle_top.png deleted file mode 100644 index e86270ad1..000000000 Binary files a/res/drawable-xxhdpi/widget_resize_handle_top.png and /dev/null differ diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png new file mode 100644 index 000000000..ae0f564dd Binary files /dev/null and b/res/drawable-xxhdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-xxxhdpi/ic_widget_resize_handle.png b/res/drawable-xxxhdpi/ic_widget_resize_handle.png new file mode 100644 index 000000000..4bde6b9a6 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_widget_resize_handle.png differ diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png new file mode 100644 index 000000000..9b711f2c5 Binary files /dev/null and b/res/drawable-xxxhdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png new file mode 100644 index 000000000..defb311ca Binary files /dev/null and b/res/drawable-xxxhdpi/widget_resize_shadow.9.png differ diff --git a/res/values/dimens.xml b/res/values/dimens.xml index d2f6237a8..60591e14a 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -28,7 +28,10 @@ 80dp 20dp + 8dp + 13dp + 24dp 240dp diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 3c698c014..e6bf52531 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -15,24 +15,36 @@ import android.widget.FrameLayout; import android.widget.ImageView; public class AppWidgetResizeFrame extends FrameLayout { - private LauncherAppWidgetHostView mWidgetView; - private CellLayout mCellLayout; - private DragLayer mDragLayer; - private ImageView mLeftHandle; - private ImageView mRightHandle; - private ImageView mTopHandle; - private ImageView mBottomHandle; + private static final int SNAP_DURATION = 150; + private static final float DIMMED_HANDLE_ALPHA = 0f; + private static final float RESIZE_THRESHOLD = 0.66f; + + private static Rect sTmpRect = new Rect(); + + private final Launcher mLauncher; + private final LauncherAppWidgetHostView mWidgetView; + private final CellLayout mCellLayout; + private final DragLayer mDragLayer; + + private final ImageView mLeftHandle; + private final ImageView mRightHandle; + private final ImageView mTopHandle; + private final ImageView mBottomHandle; + + private final Rect mWidgetPadding; + + private final int mBackgroundPadding; + private final int mTouchTargetWidth; + + private final int[] mDirectionVector = new int[2]; + private final int[] mLastDirectionVector = new int[2]; + private final int[] mTmpPt = new int[2]; private boolean mLeftBorderActive; private boolean mRightBorderActive; private boolean mTopBorderActive; private boolean mBottomBorderActive; - private int mWidgetPaddingLeft; - private int mWidgetPaddingRight; - private int mWidgetPaddingTop; - private int mWidgetPaddingBottom; - private int mBaselineWidth; private int mBaselineHeight; private int mBaselineX; @@ -48,30 +60,9 @@ public class AppWidgetResizeFrame extends FrameLayout { private int mDeltaXAddOn; private int mDeltaYAddOn; - private int mBackgroundPadding; - private int mTouchTargetWidth; - private int mTopTouchRegionAdjustment = 0; private int mBottomTouchRegionAdjustment = 0; - int[] mDirectionVector = new int[2]; - int[] mLastDirectionVector = new int[2]; - int[] mTmpPt = new int[2]; - - final int SNAP_DURATION = 150; - final int BACKGROUND_PADDING = 24; - final float DIMMED_HANDLE_ALPHA = 0f; - final float RESIZE_THRESHOLD = 0.66f; - - private static Rect mTmpRect = new Rect(); - - public static final int LEFT = 0; - public static final int TOP = 1; - public static final int RIGHT = 2; - public static final int BOTTOM = 3; - - private Launcher mLauncher; - public AppWidgetResizeFrame(Context context, LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) { @@ -87,49 +78,49 @@ public class AppWidgetResizeFrame extends FrameLayout { mMinHSpan = info.minSpanX; mMinVSpan = info.minSpanY; - setBackgroundResource(R.drawable.widget_resize_frame_holo); + setBackgroundResource(R.drawable.widget_resize_shadow); + setForeground(getResources().getDrawable(R.drawable.widget_resize_frame)); setPadding(0, 0, 0, 0); + final int handleMargin = getResources().getDimensionPixelSize(R.dimen.widget_handle_margin); LayoutParams lp; mLeftHandle = new ImageView(context); - mLeftHandle.setImageResource(R.drawable.widget_resize_handle_left); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, + mLeftHandle.setImageResource(R.drawable.ic_widget_resize_handle); + lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL); + lp.leftMargin = handleMargin; addView(mLeftHandle, lp); mRightHandle = new ImageView(context); - mRightHandle.setImageResource(R.drawable.widget_resize_handle_right); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, + mRightHandle.setImageResource(R.drawable.ic_widget_resize_handle); + lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.CENTER_VERTICAL); + lp.rightMargin = handleMargin; addView(mRightHandle, lp); mTopHandle = new ImageView(context); - mTopHandle.setImageResource(R.drawable.widget_resize_handle_top); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, + mTopHandle.setImageResource(R.drawable.ic_widget_resize_handle); + lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP); + lp.topMargin = handleMargin; addView(mTopHandle, lp); mBottomHandle = new ImageView(context); - mBottomHandle.setImageResource(R.drawable.widget_resize_handle_bottom); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, + mBottomHandle.setImageResource(R.drawable.ic_widget_resize_handle); + lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + lp.bottomMargin = handleMargin; addView(mBottomHandle, lp); - Rect p = new Rect(0, 0, 0, 0); if (!info.isCustomWidget) { - p = AppWidgetHostView.getDefaultPaddingForWidget(context, + mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, widgetView.getAppWidgetInfo().provider, null); } else { Resources r = context.getResources(); int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding); - p.set(padding, padding, padding, padding); + mWidgetPadding = new Rect(padding, padding, padding, padding); } - mWidgetPaddingLeft = p.left; - mWidgetPaddingTop = p.top; - mWidgetPaddingRight = p.right; - mWidgetPaddingBottom = p.bottom; - if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) { mTopHandle.setVisibility(GONE); mBottomHandle.setVisibility(GONE); @@ -138,8 +129,8 @@ public class AppWidgetResizeFrame extends FrameLayout { mRightHandle.setVisibility(GONE); } - final float density = mLauncher.getResources().getDisplayMetrics().density; - mBackgroundPadding = (int) Math.ceil(density * BACKGROUND_PADDING); + mBackgroundPadding = getResources() + .getDimensionPixelSize(R.dimen.resize_frame_background_padding); mTouchTargetWidth = 2 * mBackgroundPadding; // When we create the resize frame, we first mark all cells as unoccupied. The appropriate @@ -344,9 +335,9 @@ public class AppWidgetResizeFrame extends FrameLayout { static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher, int spanX, int spanY) { - getWidgetSizeRanges(launcher, spanX, spanY, mTmpRect); - widgetView.updateAppWidgetSize(null, mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom); + getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect); + widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top, + sTmpRect.right, sTmpRect.bottom); } public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) { @@ -404,19 +395,19 @@ public class AppWidgetResizeFrame extends FrameLayout { public void snapToWidget(boolean animate) { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); - int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding - mWidgetPaddingLeft - - mWidgetPaddingRight; - int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding - mWidgetPaddingTop - - mWidgetPaddingBottom; + int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding + - mWidgetPadding.left - mWidgetPadding.right; + int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding + - mWidgetPadding.top - mWidgetPadding.bottom; mTmpPt[0] = mWidgetView.getLeft(); mTmpPt[1] = mWidgetView.getTop(); mDragLayer.getDescendantCoordRelativeToSelf(mCellLayout.getShortcutsAndWidgets(), mTmpPt); - int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPaddingLeft; - int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPaddingTop; + int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPadding.left; + int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPadding.top; - // We need to make sure the frame's touchable regions lie fully within the bounds of the + // We need to make sure the frame's touchable regions lie fully within the bounds of the // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions // down accordingly to provide a proper touch target. if (newY < 0) { -- cgit v1.2.3 From 5537faaf9f40c2d7cdb8ab5947f494ce96c832a3 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 17:35:07 -0700 Subject: Adding null check to outline generator Change-Id: I8eb8b62a868d80fd0a7c9deec8915828bd8177d0 --- src/com/android/launcher3/HolographicOutlineHelper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java index 4a04d038a..5ff85d664 100644 --- a/src/com/android/launcher3/HolographicOutlineHelper.java +++ b/src/com/android/launcher3/HolographicOutlineHelper.java @@ -157,6 +157,9 @@ public class HolographicOutlineHelper { Bitmap createMediumDropShadow(BubbleTextView view) { Drawable icon = view.getIcon(); + if (icon == null) { + return null; + } Rect rect = icon.getBounds(); int bitmapWidth = (int) (rect.width() * view.getScaleX()); -- cgit v1.2.3 From b135956e95d2d7479290af89d618892ed0e7327d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 19:07:29 -0700 Subject: Avoiding object allocation during draw Change-Id: I94c146e0f4ad7386a31782f0e63e5228a2fa0442 --- src/com/android/launcher3/DragLayer.java | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index e25e6152c..a85c42648 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -48,34 +48,35 @@ import java.util.ArrayList; */ public class DragLayer extends InsettableFrameLayout { + public static final int ANIMATION_END_DISAPPEAR = 0; + public static final int ANIMATION_END_FADE_OUT = 1; + public static final int ANIMATION_END_REMAIN_VISIBLE = 2; + // Scrim color without any alpha component. private static final int SCRIM_COLOR = Color.BLACK & 0x00FFFFFF; + private final int[] mTmpXY = new int[2]; + @Thunk DragController mDragController; - private int[] mTmpXY = new int[2]; private int mXDown, mYDown; private Launcher mLauncher; // Variables relating to resizing widgets - private final ArrayList mResizeFrames = - new ArrayList(); + private final ArrayList mResizeFrames = new ArrayList<>(); private final boolean mIsRtl; private AppWidgetResizeFrame mCurrentResizeFrame; // Variables relating to animation of views after drop private ValueAnimator mDropAnim = null; private ValueAnimator mFadeOutAnim = null; - private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); + private final TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); @Thunk DragView mDropView = null; @Thunk int mAnchorViewInitialScrollX = 0; @Thunk View mAnchorView = null; private boolean mHoverPointClosesFolder = false; - private Rect mHitRect = new Rect(); - public static final int ANIMATION_END_DISAPPEAR = 0; - public static final int ANIMATION_END_FADE_OUT = 1; - public static final int ANIMATION_END_REMAIN_VISIBLE = 2; + private final Rect mHitRect = new Rect(); private TouchCompleteListener mTouchCompleteListener; @@ -87,6 +88,7 @@ public class DragLayer extends InsettableFrameLayout { private float mBackgroundAlpha = 0; // Related to adjacent page hints + private final Rect mScrollChildPosition = new Rect(); private boolean mInScrollArea; private boolean mShowPageHints; private Drawable mLeftHoverDrawable; @@ -914,6 +916,9 @@ public class DragLayer extends InsettableFrameLayout { void showPageHints() { mShowPageHints = true; + Workspace workspace = mLauncher.getWorkspace(); + getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1), + mScrollChildPosition); invalidate(); } @@ -937,10 +942,6 @@ public class DragLayer extends InsettableFrameLayout { if (mShowPageHints) { Workspace workspace = mLauncher.getWorkspace(); int width = getMeasuredWidth(); - Rect childRect = new Rect(); - getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1), - childRect); - int page = workspace.getNextPage(); CellLayout leftPage = (CellLayout) workspace.getChildAt(mIsRtl ? page + 1 : page - 1); CellLayout rightPage = (CellLayout) workspace.getChildAt(mIsRtl ? page - 1 : page + 1); @@ -948,15 +949,15 @@ public class DragLayer extends InsettableFrameLayout { if (leftPage != null && leftPage.isDragTarget()) { Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ? mLeftHoverDrawableActive : mLeftHoverDrawable; - left.setBounds(0, childRect.top, - left.getIntrinsicWidth(), childRect.bottom); + left.setBounds(0, mScrollChildPosition.top, + left.getIntrinsicWidth(), mScrollChildPosition.bottom); left.draw(canvas); } if (rightPage != null && rightPage.isDragTarget()) { Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ? mRightHoverDrawableActive : mRightHoverDrawable; right.setBounds(width - right.getIntrinsicWidth(), - childRect.top, width, childRect.bottom); + mScrollChildPosition.top, width, mScrollChildPosition.bottom); right.draw(canvas); } } -- cgit v1.2.3 From 057397714327b42c55804624cc06c7761d999f84 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 19:59:09 -0700 Subject: Removing some unused functionalities > Removing obsolete progrard rules > Removing BackgroundAlphaMultiplier from CellLayout, which is always 1 > Removign otiline animation from workspace. This animation never runs, as it is called during startReordeing which always happens when overview mode (workspaceInModalState() is true) Change-Id: I43219e41ea188771bc818988c1bcbd523f28cba6 --- proguard.flags | 18 ---------- src/com/android/launcher3/CellLayout.java | 25 ++++--------- src/com/android/launcher3/Workspace.java | 58 ++----------------------------- 3 files changed, 8 insertions(+), 93 deletions(-) diff --git a/proguard.flags b/proguard.flags index 6eb594825..5e2c384ce 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,19 +1,3 @@ --keep class com.android.launcher3.Launcher { - public void previousScreen(android.view.View); - public void nextScreen(android.view.View); - public void launchHotSeat(android.view.View); - public void onClickSearchButton(android.view.View); - public void onClickVoiceButton(android.view.View); - public void onClickConfigureButton(android.view.View); - public void onClickAllAppsButton(android.view.View); - public void dismissFirstRunCling(android.view.View); - public void dismissMigrationClingCopyApps(android.view.View); - public void dismissMigrationClingUseDefault(android.view.View); - public void dismissMigrationWorkspaceCling(android.view.View); - public void dismissWorkspaceCling(android.view.View); - public void dismissAllAppsCling(android.view.View); -} - -keep class com.android.launcher3.CellLayout { public float getBackgroundAlpha(); public void setBackgroundAlpha(float); @@ -44,8 +28,6 @@ -keep class com.android.launcher3.Workspace { public float getBackgroundAlpha(); public void setBackgroundAlpha(float); - public float getChildrenOutlineAlpha(); - public void setChildrenOutlineAlpha(float); } -keep class com.android.launcher3.MemoryDumpActivity { diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 57db805d8..48ddfe6bf 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -103,8 +103,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private float FOREGROUND_ALPHA_DAMPER = 0.65f; private int mForegroundAlpha = 0; private float mBackgroundAlpha; - private float mBackgroundAlphaMultiplier = 1.0f; - private boolean mDrawBackground = true; private Drawable mNormalBackground; private Drawable mActiveGlowBackground; @@ -423,10 +421,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - void disableBackground() { - mDrawBackground = false; - } - void disableDragTarget() { mIsDragTarget = false; } @@ -448,12 +442,16 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { @Override protected void onDraw(Canvas canvas) { + if (!mIsDragTarget) { + return; + } + // When we're large, we are either drawn in a "hover" state (ie when dragging an item to // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f) // When we're small, we are either drawn normally or in the "accepts drops" state (during // a drag). However, we also drag the mini hover background *over* one of those two // backgrounds - if (mDrawBackground && mBackgroundAlpha > 0.0f) { + if (mBackgroundAlpha > 0.0f) { Drawable bg; if (mIsDragOverlapping) { @@ -463,7 +461,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { bg = mNormalBackground; } - bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255)); + bg.setAlpha((int) (mBackgroundAlpha * 255)); bg.setBounds(mBackgroundRect); bg.draw(canvas); } @@ -951,17 +949,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return mBackgroundAlpha; } - public void setBackgroundAlphaMultiplier(float multiplier) { - if (mBackgroundAlphaMultiplier != multiplier) { - mBackgroundAlphaMultiplier = multiplier; - invalidate(); - } - } - - public float getBackgroundAlphaMultiplier() { - return mBackgroundAlphaMultiplier; - } - public void setBackgroundAlpha(float alpha) { if (mBackgroundAlpha != alpha) { mBackgroundAlpha = alpha; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 58e5877a6..e645b6570 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -57,6 +57,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.TextView; + import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; @@ -70,6 +71,7 @@ import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; + import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -86,10 +88,6 @@ public class Workspace extends SmoothPagedView Insettable, UninstallSource, AccessibilityDragSource { private static final String TAG = "Launcher.Workspace"; - private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0; - private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; - private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; - protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; protected static final int FADE_EMPTY_SCREEN_DURATION = 150; @@ -98,11 +96,6 @@ public class Workspace extends SmoothPagedView static final boolean MAP_NO_RECURSE = false; static final boolean MAP_RECURSE = true; - // These animators are used to fade the children's outlines - private ObjectAnimator mChildrenOutlineFadeInAnimation; - private ObjectAnimator mChildrenOutlineFadeOutAnimation; - private float mChildrenOutlineAlpha = 0; - private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; private long mTouchDownTime = -1; private long mCustomContentShowTime = -1; @@ -385,7 +378,6 @@ public class Workspace extends SmoothPagedView updateChildrenLayersEnabled(false); mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); - setChildrenBackgroundAlphaMultipliers(1f); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging InstallShortcutReceiver.enableInstallQueue(); post(new Runnable() { @@ -575,7 +567,6 @@ public class Workspace extends SmoothPagedView public void createCustomContentContainer() { CellLayout customScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false); - customScreen.disableBackground(); customScreen.disableDragTarget(); mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen); @@ -1530,45 +1521,12 @@ public class Workspace extends SmoothPagedView } } - void showOutlines() { - if (!workspaceInModalState() && !mIsSwitchingState) { - if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); - if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); - mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f); - mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION); - mChildrenOutlineFadeInAnimation.start(); - } - } - - void hideOutlines() { - if (!workspaceInModalState() && !mIsSwitchingState) { - if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); - if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); - mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f); - mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION); - mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY); - mChildrenOutlineFadeOutAnimation.start(); - } - } - public void showOutlinesTemporarily() { if (!mIsPageMoving && !isTouchActive()) { snapToPage(mCurrentPage); } } - public void setChildrenOutlineAlpha(float alpha) { - mChildrenOutlineAlpha = alpha; - for (int i = 0; i < getChildCount(); i++) { - CellLayout cl = (CellLayout) getChildAt(i); - cl.setBackgroundAlpha(alpha); - } - } - - public float getChildrenOutlineAlpha() { - return mChildrenOutlineAlpha; - } - float backgroundAlphaInterpolator(float r) { float pivotA = 0.1f; float pivotB = 0.4f; @@ -1598,13 +1556,6 @@ public class Workspace extends SmoothPagedView } } - private void setChildrenBackgroundAlphaMultipliers(float a) { - for (int i = 0; i < getChildCount(); i++) { - CellLayout child = (CellLayout) getChildAt(i); - child.setBackgroundAlphaMultiplier(a); - } - } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void enableAccessibleDrag(boolean enable) { @@ -2001,7 +1952,6 @@ public class Workspace extends SmoothPagedView public void onStartReordering() { super.onStartReordering(); - showOutlines(); // Reordering handles its own animations, disable the automatic ones. disableLayoutTransitions(); } @@ -2014,7 +1964,6 @@ public class Workspace extends SmoothPagedView return; } - hideOutlines(); mScreenOrder.clear(); int count = getChildCount(); for (int i = 0; i < count; i++) { @@ -2966,9 +2915,6 @@ public class Workspace extends SmoothPagedView mSpringLoadedDragController.cancel(); - if (!mIsPageMoving) { - hideOutlines(); - } mLauncher.getDragLayer().hidePageHints(); } -- cgit v1.2.3 From 05304db9053b4794bd7e776ff6e80cd1ae653110 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 18 May 2015 16:53:20 -0700 Subject: Reducing delay in applying widget previews loaded in the background. - Push the writing of previews to the DB to the worker thread to ensure that we can apply the loaded preview as soon as possible. - Reducing additional bitmap allocations from cancelled tasks that did not re-add their bitmaps to the unused set (happens when the list is scrolling quickly). Change-Id: Ib7c2cfdfcc668c215b3a74271e1165235fbdf752 --- src/com/android/launcher3/WidgetPreviewLoader.java | 100 +++++++++++++++------ 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 8459673b1..bbcdaae39 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -24,17 +24,16 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Handler; import android.os.Process; import android.util.Log; import android.util.LongSparseArray; - import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetCell; - import junit.framework.Assert; import java.util.ArrayList; @@ -69,6 +68,7 @@ public class WidgetPreviewLoader { private final CacheDb mDb; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + private final Handler mWorkerHandler; public WidgetPreviewLoader(Context context, IconCache iconCache) { mContext = context; @@ -76,6 +76,7 @@ public class WidgetPreviewLoader { mManager = AppWidgetManagerCompat.getInstance(context); mUserManager = UserManagerCompat.getInstance(context); mDb = new CacheDb(context); + mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); } /** @@ -83,8 +84,6 @@ public class WidgetPreviewLoader { * called on UI thread * * @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo} - * @param immediateResult A bitmap array of size 1. If the result is already cached, it is - * set to the final result. * @return a request id which can be used to cancel the request. */ public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight, @@ -289,7 +288,10 @@ public class WidgetPreviewLoader { } } - private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle) { + /** + * Reads the preview bitmap from the DB or null if the preview is not in the DB. + */ + private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) { Cursor cursor = null; try { cursor = mDb.getReadableDatabase().query( @@ -302,12 +304,18 @@ public class WidgetPreviewLoader { key.size }, null, null, null); + // If cancelled, skip getting the blob and decoding it into a bitmap + if (loadTask.isCancelled()) { + return null; + } if (cursor.moveToNext()) { byte[] blob = cursor.getBlob(0); BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inBitmap = recycle; try { - return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts); + if (!loadTask.isCancelled()) { + return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts); + } } catch (Exception e) { return null; } @@ -545,15 +553,17 @@ public class WidgetPreviewLoader { mTask.cancel(true); } - if (mTask.mBitmap == null) { - return; - } - - // The preview is no longer bound to any view, move it to {@link WeakReference} list. - synchronized(mUnusedBitmaps) { - mUnusedBitmaps.add(mTask.mBitmap); + // This only handles the case where the PreviewLoadTask is cancelled after the task has + // successfully completed (including having written to disk when necessary). In the + // other cases where it is cancelled while the task is running, it will be cleaned up + // in the tasks's onCancelled() call, and if cancelled while the task is writing to + // disk, it will be cancelled in the task's onPostExecute() call. + if (mTask.mBitmapToRecycle != null) { + synchronized (mUnusedBitmaps) { + mUnusedBitmaps.add(mTask.mBitmapToRecycle); + } + mTask.mBitmapToRecycle = null; } - mTask.mBitmap = null; } } @@ -564,7 +574,8 @@ public class WidgetPreviewLoader { private final int mPreviewHeight; private final int mPreviewWidth; private final WidgetCell mCaller; - private Bitmap mBitmap; + private long[] mVersions; + private Bitmap mBitmapToRecycle; PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, int previewHeight, WidgetCell caller) { @@ -584,6 +595,10 @@ public class WidgetPreviewLoader { Bitmap unusedBitmap = null; synchronized (mUnusedBitmaps) { + // If already cancelled before this gets to run in the background, then return early + if (isCancelled()) { + return null; + } // Check if we can use a bitmap for (Bitmap candidate : mUnusedBitmaps) { if (candidate != null && candidate.isMutable() && @@ -600,31 +615,64 @@ public class WidgetPreviewLoader { mUnusedBitmaps.remove(unusedBitmap); } } + // If cancelled now, don't bother reading the preview from the DB if (isCancelled()) { - return null; + return unusedBitmap; } - Bitmap preview = readFromDb(mKey, unusedBitmap); + Bitmap preview = readFromDb(mKey, unusedBitmap, this); + // Only consider generating the preview if we have not cancelled the task already if (!isCancelled() && preview == null) { // Fetch the version info before we generate the preview, so that, in-case the // app was updated while we are generating the preview, we use the old version info, // which would gets re-written next time. - long[] versions = getPackageVersion(mKey.componentName.getPackageName()); + mVersions = getPackageVersion(mKey.componentName.getPackageName()); // it's not in the db... we need to generate it preview = generatePreview(mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight); - - if (!isCancelled()) { - writeToDb(mKey, versions, preview); - } } - return preview; } @Override - protected void onPostExecute(Bitmap result) { - mBitmap = result; - mCaller.applyPreview(result); + protected void onPostExecute(final Bitmap preview) { + mCaller.applyPreview(preview); + + // Write the generated preview to the DB in the worker thread + if (mVersions != null) { + mWorkerHandler.post(new Runnable() { + @Override + public void run() { + if (!isCancelled()) { + // If we are still using this preview, then write it to the DB and then + // let the normal clear mechanism recycle the bitmap + writeToDb(mKey, mVersions, preview); + mBitmapToRecycle = preview; + } else { + // If we've already cancelled, then skip writing the bitmap to the DB + // and manually add the bitmap back to the recycled set + synchronized (mUnusedBitmaps) { + mUnusedBitmaps.add(preview); + } + } + } + }); + } else { + // If we don't need to write to disk, then ensure the preview gets recycled by + // the normal clear mechanism + mBitmapToRecycle = preview; + } + } + + @Override + protected void onCancelled(Bitmap preview) { + // If we've cancelled while the task is running, then can return the bitmap to the + // recycled set immediately. Otherwise, it will be recycled after the preview is written + // to disk. + if (preview != null) { + synchronized (mUnusedBitmaps) { + mUnusedBitmaps.add(preview); + } + } } } -- cgit v1.2.3 From 0230c3a87f89afcfb61f80310d5b896986e23118 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 20 May 2015 11:03:46 -0700 Subject: List work profile widgets to the end of the row of the widget tray, then sort b/20339403 Change-Id: I7cd824e47eba1121c9053a4064a51750bed587e7 --- .../model/WidgetsAndShortcutNameComparator.java | 33 +++++++++++++--------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java index 7c4e80651..61e895283 100644 --- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java +++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java @@ -1,6 +1,5 @@ package com.android.launcher3.model; -import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -8,6 +7,7 @@ import android.content.pm.ResolveInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.compat.UserHandleCompat; import java.text.Collator; import java.util.Comparator; @@ -18,12 +18,14 @@ public class WidgetsAndShortcutNameComparator implements Comparator { private final PackageManager mPackageManager; private final HashMap mLabelCache; private final Collator mCollator; + private final UserHandleCompat mMainHandle; public WidgetsAndShortcutNameComparator(Context context) { mManager = AppWidgetManagerCompat.getInstance(context); mPackageManager = context.getPackageManager(); mLabelCache = new HashMap(); mCollator = Collator.getInstance(); + mMainHandle = UserHandleCompat.myUserHandle(); } @Override @@ -45,19 +47,22 @@ public class WidgetsAndShortcutNameComparator implements Comparator { : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager)); mLabelCache.put(b, labelB); } - int result = mCollator.compare(labelA, labelB); - if (result == 0 && a instanceof AppWidgetProviderInfo && - b instanceof AppWidgetProviderInfo) { - AppWidgetProviderInfo aInfo = (AppWidgetProviderInfo) a; - AppWidgetProviderInfo bInfo = (AppWidgetProviderInfo) b; - - // prioritize main user's widgets against work profile widgets. - if (aInfo.getProfile().equals(android.os.Process.myUserHandle())) { - return -1; - } else if (bInfo.getProfile().equals(android.os.Process.myUserHandle())) { - return 1; - } + + // Currently, there is no work profile shortcuts, hence only considering the widget cases. + + boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) && + !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a)); + boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) && + !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b)); + + // Independent of how the labels compare, if only one of the two widget info belongs to + // work profile, put that one in the back. + if (aWorkProfile && !bWorkProfile) { + return 1; + } + if (!aWorkProfile && bWorkProfile) { + return -1; } - return result; + return mCollator.compare(labelA, labelB); } }; -- cgit v1.2.3 From f72eb710af8d5125d1d87ea18a94b19c9bf56cea Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 20 May 2015 11:13:36 -0700 Subject: Removing override annotation to fix build. Change-Id: I5a4dfa339db7862ac3db4cc379469ac8d9f6fae5 --- src/com/android/launcher3/widget/WidgetsListAdapter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 2f733dcbc..3916c00d2 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -183,7 +183,6 @@ public class WidgetsListAdapter extends Adapter { } } - @Override public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) { // If child views are animating, then the RecyclerView may choose not to recycle the view, // causing extraneous onCreateViewHolder() calls. It is safe in this case to continue -- cgit v1.2.3 From a1fbd84b791474a9e86b67caeaf27b8429afba73 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 20 May 2015 13:40:27 -0700 Subject: Fixing unnecessary padding added to pages in overview mode Change-Id: Icd17c2956b201ea0b6c6a2a495f567f51987b1ac --- src/com/android/launcher3/FolderPagedView.java | 5 +++++ src/com/android/launcher3/PagedView.java | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index de30b606a..06ed58895 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -241,6 +241,11 @@ public class FolderPagedView extends PagedView { return page; } + @Override + protected int getChildGap() { + return getPaddingLeft() + getPaddingRight(); + } + public void setFixedSize(int width, int height) { width -= (getPaddingLeft() + getPaddingRight()); height -= (getPaddingTop() + getPaddingBottom()); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 686dd2f58..dda9a166c 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -909,8 +909,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc pageGap = getPaddingRight(); } - childLeft += childWidth + pageGap - + (lp.isFullScreenPage ? 0 : (getPaddingLeft() + getPaddingRight())); + childLeft += childWidth + pageGap + getChildGap(); } } @@ -958,6 +957,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } + protected int getChildGap() { + return 0; + } + private void updateMaxScrollX() { int childCount = getChildCount(); if (childCount > 0) { -- cgit v1.2.3 From 611bcf172a7b7043040491ed1343c6f96af25cd3 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 20 May 2015 14:14:04 -0700 Subject: UX widget cell color issue - widget cell color is slightly brigher than the background of the widget row. - They should be the same. Change-Id: I20bda325d5d2fe7e194a453978dc74fd56a4e154 --- res/layout/widgets_list_row_view.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml index dc1bccefb..67b4acb4b 100644 --- a/res/layout/widgets_list_row_view.xml +++ b/res/layout/widgets_list_row_view.xml @@ -19,6 +19,7 @@ android:id="@+id/widgets_cell_list_container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/widgets_cell_color" android:orientation="vertical" android:focusable="true" android:descendantFocusability="afterDescendants"> @@ -52,7 +53,7 @@ android:id="@+id/widgets_scroll_container" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:scrollbars="none" > + android:scrollbars="none"> + android:showDividers="middle"/> -- cgit v1.2.3 From 99d950fb4dfb5cb9edaae572579fc25b2aca726f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 20 May 2015 14:42:25 -0700 Subject: Removing unnecessary linear layout Change-Id: I7fe423b9526805a2e84887e0437ebdc96203c263 --- res/layout/widgets_view.xml | 21 +++++++-------------- .../launcher3/widget/WidgetsContainerView.java | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 1857595e8..9d9fa10c2 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -34,19 +34,12 @@ android:focusable="false" android:visibility="invisible" /> - - - + android:id="@+id/widgets_list_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/quantum_panel_dark" + android:elevation="15dp" + android:visibility="gone" /> + \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 181c08a40..7a7895ff8 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -135,7 +135,7 @@ public class WidgetsContainerView extends BaseContainerView // public View getContentView() { - return findViewById(R.id.widgets_content); + return mView; } public View getRevealView() { -- cgit v1.2.3 From e0cab303b8cf1fd00b152219aa56663608582f5a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 20 May 2015 16:40:30 -0700 Subject: Setting drawable to droptarget in the code to avoid multiple drawable inflation Change-Id: Ib713b3051e2707d46a4ee0090aed2db1d2e641b9 --- res/layout/search_drop_target_bar.xml | 6 ----- src/com/android/launcher3/ButtonDropTarget.java | 33 ++++++------------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml index b0435aa0e..4737ee1bc 100644 --- a/res/layout/search_drop_target_bar.xml +++ b/res/layout/search_drop_target_bar.xml @@ -36,8 +36,6 @@ @@ -50,8 +48,6 @@ @@ -64,8 +60,6 @@ diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index b8214d1ef..80b542f1d 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -23,7 +23,6 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Build; import android.util.AttributeSet; @@ -85,21 +84,15 @@ public abstract class ButtonDropTarget extends TextView @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) protected void setDrawable(int resId) { - // Get the hover color - mDrawable = (TransitionDrawable) getCurrentDrawable(); - - if (mDrawable == null) { - // TODO: investigate why this is ever happening. Presently only on one known device. - mDrawable = (TransitionDrawable) getResources().getDrawable(resId); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null); - } else { - setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null); - } - } + // We do not set the drawable in the xml as that inflates two drawables corresponding to + // drawableLeft and drawableStart. + mDrawable = (TransitionDrawable) getResources().getDrawable(resId); + mDrawable.setCrossFadeEnabled(true); - if (null != mDrawable) { - mDrawable.setCrossFadeEnabled(true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null); + } else { + setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null); } } @@ -111,16 +104,6 @@ public abstract class ButtonDropTarget extends TextView mSearchDropTargetBar = searchDropTargetBar; } - protected Drawable getCurrentDrawable() { - Drawable[] drawables = getCompoundDrawables(); - for (int i = 0; i < drawables.length; ++i) { - if (drawables[i] != null) { - return drawables[i]; - } - } - return null; - } - @Override public void onFlingToDelete(DragObject d, PointF vec) { } -- cgit v1.2.3 From c393b0765df8d2d34b3b996b71700a705b7d0106 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 20 May 2015 15:03:13 -0700 Subject: Fixing issue where the prediction bar apps are not focused. - Also fixes issue where all apps is not accessible by keyboard when there are no other apps in the hotseat. Bug: 21334471 --- res/layout/apps_list_view.xml | 2 ++ src/com/android/launcher3/AppsContainerView.java | 18 +++++++++++++++- src/com/android/launcher3/FocusHelper.java | 6 ++++-- src/com/android/launcher3/Hotseat.java | 26 +++++++----------------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml index 6f9be05af..040498359 100644 --- a/res/layout/apps_list_view.xml +++ b/res/layout/apps_list_view.xml @@ -39,6 +39,8 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/apps_search_bar_height" android:orientation="horizontal" + android:descendantFocusability="afterDescendants" + android:focusable="true" android:visibility="invisible" > diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 5ccb6c620..76f3c8861 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -183,6 +183,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, private int mContainerInset; private int mPredictionBarHeight; private int mLastRecyclerViewScrollPos = -1; + private boolean mFocusPredictionBarOnFirstBind; private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; @@ -298,7 +299,17 @@ public class AppsContainerView extends BaseContainerView implements DragSource, @Override public void onFocusChange(View v, boolean hasFocus) { if (v == mContentView && hasFocus) { - mAppsRecyclerView.requestFocus(); + if (!mApps.getPredictedApps().isEmpty()) { + // If the prediction bar is going to be bound, then defer focusing until + // it is first bound + if (mPredictionBarView.getChildCount() == 0) { + mFocusPredictionBarOnFirstBind = true; + } else { + mPredictionBarView.requestFocus(); + } + } else { + mAppsRecyclerView.requestFocus(); + } } } }); @@ -387,6 +398,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource, icon.setVisibility(View.INVISIBLE); } } + + if (mFocusPredictionBarOnFirstBind) { + mFocusPredictionBarOnFirstBind = false; + mPredictionBarView.requestFocus(); + } } @Override diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index c77d41657..678ed0f82 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -314,12 +314,14 @@ public class FocusHelper { // with the hotseat. if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */, - hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */); + hotseat.getAllAppsButtonRank(), + !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */); countY = countY + 1; } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */, - hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */); + hotseat.getAllAppsButtonRank(), + !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */); countX = countX + 1; } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { workspace.removeWorkspaceItem(v); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index b614bc628..b8337b6a4 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -64,6 +64,13 @@ public class Hotseat extends FrameLayout { return mContent; } + /** + * Returns whether there are other icons than the all apps button in the hotseat. + */ + public boolean hasIcons() { + return mContent.getShortcutsAndWidgets().getChildCount() > 1; + } + /** * Registers the specified listener on the cell layout of the hotseat. */ @@ -98,25 +105,6 @@ public class Hotseat extends FrameLayout { return rank == mAllAppsButtonRank; } - /** This returns the coordinates of an app in a given cell, relative to the DragLayer */ - Rect getCellCoordinates(int cellX, int cellY) { - Rect coords = new Rect(); - mContent.cellToRect(cellX, cellY, 1, 1, coords); - int[] hotseatInParent = new int[2]; - Utilities.getDescendantCoordRelativeToParent(this, mLauncher.getDragLayer(), - hotseatInParent, false); - coords.offset(hotseatInParent[0], hotseatInParent[1]); - - // Center the icon - int cWidth = mContent.getShortcutsAndWidgets().getCellContentWidth(); - int cHeight = mContent.getShortcutsAndWidgets().getCellContentHeight(); - int cellPaddingX = (int) Math.max(0, ((coords.width() - cWidth) / 2f)); - int cellPaddingY = (int) Math.max(0, ((coords.height() - cHeight) / 2f)); - coords.offset(cellPaddingX, cellPaddingY); - - return coords; - } - @Override protected void onFinishInflate() { super.onFinishInflate(); -- cgit v1.2.3 From 75b0f552ae653a4afc64c5fb6782a481fdb89b4d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 20 May 2015 21:57:06 -0700 Subject: Breaking icon update task so that it doesn't block worker thread Bug: 20945600 Change-Id: Iaf516577898b51ad6e8a813d7f018ecad969c100 --- src/com/android/launcher3/IconCache.java | 95 ++++++++++++++++------- src/com/android/launcher3/LauncherModel.java | 112 ++++++++++++--------------- 2 files changed, 120 insertions(+), 87 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 1fac5a1c2..ac257b5c3 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -35,6 +35,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; @@ -46,7 +47,6 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PackageItemInfo; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -71,6 +71,8 @@ public class IconCache { private static final int LOW_RES_SCALE_FACTOR = 8; + private static final Object ICON_UPDATE_TOKEN = new Object(); + @Thunk static class CacheEntry { public Bitmap icon; public CharSequence title; @@ -223,13 +225,32 @@ public class IconCache { new String[] {packageName + "/%", Long.toString(userSerial)}); } + public void updateDbIcons() { + // Remove all active icon update tasks. + mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); + + mIconDb.updateSystemStateString(mContext); + for (UserHandleCompat user : mUserManager.getUserProfiles()) { + // Query for the set of apps + final List apps = mLauncherApps.getActivityList(null, user); + // Fail if we don't have any apps + // TODO: Fix this. Only fail for the current user. + if (apps == null || apps.isEmpty()) { + return; + } + + // Update icon cache. This happens in segments and {@link #onPackageIconsUpdated} + // is called by the icon cache when the job is complete. + updateDBIcons(user, apps); + } + } + /** * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in * the DB and are updated. * @return The set of packages for which icons have updated. */ - public HashSet updateDBIcons(UserHandleCompat user, List apps) { - mIconDb.updateSystemStateString(mContext); + private void updateDBIcons(UserHandleCompat user, List apps) { long userSerial = mUserManager.getSerialNumberForUser(user); PackageManager pm = mContext.getPackageManager(); HashMap pkgInfoMap = new HashMap(); @@ -257,7 +278,7 @@ public class IconCache { final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); HashSet itemsToRemove = new HashSet(); - HashSet updatedPackages = new HashSet(); + Stack appsToUpdate = new Stack<>(); while (c.moveToNext()) { String cn = c.getString(indexComponent); @@ -281,14 +302,9 @@ public class IconCache { } if (app == null) { itemsToRemove.add(c.getInt(rowIndex)); - continue; + } else { + appsToUpdate.add(app); } - ContentValues values = updateCacheAndGetContentValues(app, true); - mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, - IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", - new String[] {cn, Long.toString(userSerial)}); - - updatedPackages.add(component.getPackageName()); } c.close(); if (!itemsToRemove.isEmpty()) { @@ -298,11 +314,12 @@ public class IconCache { } // Insert remaining apps. - if (!componentMap.isEmpty()) { - mWorkerHandler.post(new SerializedIconAdditionTask(userSerial, pkgInfoMap, - componentMap.values())); + if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { + Stack appsToAdd = new Stack<>(); + appsToAdd.addAll(componentMap.values()); + new SerializedIconUpdateTask(userSerial, pkgInfoMap, + appsToAdd, appsToUpdate).scheduleNext(); } - return updatedPackages; } private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, @@ -684,25 +701,46 @@ public class IconCache { } /** - * A runnable that adds icons in the DB for the provided LauncherActivityInfoCompat list. - * Items are added one at a time, to that the worker thread does not get blocked. + * A runnable that updates invalid icons and adds missing icons in the DB for the provided + * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the + * worker thread doesn't get blocked. */ - private class SerializedIconAdditionTask implements Runnable { + private class SerializedIconUpdateTask implements Runnable { private final long mUserSerial; private final HashMap mPkgInfoMap; private final Stack mAppsToAdd; + private final Stack mAppsToUpdate; + private final HashSet mUpdatedPackages = new HashSet(); - private SerializedIconAdditionTask(long userSerial, HashMap pkgInfoMap, - Collection appsToAdd) { + private SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap, + Stack appsToAdd, + Stack appsToUpdate) { mUserSerial = userSerial; mPkgInfoMap = pkgInfoMap; - mAppsToAdd = new Stack(); - mAppsToAdd.addAll(appsToAdd); + mAppsToAdd = appsToAdd; + mAppsToUpdate = appsToUpdate; } @Override public void run() { - if (!mAppsToAdd.isEmpty()) { + if (!mAppsToUpdate.isEmpty()) { + LauncherActivityInfoCompat app = mAppsToUpdate.pop(); + String cn = app.getComponentName().flattenToString(); + ContentValues values = updateCacheAndGetContentValues(app, true); + mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, + IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", + new String[] {cn, Long.toString(mUserSerial)}); + mUpdatedPackages.add(app.getComponentName().getPackageName()); + + if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { + // No more app to update. Notify model. + LauncherAppState.getInstance().getModel().onPackageIconsUpdated( + mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial)); + } + + // Let it run one more time. + scheduleNext(); + } else if (!mAppsToAdd.isEmpty()) { LauncherActivityInfoCompat app = mAppsToAdd.pop(); PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName()); if (info != null) { @@ -710,10 +748,15 @@ public class IconCache { addIconToDBAndMemCache(app, info, mUserSerial); } } + + if (!mAppsToAdd.isEmpty()) { + scheduleNext(); + } } - if (!mAppsToAdd.isEmpty()) { - mWorkerHandler.post(this); - } + } + + public void scheduleNext() { + mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, SystemClock.uptimeMillis() + 1); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3e05f57b9..9deee43d4 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2754,7 +2754,7 @@ public class LauncherModel extends BroadcastReceiver return; } } - updateAllAppsIconsCache(); + mIconCache.updateDbIcons(); synchronized (LoaderTask.this) { if (mStopped) { return; @@ -2880,75 +2880,65 @@ public class LauncherModel extends BroadcastReceiver } } - private void updateAllAppsIconsCache() { - final ArrayList updatedApps = new ArrayList<>(); - - for (UserHandleCompat user : mUserManager.getUserProfiles()) { - // Query for the set of apps - final List apps = mLauncherApps.getActivityList(null, user); - // Fail if we don't have any apps - // TODO: Fix this. Only fail for the current user. - if (apps == null || apps.isEmpty()) { - return; - } - - // Update icon cache - HashSet updatedPackages = mIconCache.updateDBIcons(user, apps); - - // If any package icon has changed (app was updated while launcher was dead), - // update the corresponding shortcuts. - if (!updatedPackages.isEmpty()) { - final ArrayList updatedShortcuts = new ArrayList<>(); - synchronized (sBgLock) { - for (ItemInfo info : sBgItemsIdMap) { - if (info instanceof ShortcutInfo && user.equals(info.user) - && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - ShortcutInfo si = (ShortcutInfo) info; - ComponentName cn = si.getTargetComponent(); - if (cn != null && updatedPackages.contains(cn.getPackageName())) { - si.updateIcon(mIconCache); - updatedShortcuts.add(si); - } - } - } - mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps); - } + public void dumpState() { + synchronized (sBgLock) { + Log.d(TAG, "mLoaderTask.mContext=" + mContext); + Log.d(TAG, "mLoaderTask.mStopped=" + mStopped); + Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished); + Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size()); + } + } + } - if (!updatedShortcuts.isEmpty()) { - final UserHandleCompat userFinal = user; - mHandler.post(new Runnable() { + /** + * Called when the icons for packages have been updated in the icon cache. + */ + public void onPackageIconsUpdated(HashSet updatedPackages, UserHandleCompat user) { + final Callbacks callbacks = getCallback(); + final ArrayList updatedApps = new ArrayList<>(); + final ArrayList updatedShortcuts = new ArrayList<>(); - public void run() { - Callbacks cb = getCallback(); - if (cb != null) { - cb.bindShortcutsChanged(updatedShortcuts, - new ArrayList(), userFinal); - } - } - }); + // If any package icon has changed (app was updated while launcher was dead), + // update the corresponding shortcuts. + synchronized (sBgLock) { + for (ItemInfo info : sBgItemsIdMap) { + if (info instanceof ShortcutInfo && user.equals(info.user) + && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (cn != null && updatedPackages.contains(cn.getPackageName())) { + si.updateIcon(mIconCache); + updatedShortcuts.add(si); } } } - if (!updatedApps.isEmpty()) { - mHandler.post(new Runnable() { + mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps); + } - public void run() { - Callbacks cb = getCallback(); - if (cb != null) { - cb.bindAppsUpdated(updatedApps); - } + if (!updatedShortcuts.isEmpty()) { + final UserHandleCompat userFinal = user; + mHandler.post(new Runnable() { + + public void run() { + Callbacks cb = getCallback(); + if (cb != null && callbacks == cb) { + cb.bindShortcutsChanged(updatedShortcuts, + new ArrayList(), userFinal); } - }); - } + } + }); } - public void dumpState() { - synchronized (sBgLock) { - Log.d(TAG, "mLoaderTask.mContext=" + mContext); - Log.d(TAG, "mLoaderTask.mStopped=" + mStopped); - Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished); - Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size()); - } + if (!updatedApps.isEmpty()) { + mHandler.post(new Runnable() { + + public void run() { + Callbacks cb = getCallback(); + if (cb != null && callbacks == cb) { + cb.bindAppsUpdated(updatedApps); + } + } + }); } } -- cgit v1.2.3 From a508e4f804c2bddf9e2cdfa3d0bd4a8202ff2151 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 21 May 2015 09:33:57 -0700 Subject: Do not trim folder title Bug: 21297281 Change-Id: I633780f8207aabc8362f016e6b5a6e720cee1b86 --- src/com/android/launcher3/FolderInfo.java | 2 +- src/com/android/launcher3/LauncherModel.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 930f91103..aea21c95b 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -92,7 +92,7 @@ public class FolderInfo extends ItemInfo { } public void setTitle(CharSequence title) { - this.title = Utilities.trim(title); + this.title = title; for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onTitleChanged(title); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3e05f57b9..ad176aeca 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -968,7 +968,8 @@ public class LauncherModel extends BroadcastReceiver break; } - folderInfo.title = Utilities.trim(c.getString(titleIndex)); + // Do not trim the folder label, as is was set by the user. + folderInfo.title = c.getString(titleIndex); folderInfo.id = id; folderInfo.container = c.getInt(containerIndex); folderInfo.screenId = c.getInt(screenIndex); @@ -2110,7 +2111,8 @@ public class LauncherModel extends BroadcastReceiver id = c.getLong(idIndex); FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id); - folderInfo.title = Utilities.trim(c.getString(titleIndex)); + // Do not trim the folder label, as is was set by the user. + folderInfo.title = c.getString(titleIndex); folderInfo.id = id; container = c.getInt(containerIndex); folderInfo.container = container; -- cgit v1.2.3 From 3a644ed1ce12554fcdf5c499e959bda986d10551 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 21 May 2015 10:28:02 -0700 Subject: Using color filter when animating the drop target instead to TransitionDrawable > No more flickering when crossfading between two bitmaps in small duration > Allows us to control the color directly without depending on the assets Change-Id: Ie7ed8bb94baf560e9b939cca624ed1bad457732c --- res/drawable-hdpi/ic_launcher_info_active.png | Bin 3895 -> 0 bytes res/drawable-hdpi/ic_launcher_remove_active.png | Bin 2695 -> 0 bytes res/drawable-hdpi/ic_launcher_uninstall_active.png | Bin 1483 -> 0 bytes res/drawable-mdpi/ic_launcher_info_active.png | Bin 2597 -> 0 bytes res/drawable-mdpi/ic_launcher_remove_active.png | Bin 1853 -> 0 bytes res/drawable-mdpi/ic_launcher_uninstall_active.png | Bin 1274 -> 0 bytes res/drawable-xhdpi/ic_launcher_info_active.png | Bin 5449 -> 0 bytes res/drawable-xhdpi/ic_launcher_remove_active.png | Bin 2700 -> 0 bytes .../ic_launcher_uninstall_active.png | Bin 1893 -> 0 bytes res/drawable-xxhdpi/ic_launcher_info_active.png | Bin 8542 -> 0 bytes res/drawable-xxhdpi/ic_launcher_remove_active.png | Bin 5126 -> 0 bytes .../ic_launcher_uninstall_active.png | Bin 2673 -> 0 bytes res/drawable-xxxhdpi/ic_launcher_info_active.png | Bin 7999 -> 0 bytes res/drawable-xxxhdpi/ic_launcher_remove_active.png | Bin 3843 -> 0 bytes .../ic_launcher_uninstall_active.png | Bin 2776 -> 0 bytes res/drawable/info_target_selector.xml | 24 --------- res/drawable/remove_target_selector.xml | 24 --------- res/drawable/uninstall_target_selector.xml | 24 --------- res/values/colors.xml | 6 +-- src/com/android/launcher3/ButtonDropTarget.java | 59 ++++++++++++++++----- src/com/android/launcher3/DeleteDropTarget.java | 2 +- src/com/android/launcher3/DragView.java | 10 ++-- src/com/android/launcher3/InfoDropTarget.java | 2 +- src/com/android/launcher3/UninstallDropTarget.java | 2 +- 24 files changed, 59 insertions(+), 94 deletions(-) delete mode 100644 res/drawable-hdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-hdpi/ic_launcher_remove_active.png delete mode 100644 res/drawable-hdpi/ic_launcher_uninstall_active.png delete mode 100644 res/drawable-mdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-mdpi/ic_launcher_remove_active.png delete mode 100644 res/drawable-mdpi/ic_launcher_uninstall_active.png delete mode 100644 res/drawable-xhdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-xhdpi/ic_launcher_remove_active.png delete mode 100644 res/drawable-xhdpi/ic_launcher_uninstall_active.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_remove_active.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_uninstall_active.png delete mode 100644 res/drawable-xxxhdpi/ic_launcher_info_active.png delete mode 100644 res/drawable-xxxhdpi/ic_launcher_remove_active.png delete mode 100644 res/drawable-xxxhdpi/ic_launcher_uninstall_active.png delete mode 100644 res/drawable/info_target_selector.xml delete mode 100644 res/drawable/remove_target_selector.xml delete mode 100644 res/drawable/uninstall_target_selector.xml diff --git a/res/drawable-hdpi/ic_launcher_info_active.png b/res/drawable-hdpi/ic_launcher_info_active.png deleted file mode 100644 index f7a3b68fe..000000000 Binary files a/res/drawable-hdpi/ic_launcher_info_active.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_remove_active.png b/res/drawable-hdpi/ic_launcher_remove_active.png deleted file mode 100644 index e53de0da4..000000000 Binary files a/res/drawable-hdpi/ic_launcher_remove_active.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_uninstall_active.png b/res/drawable-hdpi/ic_launcher_uninstall_active.png deleted file mode 100644 index 22b97ee50..000000000 Binary files a/res/drawable-hdpi/ic_launcher_uninstall_active.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_info_active.png b/res/drawable-mdpi/ic_launcher_info_active.png deleted file mode 100644 index ea712722d..000000000 Binary files a/res/drawable-mdpi/ic_launcher_info_active.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_remove_active.png b/res/drawable-mdpi/ic_launcher_remove_active.png deleted file mode 100644 index f36cfdd2f..000000000 Binary files a/res/drawable-mdpi/ic_launcher_remove_active.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_uninstall_active.png b/res/drawable-mdpi/ic_launcher_uninstall_active.png deleted file mode 100644 index e4ee9112d..000000000 Binary files a/res/drawable-mdpi/ic_launcher_uninstall_active.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_info_active.png b/res/drawable-xhdpi/ic_launcher_info_active.png deleted file mode 100644 index b438f9eb2..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_info_active.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_remove_active.png b/res/drawable-xhdpi/ic_launcher_remove_active.png deleted file mode 100644 index 14ac79d41..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_remove_active.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_uninstall_active.png b/res/drawable-xhdpi/ic_launcher_uninstall_active.png deleted file mode 100644 index 2c19b329d..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_uninstall_active.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_info_active.png b/res/drawable-xxhdpi/ic_launcher_info_active.png deleted file mode 100644 index d354dd33b..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_info_active.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_remove_active.png b/res/drawable-xxhdpi/ic_launcher_remove_active.png deleted file mode 100644 index 9df44042e..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_remove_active.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxhdpi/ic_launcher_uninstall_active.png deleted file mode 100644 index db7d339e6..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png and /dev/null differ diff --git a/res/drawable-xxxhdpi/ic_launcher_info_active.png b/res/drawable-xxxhdpi/ic_launcher_info_active.png deleted file mode 100644 index 162e23dc2..000000000 Binary files a/res/drawable-xxxhdpi/ic_launcher_info_active.png and /dev/null differ diff --git a/res/drawable-xxxhdpi/ic_launcher_remove_active.png b/res/drawable-xxxhdpi/ic_launcher_remove_active.png deleted file mode 100644 index c0b8ea2ee..000000000 Binary files a/res/drawable-xxxhdpi/ic_launcher_remove_active.png and /dev/null differ diff --git a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png deleted file mode 100644 index 75896f3a2..000000000 Binary files a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png and /dev/null differ diff --git a/res/drawable/info_target_selector.xml b/res/drawable/info_target_selector.xml deleted file mode 100644 index 51caece53..000000000 --- a/res/drawable/info_target_selector.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - diff --git a/res/drawable/remove_target_selector.xml b/res/drawable/remove_target_selector.xml deleted file mode 100644 index 9025e8a1a..000000000 --- a/res/drawable/remove_target_selector.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - diff --git a/res/drawable/uninstall_target_selector.xml b/res/drawable/uninstall_target_selector.xml deleted file mode 100644 index 175cc20dd..000000000 --- a/res/drawable/uninstall_target_selector.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - diff --git a/res/values/colors.xml b/res/values/colors.xml index 1e89615af..a5db2fc40 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -20,9 +20,9 @@ - #DAC1C1C1 - #DAF0592B - #DA009688 + #FFC1C1C1 + #FFF0592B + #FF009688 #80000000 #80c6c5c5 diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 80b542f1d..4cd28c034 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -16,14 +16,20 @@ package com.android.launcher3; +import android.animation.AnimatorSet; +import android.animation.FloatArrayEvaluator; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.TransitionDrawable; +import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.view.View; @@ -55,9 +61,11 @@ public abstract class ButtonDropTarget extends TextView protected int mHoverColor = 0; protected ColorStateList mOriginalTextColor; - protected TransitionDrawable mDrawable; + protected Drawable mDrawable; + + private AnimatorSet mCurrentColorAnim; + private ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter; - private ObjectAnimator mCurrentColorAnim; public ButtonDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -86,8 +94,7 @@ public abstract class ButtonDropTarget extends TextView protected void setDrawable(int resId) { // We do not set the drawable in the xml as that inflates two drawables corresponding to // drawableLeft and drawableStart. - mDrawable = (TransitionDrawable) getResources().getDrawable(resId); - mDrawable.setCrossFadeEnabled(true); + mDrawable = getResources().getDrawable(resId); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null); @@ -111,10 +118,13 @@ public abstract class ButtonDropTarget extends TextView public final void onDragEnter(DragObject d) { d.dragView.setColor(mHoverColor); if (Utilities.isLmpOrAbove()) { - mDrawable.startTransition(DragView.COLOR_CHANGE_DURATION); animateTextColor(mHoverColor); } else { - mDrawable.startTransition(0); + if (mCurrentFilter == null) { + mCurrentFilter = new ColorMatrix(); + } + DragView.setColorScale(mHoverColor, mCurrentFilter); + mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter)); setTextColor(mHoverColor); } } @@ -124,12 +134,11 @@ public abstract class ButtonDropTarget extends TextView // Do nothing } - protected void resetHoverColor() { + protected void resetHoverColor() { if (Utilities.isLmpOrAbove()) { - mDrawable.reverseTransition(DragView.COLOR_CHANGE_DURATION); animateTextColor(mOriginalTextColor.getDefaultColor()); } else { - mDrawable.resetTransition(); + mDrawable.setColorFilter(null); setTextColor(mOriginalTextColor); } } @@ -139,8 +148,32 @@ public abstract class ButtonDropTarget extends TextView if (mCurrentColorAnim != null) { mCurrentColorAnim.cancel(); } - mCurrentColorAnim = ObjectAnimator.ofArgb(this, "textColor", targetColor); + + mCurrentColorAnim = new AnimatorSet(); mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION); + + if (mSrcFilter == null) { + mSrcFilter = new ColorMatrix(); + mDstFilter = new ColorMatrix(); + mCurrentFilter = new ColorMatrix(); + } + + DragView.setColorScale(getTextColor(), mSrcFilter); + DragView.setColorScale(targetColor, mDstFilter); + ValueAnimator anim1 = ValueAnimator.ofObject( + new FloatArrayEvaluator(mCurrentFilter.getArray()), + mSrcFilter.getArray(), mDstFilter.getArray()); + anim1.addUpdateListener(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter)); + invalidate(); + } + }); + + mCurrentColorAnim.play(anim1); + mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor)); mCurrentColorAnim.start(); } @@ -155,10 +188,10 @@ public abstract class ButtonDropTarget extends TextView } } - @Override + @Override public final void onDragStart(DragSource source, Object info, int dragAction) { mActive = supportsDrop(source, info); - mDrawable.resetTransition(); + mDrawable.setColorFilter(null); if (mCurrentColorAnim != null) { mCurrentColorAnim.cancel(); mCurrentColorAnim = null; diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 08186f517..fa6e74fd1 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -43,7 +43,7 @@ public class DeleteDropTarget extends ButtonDropTarget { // Get the hover color mHoverColor = getResources().getColor(R.color.delete_target_hover_tint); - setDrawable(R.drawable.remove_target_selector); + setDrawable(R.drawable.ic_launcher_remove_normal); } public static boolean supportsDrop(Object info) { diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index 120299e1d..b3323384d 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -38,7 +38,7 @@ import com.android.launcher3.util.Thunk; import java.util.Arrays; public class DragView extends View { - public static int COLOR_CHANGE_DURATION = 200; + public static int COLOR_CHANGE_DURATION = 120; @Thunk static float sDragAlpha = 1f; @@ -249,8 +249,7 @@ public class DragView extends View { m1.setSaturation(0); ColorMatrix m2 = new ColorMatrix(); - m2.setScale(Color.red(color) / 255f, Color.green(color) / 255f, - Color.blue(color) / 255f, Color.alpha(color) / 255f); + setColorScale(color, m2); m1.postConcat(m2); if (Utilities.isLmpOrAbove()) { @@ -355,4 +354,9 @@ public class DragView extends View { mDragLayer.removeView(DragView.this); } } + + public static void setColorScale(int color, ColorMatrix target) { + target.setScale(Color.red(color) / 255f, Color.green(color) / 255f, + Color.blue(color) / 255f, Color.alpha(color) / 255f); + } } diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index f3383ccea..0f139fa98 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -41,7 +41,7 @@ public class InfoDropTarget extends ButtonDropTarget { // Get the hover color mHoverColor = getResources().getColor(R.color.info_target_hover_tint); - setDrawable(R.drawable.info_target_selector); + setDrawable(R.drawable.ic_launcher_info_normal); } public static void startDetailsActivityForInfo(Object info, Launcher launcher) { diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 0fc8f324a..419939000 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -28,7 +28,7 @@ public class UninstallDropTarget extends ButtonDropTarget { // Get the hover color mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint); - setDrawable(R.drawable.uninstall_target_selector); + setDrawable(R.drawable.ic_launcher_uninstall_normal); } @Override -- cgit v1.2.3 From 4a6c6f31050a81068cff13fe0cc274f9be826d90 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 12:36:46 -0700 Subject: Preventing overdraw. Drawing background directly at the window, instead of the rootview. Change-Id: Ie974752e739d9acd1fbd765809c671f82f462644 --- res/layout-land/launcher.xml | 1 - res/layout-port/launcher.xml | 1 - res/layout-sw720dp/launcher.xml | 1 - src/com/android/launcher3/Launcher.java | 26 ++++++++++++++++++++------ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index f0f7bbb9f..113b319ff 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -21,7 +21,6 @@ android:id="@+id/launcher" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/workspace_bg" android:fitsSystemWindows="true"> Date: Thu, 21 May 2015 13:04:53 -0700 Subject: Load PackageItemInfo in background thread to prevent ANR - Decoupled widget model from widget view, and placed the creation to LauncherModel. - As a result packagemanager operation, iconcache retrieval is all done inside LauncherModel on background thread b/21311085 b/21325319 Change-Id: I294698527db58b89f3da558090a367530c058776 --- src/com/android/launcher3/BubbleTextView.java | 2 +- src/com/android/launcher3/IconCache.java | 2 +- src/com/android/launcher3/Launcher.java | 19 ++-- src/com/android/launcher3/LauncherModel.java | 16 ++- src/com/android/launcher3/Utilities.java | 9 ++ src/com/android/launcher3/WidgetPreviewLoader.java | 3 +- .../android/launcher3/model/PackageItemInfo.java | 58 ++++++++++ src/com/android/launcher3/model/WidgetsModel.java | 125 +++++++++++++++++++++ .../android/launcher3/widget/PackageItemInfo.java | 58 ---------- .../launcher3/widget/WidgetsContainerView.java | 22 +--- .../launcher3/widget/WidgetsListAdapter.java | 2 + src/com/android/launcher3/widget/WidgetsModel.java | 122 -------------------- 12 files changed, 221 insertions(+), 217 deletions(-) create mode 100644 src/com/android/launcher3/model/PackageItemInfo.java create mode 100644 src/com/android/launcher3/model/WidgetsModel.java delete mode 100644 src/com/android/launcher3/widget/PackageItemInfo.java delete mode 100644 src/com/android/launcher3/widget/WidgetsModel.java diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 3b3b9bf9f..d5300095b 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -38,7 +38,7 @@ import android.view.ViewParent; import android.widget.TextView; import com.android.launcher3.IconCache.IconLoadRequest; -import com.android.launcher3.widget.PackageItemInfo; +import com.android.launcher3.model.PackageItemInfo; /** * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 1fac5a1c2..a38757991 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -42,9 +42,9 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; -import com.android.launcher3.widget.PackageItemInfo; import java.util.Collection; import java.util.HashMap; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7b7b61795..59ca97865 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -102,6 +102,7 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -268,8 +269,9 @@ public class Launcher extends Activity // Main container view for the all apps screen. @Thunk AppsContainerView mAppsView; - // Main container view for the widget tray screen. - private WidgetsContainerView mWidgetsView; + // Main container view and the model for the widget tray screen. + @Thunk WidgetsContainerView mWidgetsView; + @Thunk WidgetsModel mWidgetsModel; private boolean mAutoAdvanceRunning = false; private AppWidgetHostView mQsb; @@ -4365,23 +4367,22 @@ public class Launcher extends Activity } } - @Thunk ArrayList mWidgetsAndShortcuts; private Runnable mBindPackagesUpdatedRunnable = new Runnable() { public void run() { - bindAllPackages(mWidgetsAndShortcuts); + bindAllPackages(mWidgetsModel); } }; @Override - public void bindAllPackages(final ArrayList widgetsAndShortcuts) { + public void bindAllPackages(final WidgetsModel model) { if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) { - mWidgetsAndShortcuts = widgetsAndShortcuts; + mWidgetsModel = model; return; } - if (mWidgetsView != null && widgetsAndShortcuts != null) { - mWidgetsView.addWidgets(widgetsAndShortcuts, getPackageManager()); - mWidgetsAndShortcuts = null; + if (mWidgetsView != null && model != null) { + mWidgetsView.addWidgets(model); + mWidgetsModel = null; } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3e05f57b9..500253826 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -58,6 +58,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.ManagedProfileHeuristic; @@ -203,7 +204,7 @@ public class LauncherModel extends BroadcastReceiver public void bindRestoreItemsChange(HashSet updates); public void bindComponentsRemoved(ArrayList packageNames, ArrayList appInfos, UserHandleCompat user, int reason); - public void bindAllPackages(ArrayList widgetsAndShortcuts); + public void bindAllPackages(WidgetsModel model); public void bindSearchablesChanged(); public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); @@ -3344,18 +3345,19 @@ public class LauncherModel extends BroadcastReceiver runOnWorkerThread(new Runnable(){ @Override public void run() { - final ArrayList list = getWidgetsAndShortcuts(context, refresh); + final WidgetsModel model = createWidgetsModel(context, refresh); mHandler.post(new Runnable() { @Override public void run() { Callbacks cb = getCallback(); if (callbacks == cb && cb != null) { - callbacks.bindAllPackages(list); + callbacks.bindAllPackages(model); } } }); // update the Widget entries inside DB on the worker thread. - LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(list); + LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews( + model.getRawList()); } }); } @@ -3365,13 +3367,15 @@ public class LauncherModel extends BroadcastReceiver * * @see #loadAndBindWidgetsAndShortcuts */ - private ArrayList getWidgetsAndShortcuts(Context context, boolean refresh) { + private WidgetsModel createWidgetsModel(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); - return widgetsAndShortcuts; + WidgetsModel model = new WidgetsModel(context); + model.addWidgetsAndShortcuts(widgetsAndShortcuts); + return model; } @Thunk static boolean isPackageDisabled(Context context, String packageName, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 6734fdc0d..1f8a6f2a5 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -44,6 +44,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PaintDrawable; import android.os.Build; +import android.os.Process; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -57,6 +58,8 @@ import java.util.Comparator; import java.util.regex.Matcher; import java.util.regex.Pattern; +import junit.framework.Assert; + /** * Various utilities shared amongst the Launcher's classes. */ @@ -641,4 +644,10 @@ public final class Utilities { return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) && (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); } + + public static void assertWorkerThread() { + if (LauncherAppState.isDogfoodBuild()) { + Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid()); + } + } } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index bbcdaae39..5ee1f2615 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -206,8 +206,7 @@ public class WidgetPreviewLoader { * This ensures that we remove entries for packages which changed while the launcher was dead. */ public void removeObsoletePreviews(ArrayList list) { - // This method should always be called from the worker thread. - Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid()); + Utilities.assertWorkerThread(); LongSparseArray userIdCache = new LongSparseArray<>(); LongSparseArray> validPackages = new LongSparseArray<>(); diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java new file mode 100644 index 000000000..0f0134ae3 --- /dev/null +++ b/src/com/android/launcher3/model/PackageItemInfo.java @@ -0,0 +1,58 @@ +/* + * 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.model; + +import android.content.ComponentName; +import android.graphics.Bitmap; + +import com.android.launcher3.ItemInfo; + +import java.util.Arrays; + +/** + * Represents a {@link Package} in the widget tray section. + */ +public class PackageItemInfo extends ItemInfo { + private static final String TAG = "PackageInfo"; + + /** + * A bitmap version of the application icon. + */ + public Bitmap iconBitmap; + + /** + * Indicates whether we're using a low res icon + */ + public boolean usingLowResIcon; + + public String packageName; + + int flags = 0; + + PackageItemInfo(String packageName) { + this.packageName = packageName; + } + + @Override + public String toString() { + return "PackageItemInfo(title=" + title + " id=" + this.id + + " type=" + this.itemType + " container=" + this.container + + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + + " user=" + user + ")"; + } +} diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java new file mode 100644 index 000000000..b72b98126 --- /dev/null +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -0,0 +1,125 @@ + +package com.android.launcher3.model; + +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.os.Handler; +import android.os.Process; +import android.util.Log; + +import com.android.launcher3.IconCache; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; + +import com.android.launcher3.LauncherModel; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.UserHandleCompat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Widgets data model that is used by the adapters of the widget views and controllers. + * + *

The widgets and shortcuts are organized using package name as its index. + */ +public class WidgetsModel { + + private static final String TAG = "WidgetsModel"; + private static final boolean DEBUG = false; + + /* List of packages that is tracked by this model. */ + private List mPackageItemInfos = new ArrayList<>(); + + /* Map of widgets and shortcuts that are tracked per package. */ + private Map> mWidgetsList = new HashMap<>(); + + private ArrayList mRawList; + + private final Comparator mWidgetAndShortcutNameComparator; + private final Comparator mAppNameComparator; + + private final IconCache mIconCache; + private final Handler mWorkerHandler; + + public WidgetsModel(Context context) { + mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); + mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); + mIconCache = LauncherAppState.getInstance().getIconCache(); + mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); + } + + // Access methods that may be deleted if the private fields are made package-private. + public int getPackageSize() { + return mPackageItemInfos.size(); + } + + // Access methods that may be deleted if the private fields are made package-private. + public PackageItemInfo getPackageItemInfo(int pos) { + return mPackageItemInfos.get(pos); + } + + public List getSortedWidgets(int pos) { + return mWidgetsList.get(mPackageItemInfos.get(pos)); + } + + public ArrayList getRawList() { + return mRawList; + } + + public void addWidgetsAndShortcuts(ArrayList rawWidgetsShortcuts) { + Utilities.assertWorkerThread(); + mRawList = rawWidgetsShortcuts; + if (DEBUG) { + Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size()); + } + + // Temporary list for {@link PackageItemInfos} to avoid having to go through + // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList} + HashMap tmpPackageItemInfos = new HashMap<>(); + + // clear the lists. + mWidgetsList.clear(); + mPackageItemInfos.clear(); + + // add and update. + for (Object o: rawWidgetsShortcuts) { + String packageName = ""; + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; + packageName = widgetInfo.provider.getPackageName(); + } else if (o instanceof ResolveInfo) { + ResolveInfo resolveInfo = (ResolveInfo) o; + packageName = resolveInfo.activityInfo.packageName; + } else { + Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s", + o.getClass().toString())); + } + + PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); + ArrayList widgetsShortcutsList = mWidgetsList.get(pInfo); + if (widgetsShortcutsList != null) { + widgetsShortcutsList.add(o); + } else { + widgetsShortcutsList = new ArrayList(); + widgetsShortcutsList.add(o); + pInfo = new PackageItemInfo(packageName); + mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), + true /* userLowResIcon */, pInfo); + mWidgetsList.put(pInfo, widgetsShortcutsList); + tmpPackageItemInfos.put(packageName, pInfo); + mPackageItemInfos.add(pInfo); + } + } + + // sort. + Collections.sort(mPackageItemInfos, mAppNameComparator); + for (PackageItemInfo p: mPackageItemInfos) { + Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator); + } + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java deleted file mode 100644 index 8f45a7754..000000000 --- a/src/com/android/launcher3/widget/PackageItemInfo.java +++ /dev/null @@ -1,58 +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.widget; - -import android.content.ComponentName; -import android.graphics.Bitmap; - -import com.android.launcher3.ItemInfo; - -import java.util.Arrays; - -/** - * Represents a {@link Package} in the widget tray section. - */ -public class PackageItemInfo extends ItemInfo { - private static final String TAG = "PackageInfo"; - - /** - * A bitmap version of the application icon. - */ - public Bitmap iconBitmap; - - /** - * Indicates whether we're using a low res icon - */ - public boolean usingLowResIcon; - - public String packageName; - - int flags = 0; - - PackageItemInfo(String packageName) { - this.packageName = packageName; - } - - @Override - public String toString() { - return "PackageItemInfo(title=" + title + " id=" + this.id - + " type=" + this.itemType + " container=" + this.container - + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY - + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) - + " user=" + user + ")"; - } -} diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 7a7895ff8..5a879faa0 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -17,7 +17,6 @@ package com.android.launcher3.widget; import android.content.Context; -import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -36,6 +35,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragController; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.Folder; import com.android.launcher3.IconCache; import com.android.launcher3.ItemInfo; @@ -47,8 +47,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.Workspace; -import java.util.ArrayList; - /** * The widgets list view container. */ @@ -68,9 +66,6 @@ public class WidgetsContainerView extends BaseContainerView private DragController mDragController; private IconCache mIconCache; - /* Data model for the widget */ - private WidgetsModel mWidgets; - /* Recycler view related member variables */ private RecyclerView mView; private WidgetsListAdapter mAdapter; @@ -98,8 +93,6 @@ public class WidgetsContainerView extends BaseContainerView mDragController = mLauncher.getDragController(); mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher); mAdapter = new WidgetsListAdapter(context, this, this, mLauncher); - mWidgets = new WidgetsModel(context, mAdapter); - mAdapter.setWidgetsModel(mWidgets); mIconCache = (LauncherAppState.getInstance()).getIconCache(); if (DEBUG) { @@ -109,10 +102,6 @@ public class WidgetsContainerView extends BaseContainerView @Override protected void onFinishInflate() { - if (DEBUG) { - Log.d(TAG, String.format("onFinishInflate [widgets size=%d]", - mWidgets.getPackageSize())); - } mView = (RecyclerView) findViewById(R.id.widgets_list_view); mView.setAdapter(mAdapter); @@ -145,10 +134,6 @@ public class WidgetsContainerView extends BaseContainerView public void scrollToTop() { mView.scrollToPosition(0); - if (DEBUG) { - Log.d(TAG, String.format("scrollToTop, [widgets size=%d]", - mWidgets.getPackageSize())); - } } // @@ -374,8 +359,9 @@ public class WidgetsContainerView extends BaseContainerView /** * Initialize the widget data model. */ - public void addWidgets(ArrayList widgetsShortcuts, PackageManager pm) { - mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm); + public void addWidgets(WidgetsModel model) { + mAdapter.setWidgetsModel(model); + mAdapter.notifyDataSetChanged(); } private WidgetPreviewLoader getWidgetPreviewLoader() { diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 3916c00d2..918ec1bda 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -38,6 +38,8 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; +import com.android.launcher3.model.PackageItemInfo; +import com.android.launcher3.model.WidgetsModel; import java.util.List; diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java deleted file mode 100644 index 5a920e8d4..000000000 --- a/src/com/android/launcher3/widget/WidgetsModel.java +++ /dev/null @@ -1,122 +0,0 @@ - -package com.android.launcher3.widget; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.support.v7.widget.RecyclerView; -import android.util.Log; - -import com.android.launcher3.IconCache; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.model.AppNameComparator; -import com.android.launcher3.model.WidgetsAndShortcutNameComparator; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Widgets data model that is used by the adapters of the widget views and controllers. - * - *

The widgets and shortcuts are organized using package name as its index. - */ -public class WidgetsModel { - - private static final String TAG = "WidgetsModel"; - private static final boolean DEBUG = false; - - /* List of packages that is tracked by this model. */ - private List mPackageItemInfos = new ArrayList<>(); - - /* Map of widgets and shortcuts that are tracked per package. */ - private Map> mWidgetsList = new HashMap<>(); - - /* Notifies the adapter when data changes. */ - private RecyclerView.Adapter mAdapter; - - private Comparator mWidgetAndShortcutNameComparator; - private Comparator mAppNameComparator; - - private IconCache mIconCache; - - public WidgetsModel(Context context, RecyclerView.Adapter adapter) { - mAdapter = adapter; - mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); - mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); - mIconCache = LauncherAppState.getInstance().getIconCache(); - } - - // Access methods that may be deleted if the private fields are made package-private. - public int getPackageSize() { - return mPackageItemInfos.size(); - } - - // Access methods that may be deleted if the private fields are made package-private. - public PackageItemInfo getPackageItemInfo(int pos) { - return mPackageItemInfos.get(pos); - } - - public List getSortedWidgets(int pos) { - return mWidgetsList.get(mPackageItemInfos.get(pos)); - } - - public void addWidgetsAndShortcuts(ArrayList widgetsShortcuts, PackageManager pm) { - if (DEBUG) { - Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size()); - } - - // Temporary list for {@link PackageItemInfos} to avoid having to go through - // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList} - HashMap tmpPackageItemInfos = new HashMap<>(); - - // clear the lists. - mWidgetsList.clear(); - mPackageItemInfos.clear(); - - // add and update. - for (Object o: widgetsShortcuts) { - String packageName = ""; - if (o instanceof LauncherAppWidgetProviderInfo) { - LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; - packageName = widgetInfo.provider.getPackageName(); - } else if (o instanceof ResolveInfo) { - ResolveInfo resolveInfo = (ResolveInfo) o; - packageName = resolveInfo.activityInfo.packageName; - } else { - Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s", - o.getClass().toString())); - } - - PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); - ArrayList widgetsShortcutsList = mWidgetsList.get(pInfo); - if (widgetsShortcutsList != null) { - widgetsShortcutsList.add(o); - } else { - widgetsShortcutsList = new ArrayList(); - widgetsShortcutsList.add(o); - - pInfo = new PackageItemInfo(packageName); - mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), - true /* useLowResIcon */, pInfo); - mWidgetsList.put(pInfo, widgetsShortcutsList); - tmpPackageItemInfos.put(packageName, pInfo); - mPackageItemInfos.add(pInfo); - } - } - - // sort. - Collections.sort(mPackageItemInfos, mAppNameComparator); - for (PackageItemInfo p: mPackageItemInfos) { - Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator); - } - - // notify. - mAdapter.notifyDataSetChanged(); - } -} \ No newline at end of file -- cgit v1.2.3 From 3abcd5ba1ba0fe4d8061309dd073e36c64b05963 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 20 May 2015 16:07:46 -0700 Subject: Cleaning up some app model code. - Preventing extra sorting when adding/updating apps - Preventing extra logic when filtering apps - Fixing overlapping prediction bar when all predictions are removed - Fixing crash when retrieving section names for AppInfos whose titles have been updated, but updateApps has not yet been called. Change-Id: I1da468b0fd5c5cc404b6a5e6146a268fefeca267 --- .../android/launcher3/AlphabeticalAppsList.java | 194 +++++++++++---------- src/com/android/launcher3/AppsContainerView.java | 4 +- 2 files changed, 102 insertions(+), 96 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 82aaeb99a..5e05d11bc 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -5,14 +5,10 @@ import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.model.AppNameComparator; -import java.text.Collator; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -121,10 +117,10 @@ public class AlphabeticalAppsList { } /** - * A callback to notify of changes to the filter. + * Callback to notify when the set of adapter items have changed. */ - public interface FilterChangedCallback { - void onFilterChanged(); + public interface AdapterChangedCallback { + void onAdapterItemsChanged(); } /** @@ -178,12 +174,20 @@ public class AlphabeticalAppsList { private static final int MAX_NUM_MERGES_PHONE = 2; private Context mContext; + + // The set of apps from the system not including predictions private List mApps = new ArrayList<>(); + // The set of filtered apps with the current filter private List mFilteredApps = new ArrayList<>(); - private List mSectionedFilteredApps = new ArrayList<>(); + // The current set of adapter items + private List mAdapterItems = new ArrayList<>(); + // The set of sections for the apps with the current filter private List mSections = new ArrayList<>(); + // The set of sections that we allow fast-scrolling to (includes non-merged sections) private List mFastScrollerSections = new ArrayList<>(); + // The set of predicted app component names private List mPredictedAppComponents = new ArrayList<>(); + // The set of predicted apps resolved from the component names and the current set of apps private List mPredictedApps = new ArrayList<>(); private HashMap mCachedSectionNames = new HashMap<>(); private RecyclerView.Adapter mAdapter; @@ -191,16 +195,16 @@ public class AlphabeticalAppsList { private AlphabeticIndexCompat mIndexer; private AppNameComparator mAppNameComparator; private MergeAlgorithm mMergeAlgorithm; - private FilterChangedCallback mFilterChangedCallback; + private AdapterChangedCallback mAdapterChangedCallback; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; - public AlphabeticalAppsList(Context context, FilterChangedCallback cb, int numAppsPerRow, + public AlphabeticalAppsList(Context context, AdapterChangedCallback auCb, int numAppsPerRow, int numPredictedAppsPerRow) { mContext = context; mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppNameComparator(context); - mFilterChangedCallback = cb; + mAdapterChangedCallback = auCb; setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow); } @@ -248,7 +252,7 @@ public class AlphabeticalAppsList { * Returns the current filtered list of applications broken down into their sections. */ public List getAdapterItems() { - return mSectionedFilteredApps; + return mAdapterItems; } /** @@ -278,11 +282,7 @@ public class AlphabeticalAppsList { public void setFilter(Filter f) { if (mFilter != f) { mFilter = f; - onAppsUpdated(); - mAdapter.notifyDataSetChanged(); - if (mFilterChangedCallback != null){ - mFilterChangedCallback.onFilterChanged(); - } + updateAdapterItems(); } } @@ -294,7 +294,6 @@ public class AlphabeticalAppsList { mPredictedAppComponents.clear(); mPredictedAppComponents.addAll(apps); onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } /** @@ -311,7 +310,6 @@ public class AlphabeticalAppsList { mApps.clear(); mApps.addAll(apps); onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } /** @@ -320,10 +318,9 @@ public class AlphabeticalAppsList { public void addApps(List apps) { // We add it in place, in alphabetical order for (AppInfo info : apps) { - addApp(info); + mApps.add(info); } onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } /** @@ -335,11 +332,10 @@ public class AlphabeticalAppsList { if (index != -1) { mApps.set(index, info); } else { - addApp(info); + mApps.add(info); } } onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } /** @@ -353,7 +349,6 @@ public class AlphabeticalAppsList { } } onAppsUpdated(); - mAdapter.notifyDataSetChanged(); } /** @@ -372,16 +367,6 @@ public class AlphabeticalAppsList { return -1; } - /** - * Implementation to actually add an app to the alphabetic list, but does not notify. - */ - private void addApp(AppInfo info) { - int index = Collections.binarySearch(mApps, info, mAppNameComparator.getAppInfoComparator()); - if (index < 0) { - mApps.add(-(index + 1), info); - } - } - /** * Updates internals when the set of apps are updated. */ @@ -389,42 +374,6 @@ public class AlphabeticalAppsList { // Sort the list of apps Collections.sort(mApps, mAppNameComparator.getAppInfoComparator()); - // Prepare to update the list of sections, filtered apps, etc. - mFilteredApps.clear(); - mSections.clear(); - mSectionedFilteredApps.clear(); - mFastScrollerSections.clear(); - SectionInfo lastSectionInfo = null; - String lastSectionName = null; - FastScrollSectionInfo lastFastScrollerSectionInfo = null; - int position = 0; - int appIndex = 0; - List allApps = new ArrayList<>(); - - - // Process the predicted app components - mPredictedApps.clear(); - if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { - for (ComponentName cn : mPredictedAppComponents) { - for (AppInfo info : mApps) { - if (cn.equals(info.componentName)) { - mPredictedApps.add(info); - break; - } - } - // Stop at the number of predicted apps - if (mPredictedApps.size() == mNumPredictedAppsPerRow) { - break; - } - } - - if (!mPredictedApps.isEmpty()) { - // Create a new spacer for the prediction bar - AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++); - mSectionedFilteredApps.add(sectionItem); - } - } - // As a special case for some languages (currently only Simplified Chinese), we may need to // coalesce sections Locale curLocale = mContext.getResources().getConfiguration().locale; @@ -436,11 +385,7 @@ public class AlphabeticalAppsList { sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator()); for (AppInfo info : mApps) { // Add the section to the cache - String sectionName = mCachedSectionNames.get(info.title); - if (sectionName == null) { - sectionName = mIndexer.computeSectionName(info.title); - mCachedSectionNames.put(info.title, sectionName); - } + String sectionName = getAndUpdateCachedSectionName(info.title); // Add it to the mapping ArrayList sectionApps = sectionMap.get(sectionName); @@ -451,31 +396,70 @@ public class AlphabeticalAppsList { sectionApps.add(info); } - // Add it to the list + // Add each of the section apps to the list in order + List allApps = new ArrayList<>(mApps.size()); for (Map.Entry> entry : sectionMap.entrySet()) { allApps.addAll(entry.getValue()); } + mApps = allApps; } else { // Just compute the section headers for use below for (AppInfo info : mApps) { // Add the section to the cache - String sectionName = mCachedSectionNames.get(info.title); - if (sectionName == null) { - sectionName = mIndexer.computeSectionName(info.title); - mCachedSectionNames.put(info.title, sectionName); + getAndUpdateCachedSectionName(info.title); + } + } + + // Recompose the set of adapter items from the current set of apps + updateAdapterItems(); + } + + /** + * Updates the set of filtered apps with the current filter. At this point, we expect + * mCachedSectionNames to have been calculated for the set of all apps in mApps. + */ + private void updateAdapterItems() { + SectionInfo lastSectionInfo = null; + String lastSectionName = null; + FastScrollSectionInfo lastFastScrollerSectionInfo = null; + int position = 0; + int appIndex = 0; + + // Prepare to update the list of sections, filtered apps, etc. + mFilteredApps.clear(); + mFastScrollerSections.clear(); + mAdapterItems.clear(); + mSections.clear(); + + // Process the predicted app components + mPredictedApps.clear(); + if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { + for (ComponentName cn : mPredictedAppComponents) { + for (AppInfo info : mApps) { + if (cn.equals(info.componentName)) { + mPredictedApps.add(info); + break; + } } + // Stop at the number of predicted apps + if (mPredictedApps.size() == mNumPredictedAppsPerRow) { + break; + } + } + + if (!mPredictedApps.isEmpty()) { + // Create a new spacer for the prediction bar + AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++); + mAdapterItems.add(sectionItem); } - // Add it to the list - allApps.addAll(mApps); } // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections - int numApps = allApps.size(); + int numApps = mApps.size(); for (int i = 0; i < numApps; i++) { - AppInfo info = allApps.get(i); - // The section name was computed above so this should be find - String sectionName = mCachedSectionNames.get(info.title); + AppInfo info = mApps.get(i); + String sectionName = getAndUpdateCachedSectionName(info.title); // Check if we want to retain this app if (mFilter != null && !mFilter.retainApp(info, sectionName)) { @@ -494,7 +478,7 @@ public class AlphabeticalAppsList { // Create a new section item to break the flow of items in the list if (!hasFilter()) { AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); - mSectionedFilteredApps.add(sectionItem); + mAdapterItems.add(sectionItem); } } @@ -505,12 +489,21 @@ public class AlphabeticalAppsList { lastSectionInfo.firstAppItem = appItem; lastFastScrollerSectionInfo.appItem = appItem; } - mSectionedFilteredApps.add(appItem); + mAdapterItems.add(appItem); mFilteredApps.add(info); } // Merge multiple sections together as requested by the merge strategy for this device mergeSections(); + + // Refresh the recycler view + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } + + if (mAdapterChangedCallback != null) { + mAdapterChangedCallback.onAdapterItemsChanged(); + } } /** @@ -531,20 +524,20 @@ public class AlphabeticalAppsList { SectionInfo nextSection = mSections.remove(i + 1); // Remove the next section break - mSectionedFilteredApps.remove(nextSection.sectionBreakItem); - int pos = mSectionedFilteredApps.indexOf(section.firstAppItem); + mAdapterItems.remove(nextSection.sectionBreakItem); + int pos = mAdapterItems.indexOf(section.firstAppItem); // Point the section for these new apps to the merged section int nextPos = pos + section.numApps; for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) { - AdapterItem item = mSectionedFilteredApps.get(j); + AdapterItem item = mAdapterItems.get(j); item.sectionInfo = section; item.sectionAppIndex += section.numApps; } // Update the following adapter items of the removed section item - pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem); - for (int j = pos; j < mSectionedFilteredApps.size(); j++) { - AdapterItem item = mSectionedFilteredApps.get(j); + pos = mAdapterItems.indexOf(nextSection.firstAppItem); + for (int j = pos; j < mAdapterItems.size(); j++) { + AdapterItem item = mAdapterItems.get(j); item.position--; } section.numApps += nextSection.numApps; @@ -560,4 +553,17 @@ public class AlphabeticalAppsList { } } } + + /** + * Returns the cached section name for the given title, recomputing and updating the cache if + * the title has no cached section name. + */ + private String getAndUpdateCachedSectionName(CharSequence title) { + String sectionName = mCachedSectionNames.get(title); + if (sectionName == null) { + sectionName = mIndexer.computeSectionName(title); + mCachedSectionNames.put(title, sectionName); + } + return sectionName; + } } diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 76f3c8861..612c19c0f 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -136,7 +136,7 @@ final class HeaderElevationControllerVL implements HeaderElevationController { */ public class AppsContainerView extends BaseContainerView implements DragSource, Insettable, TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, - AlphabeticalAppsList.FilterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks, + AlphabeticalAppsList.AdapterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, ViewTreeObserver.OnPreDrawListener { @@ -686,7 +686,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } @Override - public void onFilterChanged() { + public void onAdapterItemsChanged() { updatePredictionBarVisibility(); } -- cgit v1.2.3 From 5c97f51fd9443e461a360ff7852aa223c05d667f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 May 2015 16:03:28 -0700 Subject: Icons are not updated to TYPE_APPLICATION during restore > Use low res only for app shortcuts > Running icon migration after restore > Running icon migration again for all users > Deduping shortcuts added from widget tray Bug: 20945600 Change-Id: I3bb47545fdd9832510069026fbae8966d2311cc1 --- .../android/launcher3/InstallShortcutReceiver.java | 36 +++++++--------------- src/com/android/launcher3/Launcher.java | 7 ++--- .../launcher3/LauncherBackupAgentHelper.java | 1 + src/com/android/launcher3/LauncherModel.java | 6 ++-- src/com/android/launcher3/LauncherProvider.java | 13 ++++++-- src/com/android/launcher3/Utilities.java | 23 ++++++++++++-- 6 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 115598f7b..448cc1dfd 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -156,6 +156,16 @@ public class InstallShortcutReceiver extends BroadcastReceiver { queuePendingShortcutInfo(info, context); } + public static ShortcutInfo fromShortcutIntent(Context context, Intent data) { + PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context); + if (info.launchIntent == null || info.label == null) { + if (DBG) Log.e(TAG, "Invalid install shortcut intent"); + return null; + } + info = convertToLauncherActivityIfPossible(info); + return info.getShortcutInfo(); + } + static void queueInstallShortcut(LauncherActivityInfoCompat info, Context context) { queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context); } @@ -214,30 +224,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } } - /** - * Returns true if the intent is a valid launch intent for a shortcut. - * This is used to identify shortcuts which are different from the ones exposed by the - * applications' manifest file. - * - * When DISABLE_ALL_APPS is true, shortcuts exposed via the app's manifest should never be - * duplicated or removed(unless the app is un-installed). - * - * @param launchIntent The intent that will be launched when the shortcut is clicked. - */ - static boolean isValidShortcutLaunchIntent(Intent launchIntent) { - if (launchIntent != null - && Intent.ACTION_MAIN.equals(launchIntent.getAction()) - && launchIntent.getComponent() != null - && launchIntent.getCategories() != null - && launchIntent.getCategories().size() == 1 - && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) - && launchIntent.getExtras() == null - && TextUtils.isEmpty(launchIntent.getDataString())) { - return false; - } - return true; - } - /** * Ensures that we have a valid, non-null name. If the provided name is null, we will return * the application name instead. @@ -428,7 +414,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // Already an activity target return original; } - if (isValidShortcutLaunchIntent(original.launchIntent) + if (!Utilities.isLauncherAppTarget(original.launchIntent) || !original.user.equals(UserHandleCompat.myUserHandle())) { // We can only convert shortcuts which point to a main activity in the current user. return original; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 59ca97865..6697dc105 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -806,7 +806,7 @@ public class Launcher extends Activity } boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || - requestCode == REQUEST_CREATE_APPWIDGET || requestCode == REQUEST_CREATE_SHORTCUT); + requestCode == REQUEST_CREATE_APPWIDGET); final boolean workspaceLocked = isWorkspaceLocked(); // We have special handling for widgets @@ -1550,14 +1550,13 @@ public class Launcher extends Activity int[] touchXY = mPendingAddInfo.dropPos; CellLayout layout = getCellLayout(container, screenId); - boolean foundCellSpan = false; - - ShortcutInfo info = mModel.infoFromShortcutIntent(this, data); + ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data); if (info == null) { return; } final View view = createShortcut(info); + boolean foundCellSpan = false; // First we check if we already know the exact location where we want to add this item. if (cellX >= 0 && cellY >= 0) { cellXY[0] = cellX; diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 5f7173fac..e607a0faf 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -94,6 +94,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { // TODO: Update the backup set to include rank. if (mHelper.restoredBackupVersion <= 2) { LauncherAppState.getLauncherProvider().updateFolderItemsRank(); + LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities(); } } else { if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB"); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d994dc77c..1b9260290 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2027,7 +2027,7 @@ public class LauncherModel extends BroadcastReceiver "constructing info for partially restored package", true); info = getRestoredItemInfo(c, titleIndex, intent, - promiseType, useLowResIcon); + promiseType); intent = getRestoredItemIntent(c, context, intent); } else { // Don't restore items for other profiles. @@ -3402,10 +3402,10 @@ public class LauncherModel extends BroadcastReceiver * to a package that is not yet installed on the system. */ public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent, - int promiseType, boolean useLowResIcon) { + int promiseType) { final ShortcutInfo info = new ShortcutInfo(); info.user = UserHandleCompat.myUserHandle(); - mIconCache.getTitleAndIcon(info, intent, info.user, useLowResIcon); + mIconCache.getTitleAndIcon(info, intent, info.user, false /* useLowResIcon */); if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) { String title = (cursor != null) ? cursor.getString(titleIndex) : null; diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 8bc898855..8fef32daa 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -65,7 +65,7 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 25; + private static final int DATABASE_VERSION = 26; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -378,6 +378,11 @@ public class LauncherProvider extends ContentProvider { mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false); } + public void convertShortcutsToLauncherActivities() { + mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase()); + } + + public void deleteDatabase() { // Are you sure? (y/n) final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -623,10 +628,12 @@ public class LauncherProvider extends ContentProvider { } } case 23: - convertShortcutsToLauncherActivities(db); + // No-op case 24: ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext); - case 25: { + case 25: + convertShortcutsToLauncherActivities(db); + case 26: { // DB Upgraded successfully return; } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 1f8a6f2a5..9f47e138d 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -45,12 +45,15 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.PaintDrawable; import android.os.Build; import android.os.Process; +import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.view.View; import android.widget.Toast; +import junit.framework.Assert; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -58,8 +61,6 @@ import java.util.Comparator; import java.util.regex.Matcher; import java.util.regex.Pattern; -import junit.framework.Assert; - /** * Various utilities shared amongst the Launcher's classes. */ @@ -650,4 +651,22 @@ public final class Utilities { Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid()); } } + + /** + * Returns true if the intent is a valid launch intent for a launcher activity of an app. + * This is used to identify shortcuts which are different from the ones exposed by the + * applications' manifest file. + * + * @param launchIntent The intent that will be launched when the shortcut is clicked. + */ + public static boolean isLauncherAppTarget(Intent launchIntent) { + return launchIntent != null + && Intent.ACTION_MAIN.equals(launchIntent.getAction()) + && launchIntent.getComponent() != null + && launchIntent.getCategories() != null + && launchIntent.getCategories().size() == 1 + && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) + && launchIntent.getExtras() == null + && TextUtils.isEmpty(launchIntent.getDataString()); + } } -- cgit v1.2.3 From db0b572862b1c34ddee35fefb1197d9057717b6b Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 21 May 2015 14:39:54 -0700 Subject: Fixing crash in previous CL. - Shouldn't be referencing mApps before it is set. --- src/com/android/launcher3/AlphabeticalAppsList.java | 11 ++++++++--- src/com/android/launcher3/AppsContainerView.java | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 5e05d11bc..2505c803a 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -199,15 +199,20 @@ public class AlphabeticalAppsList { private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; - public AlphabeticalAppsList(Context context, AdapterChangedCallback auCb, int numAppsPerRow, - int numPredictedAppsPerRow) { + public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) { mContext = context; mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppNameComparator(context); - mAdapterChangedCallback = auCb; setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow); } + /** + * Sets the apps updated callback. + */ + public void setAppsUpdatedCallback(AdapterChangedCallback auCb) { + mAdapterChangedCallback = auCb; + } + /** * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. */ diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 612c19c0f..5661df648 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -210,7 +210,8 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mLayoutInflater = LayoutInflater.from(context); mNumAppsPerRow = grid.appsViewNumCols; mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols; - mApps = new AlphabeticalAppsList(context, this, mNumAppsPerRow, mNumPredictedAppsPerRow); + mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow); + mApps.setAppsUpdatedCallback(this); mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this); mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); mAdapter.setNumAppsPerRow(mNumAppsPerRow); -- cgit v1.2.3 From dd721f86330ccc593e5bd4274c5701eff72f6521 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 21 May 2015 15:31:42 -0700 Subject: Introduce scrollbar in widget tray b/21270754 Change-Id: I82dd87193f0ed5822f8cd75ef0c785057f949015 --- res/layout/widgets_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 9d9fa10c2..196dc45e5 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -39,6 +39,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/quantum_panel_dark" + android:scrollbars="vertical" android:elevation="15dp" android:visibility="gone" /> -- cgit v1.2.3 From 7eb9925491edadf871eb1cebfda1f9e5174d37c4 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 21 May 2015 15:45:24 -0700 Subject: Tweaking method name. Change-Id: I2f4f9dc58c8148059914a106526995e80a82376f --- src/com/android/launcher3/AlphabeticalAppsList.java | 4 ++-- src/com/android/launcher3/AppsContainerView.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 2505c803a..b75b3ef89 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -209,8 +209,8 @@ public class AlphabeticalAppsList { /** * Sets the apps updated callback. */ - public void setAppsUpdatedCallback(AdapterChangedCallback auCb) { - mAdapterChangedCallback = auCb; + public void setAdapterChangedCallback(AdapterChangedCallback cb) { + mAdapterChangedCallback = cb; } /** diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 5661df648..26a7cd761 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -211,7 +211,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mNumAppsPerRow = grid.appsViewNumCols; mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols; mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow); - mApps.setAppsUpdatedCallback(this); + mApps.setAdapterChangedCallback(this); mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this); mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); mAdapter.setNumAppsPerRow(mNumAppsPerRow); -- cgit v1.2.3 From 8abbd29ca320fe88d0043a51bc4a4f5914480b00 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 21 May 2015 15:54:58 -0700 Subject: Enabling setWallpaper button only if the image was loaded successfully Bug: 20187333 Change-Id: I1b738a06499d20facbc4bccb1e6247086a45b686 --- .../src/com/android/launcher3/WallpaperCropActivity.java | 1 + .../src/com/android/launcher3/WallpaperPickerActivity.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index ee0d8b0c6..f2bb50944 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -263,6 +263,7 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb if (req.postExecute != null) { req.postExecute.run(); } + mProgressView.setVisibility(View.GONE); } public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled, diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 72cb194d6..3f90203ed 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -196,13 +196,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(final WallpaperPickerActivity a) { a.setWallpaperButtonEnabled(false); - BitmapRegionTileSource.UriBitmapSource bitmapSource = + final BitmapRegionTileSource.UriBitmapSource bitmapSource = new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile)); a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() { @Override public void run() { - a.setWallpaperButtonEnabled(true); + if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + a.setWallpaperButtonEnabled(true); + } } }); } @@ -232,7 +234,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(final WallpaperPickerActivity a) { a.setWallpaperButtonEnabled(false); - BitmapRegionTileSource.ResourceBitmapSource bitmapSource = + final BitmapRegionTileSource.ResourceBitmapSource bitmapSource = new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId); a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() { @@ -249,7 +251,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void run() { - a.setWallpaperButtonEnabled(true); + if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + a.setWallpaperButtonEnabled(true); + } } }); } -- cgit v1.2.3 From 2e6da1539bc7286336b3c24d96ab76434939ce4d Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 6 May 2015 11:42:25 -0700 Subject: Refactoring DeviceProfile -> Pulling out the parts of device profile which can (and need to be) initialized and accessed without access to an Activity context, ie. the invariant bits. -> The invariant bits are stored in InvariantDeviceProfile which is initialized statically from LauncherAppState. -> The DeviceProfile contains the Activity context-dependent bits, and we will create one of these for each Activity instance, and this instance is accessed through the Launcher activity. -> It's possible that we can continue to refactor this such that all appropriate dimensions can be computed without an Activity context (by only specifying orientation). This would be an extension of this CL and allow us to know exactly how launcher will look in both orientations from any context. Sets the stage for some improvements around b/19514688 Change-Id: Ia7daccf14d8ca2b9cb340b8780b684769e9f1892 --- .../android/launcher3/AlphabeticalAppsList.java | 8 +- .../android/launcher3/AppWidgetResizeFrame.java | 4 +- .../launcher3/AppsContainerRecyclerView.java | 5 +- src/com/android/launcher3/AppsContainerView.java | 21 +- src/com/android/launcher3/AppsGridAdapter.java | 12 +- .../launcher3/AppsRecyclerViewContainer.java | 4 +- src/com/android/launcher3/AutoInstallsLayout.java | 4 +- src/com/android/launcher3/BubbleTextView.java | 2 +- src/com/android/launcher3/CellLayout.java | 23 +- src/com/android/launcher3/DeviceProfile.java | 245 +++---------------- src/com/android/launcher3/DynamicGrid.java | 108 --------- src/com/android/launcher3/FocusHelper.java | 35 +-- src/com/android/launcher3/Folder.java | 6 +- src/com/android/launcher3/FolderIcon.java | 10 +- src/com/android/launcher3/FolderPagedView.java | 8 +- src/com/android/launcher3/Hotseat.java | 14 +- src/com/android/launcher3/IconCache.java | 16 +- .../android/launcher3/InvariantDeviceProfile.java | 264 +++++++++++++++++++++ src/com/android/launcher3/Launcher.java | 38 ++- src/com/android/launcher3/LauncherAppState.java | 54 +---- .../launcher3/LauncherAppWidgetHostView.java | 4 + .../launcher3/LauncherAppWidgetProviderInfo.java | 59 +++-- .../android/launcher3/LauncherBackupHelper.java | 104 ++++---- src/com/android/launcher3/LauncherModel.java | 43 ++-- src/com/android/launcher3/LauncherProvider.java | 12 +- .../LauncherStateTransitionAnimation.java | 6 +- src/com/android/launcher3/Partner.java | 68 +++--- .../launcher3/PendingAppWidgetHostView.java | 14 +- .../launcher3/ShortcutAndWidgetContainer.java | 12 +- src/com/android/launcher3/Utilities.java | 15 ++ src/com/android/launcher3/WidgetPreviewLoader.java | 42 ++-- src/com/android/launcher3/Workspace.java | 26 +- .../WorkspaceStateTransitionAnimation.java | 2 +- src/com/android/launcher3/util/FocusLogic.java | 15 +- .../launcher3/widget/PendingAddWidgetInfo.java | 11 +- src/com/android/launcher3/widget/WidgetCell.java | 22 +- .../launcher3/widget/WidgetsContainerView.java | 6 +- .../launcher3/widget/WidgetsListAdapter.java | 13 +- util/com/android/launcher3/DecoderRing.java | 3 - 39 files changed, 646 insertions(+), 712 deletions(-) delete mode 100644 src/com/android/launcher3/DynamicGrid.java create mode 100644 src/com/android/launcher3/InvariantDeviceProfile.java diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index b75b3ef89..808bddbf3 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -173,7 +173,7 @@ public class AlphabeticalAppsList { private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3; private static final int MAX_NUM_MERGES_PHONE = 2; - private Context mContext; + private Launcher mLauncher; // The set of apps from the system not including predictions private List mApps = new ArrayList<>(); @@ -200,7 +200,7 @@ public class AlphabeticalAppsList { private int mNumPredictedAppsPerRow; public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) { - mContext = context; + mLauncher = (Launcher) context; mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppNameComparator(context); setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow); @@ -218,7 +218,7 @@ public class AlphabeticalAppsList { */ public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) { // Update the merge algorithm - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); if (grid.isPhone()) { mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f), MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); @@ -381,7 +381,7 @@ public class AlphabeticalAppsList { // As a special case for some languages (currently only Simplified Chinese), we may need to // coalesce sections - Locale curLocale = mContext.getResources().getConfiguration().locale; + Locale curLocale = mLauncher.getResources().getConfiguration().locale; TreeMap> sectionMap = null; boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE); if (localeRequiresSectionSorting) { diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index e6bf52531..ea7c22189 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -75,8 +75,8 @@ public class AppWidgetResizeFrame extends FrameLayout { mResizeMode = info.resizeMode; mDragLayer = dragLayer; - mMinHSpan = info.minSpanX; - mMinVSpan = info.minSpanY; + mMinHSpan = info.getMinSpanX(mLauncher); + mMinVSpan = info.getMinSpanY(mLauncher); setBackgroundResource(R.drawable.widget_resize_shadow); setForeground(getResources().getDrawable(R.drawable.widget_resize_frame)); diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index 31942b36d..109be7e02 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -80,6 +80,8 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { private Rect mBackgroundPadding = new Rect(); private ScrollPositionState mScrollPosState = new ScrollPositionState(); + private Launcher mLauncher; + public AppsContainerRecyclerView(Context context) { this(context, null); } @@ -96,6 +98,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { int defStyleRes) { super(context, attrs, defStyleAttr); + mLauncher = (Launcher) context; Resources res = context.getResources(); int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size); mScrollbar = res.getDrawable(R.drawable.apps_list_scrollbar_thumb); @@ -129,7 +132,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mNumAppsPerRow = numAppsPerRow; mNumPredictedAppsPerRow = numPredictedAppsPerRow; - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); RecyclerView.RecycledViewPool pool = getRecycledViewPool(); int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); pool.setMaxRecycledViews(AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1); diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 26a7cd761..9ff3bfabc 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -199,15 +199,18 @@ public class AppsContainerView extends BaseContainerView implements DragSource, public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); Resources res = context.getResources(); + mLauncher = (Launcher) context; + DeviceProfile grid = mLauncher.getDeviceProfile(); + mContainerInset = context.getResources().getDimensionPixelSize( R.dimen.apps_container_inset); mPredictionBarHeight = grid.allAppsCellHeightPx + 2 * res.getDimensionPixelSize(R.dimen.apps_prediction_icon_top_bottom_padding); - mLauncher = (Launcher) context; + mLayoutInflater = LayoutInflater.from(context); + mNumAppsPerRow = grid.appsViewNumCols; mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols; mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow); @@ -219,6 +222,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mLayoutManager = mAdapter.getLayoutManager(); mItemDecoration = mAdapter.getItemDecoration(); mContentMarginStart = mAdapter.getContentMarginStart(); + mApps.setAdapter(mAdapter); } @@ -410,7 +414,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, protected void onFixedBoundsUpdated() { // Update the number of items in the grid LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) { mNumAppsPerRow = grid.appsViewNumCols; mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols; @@ -448,7 +452,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } // Update the apps recycler view, inset it by the container inset as well - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int startMargin = grid.isPhone() ? mContentMarginStart : 0; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; if (isRtl) { @@ -565,8 +569,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, @Override public float getIntrinsicIconScaleFactor() { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); return (float) grid.allAppsIconSizePx / grid.iconSizePx; } @@ -760,7 +763,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, */ private boolean handleTouchEvent(MotionEvent ev) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int x = (int) ev.getX(); int y = (int) ev.getY(); @@ -883,7 +886,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, */ private void showSearchField() { // Show the search bar and focus the search - final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP, + final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, getContext().getResources().getDisplayMetrics()); mSearchBarContainerView.setVisibility(View.VISIBLE); mSearchBarContainerView.setAlpha(0f); @@ -913,7 +916,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, */ private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; - final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP, + final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, getContext().getResources().getDisplayMetrics()); if (animated) { // Hide the search bar and focus the recycler view diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 580930cf2..9c1c46b80 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -106,6 +106,11 @@ class AppsGridAdapter extends RecyclerView.Adapter { private HashMap mCachedSectionBounds = new HashMap<>(); private Rect mTmpBounds = new Rect(); + private Launcher mLauncher; + + public GridItemDecoration(Context context) { + mLauncher = (Launcher) context; + } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { @@ -113,7 +118,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { return; } - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); List items = mApps.getAdapterItems(); boolean hasDrawnPredictedAppsDivider = false; int childCount = parent.getChildCount(); @@ -294,7 +299,6 @@ class AppsGridAdapter extends RecyclerView.Adapter { @Thunk Paint mSectionTextPaint; @Thunk Paint mPredictedAppsDividerPaint; - public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener, View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) { @@ -307,7 +311,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL, false); mGridLayoutMgr.setSpanSizeLookup(mGridSizer); - mItemDecoration = new GridItemDecoration(); + mItemDecoration = new GridItemDecoration(context); mLayoutInflater = LayoutInflater.from(context); mTouchListener = touchListener; mIconClickListener = iconClickListener; @@ -323,7 +327,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { mSectionTextPaint.setAntiAlias(true); mPredictedAppsDividerPaint = new Paint(); - mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1f, res.getDisplayMetrics())); + mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics())); mPredictedAppsDividerPaint.setColor(0x1E000000); mPredictedAppsDividerPaint.setAntiAlias(true); } diff --git a/src/com/android/launcher3/AppsRecyclerViewContainer.java b/src/com/android/launcher3/AppsRecyclerViewContainer.java index cf4beca28..6411bace4 100644 --- a/src/com/android/launcher3/AppsRecyclerViewContainer.java +++ b/src/com/android/launcher3/AppsRecyclerViewContainer.java @@ -38,8 +38,8 @@ public class AppsRecyclerViewContainer extends FrameLayout implements BubbleText public AppsRecyclerViewContainer(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + Launcher launcher = (Launcher) context; + DeviceProfile grid = launcher.getDeviceProfile(); mTouchFeedbackView = new ClickShadowView(context); diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index dac79a8ef..20c9314c9 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -78,7 +78,7 @@ public class AutoInstallsLayout { static AutoInstallsLayout get(Context context, String pkg, Resources targetRes, AppWidgetHost appWidgetHost, LayoutParserCallback callback) { - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + InvariantDeviceProfile grid = LauncherAppState.getInstance().getInvariantDeviceProfile(); // Try with grid size and hotseat count String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT, @@ -165,7 +165,7 @@ public class AutoInstallsLayout { LayoutParserCallback callback, Resources res, int layoutId, String rootTag) { this(context, appWidgetHost, callback, res, layoutId, rootTag, - LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().hotseatAllAppsRank); + LauncherAppState.getInstance().getInvariantDeviceProfile().hotseatAllAppsRank); } public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost, diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index d5300095b..edf502112 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -93,7 +93,7 @@ public class BubbleTextView extends TextView { public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = ((Launcher) context).getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 1d211bff1..27f1ac611 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -199,8 +199,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { setClipToPadding(false); mLauncher = (Launcher) context; - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0); mCellWidth = mCellHeight = -1; @@ -208,8 +207,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mWidthGap = mOriginalWidthGap = 0; mHeightGap = mOriginalHeightGap = 0; mMaxGap = Integer.MAX_VALUE; - mCountX = (int) grid.numColumns; - mCountY = (int) grid.numRows; + mCountX = (int) grid.inv.numColumns; + mCountY = (int) grid.inv.numRows; mOccupied = new boolean[mCountX][mCountY]; mTmpOccupied = new boolean[mCountX][mCountY]; mPreviousReorderDirection[0] = INVALID_DIRECTION; @@ -499,8 +498,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { int previewOffset = FolderRingAnimator.sPreviewSize; // The folder outer / inner ring image(s) - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); for (int i = 0; i < mFolderOuterRings.size(); i++) { FolderRingAnimator fra = mFolderOuterRings.get(i); @@ -841,8 +839,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); @@ -2729,18 +2726,18 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { * @param height Height in pixels * @param result An array of length 2 in which to store the result (may be null). */ - public static int[] rectToCell(int width, int height, int[] result) { + public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = launcher.getDeviceProfile(); Rect padding = grid.getWorkspacePadding(grid.isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); // Always assume we're working with the smallest span to make sure we // reserve enough space in both orientations. int parentWidth = grid.calculateCellWidth(grid.widthPx - - padding.left - padding.right, (int) grid.numColumns); + - padding.left - padding.right, (int) grid.inv.numColumns); int parentHeight = grid.calculateCellHeight(grid.heightPx - - padding.top - padding.bottom, (int) grid.numRows); + - padding.top - padding.bottom, (int) grid.inv.numRows); int smallerSize = Math.min(parentWidth, parentHeight); // Always round up to next largest cell @@ -2773,7 +2770,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { info.spanX = info.spanY = 1; return; } - int[] spans = rectToCell(minWidth, minHeight, null); + int[] spans = rectToCell(mLauncher, minWidth, minHeight, null); info.spanX = spans[0]; info.spanY = spans[1]; } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index ad0afd97b..8ab58b9c0 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -27,6 +27,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.Display; import android.view.Gravity; import android.view.Surface; @@ -44,42 +45,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; - -class DeviceProfileQuery { - DeviceProfile profile; - float widthDps; - float heightDps; - float value; - PointF dimens; - - DeviceProfileQuery(DeviceProfile p, float v) { - widthDps = p.minWidthDps; - heightDps = p.minHeightDps; - value = v; - dimens = new PointF(widthDps, heightDps); - profile = p; - } -} - public class DeviceProfile { + public static interface DeviceProfileCallbacks { public void onAvailableSizeChanged(DeviceProfile grid); } - String name; - float minWidthDps; - float minHeightDps; - public int numRows; - public int numColumns; - public int numFolderRows; - public int numFolderColumns; - float numHotseatIcons; - float iconSize; - private float iconTextSize; + public final InvariantDeviceProfile inv; private int iconDrawablePaddingOriginalPx; - private float hotseatIconSize; - - int defaultLayoutId; boolean isLandscape; public boolean isTablet; @@ -107,11 +80,7 @@ public class DeviceProfile { public int cellWidthPx; public int cellHeightPx; - public int iconSizePx; - public int iconTextSizePx; int iconDrawablePaddingPx; - int allAppsIconSizePx; - int allAppsIconTextSizePx; int allAppsCellWidthPx; int allAppsCellHeightPx; int allAppsCellPaddingPx; @@ -123,7 +92,6 @@ public class DeviceProfile { int hotseatCellHeightPx; int hotseatIconSizePx; int hotseatBarHeightPx; - int hotseatAllAppsRank; int allAppsNumRows; int allAppsNumCols; int appsViewNumCols; @@ -133,6 +101,11 @@ public class DeviceProfile { int pageIndicatorHeightPx; int allAppsButtonVisualSize; + int iconSizePx; + public int iconTextSizePx; + int allAppsIconSizePx; + int allAppsIconTextSizePx; + float dragViewScale; int allAppsShortEdgeCount = -1; @@ -140,46 +113,26 @@ public class DeviceProfile { private ArrayList mCallbacks = new ArrayList(); - DeviceProfile(String n, float w, float h, - int r, int c, int fr, int fc, - float is, float its, float hs, float his, int dlId) { - // Ensure that we have an odd number of hotseat items (since we need to place all apps) - if (hs % 2 == 0) { - throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); - } - - name = n; - minWidthDps = w; - minHeightDps = h; + public DeviceProfile(Context context, InvariantDeviceProfile inv) { + // Determine the dynamic grid properties + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); - numRows = r; - numColumns = c; - numFolderRows = fr; - numFolderColumns = fc; + Point realSize = new Point(); + display.getRealSize(realSize); + DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); - iconSize = is; - iconTextSize = its; - numHotseatIcons = hs; - hotseatIconSize = his; - defaultLayoutId = dlId; + this.inv = inv; + init(context, realSize.x, realSize.y, dm.widthPixels, dm.heightPixels); } - DeviceProfile() { - } - - DeviceProfile(Context context, - ArrayList profiles, - float minWidth, float minHeight, - int wPx, int hPx, - int awPx, int ahPx, - Resources res) { + private void init(Context context, int wPx, int hPx, int awPx, int ahPx) { + Resources res = context.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); - ArrayList points = - new ArrayList(); + transposeLayoutWithOrientation = res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); - minWidthDps = minWidth; - minHeightDps = minHeight; ComponentName cn = new ComponentName(context.getPackageName(), this.getClass().getName()); @@ -205,56 +158,14 @@ public class DeviceProfile { overviewModeScaleFactor = res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f; - // Find the closes profile given the width/height - for (DeviceProfile p : profiles) { - points.add(new DeviceProfileQuery(p, 0f)); - } - DeviceProfile closestProfile = findClosestDeviceProfile(minWidth, minHeight, points); - - // Snap to the closest row count - numRows = closestProfile.numRows; - - // Snap to the closest column count - numColumns = closestProfile.numColumns; - - numFolderRows = closestProfile.numFolderRows; - numFolderColumns = closestProfile.numFolderColumns; - - // Snap to the closest hotseat size - numHotseatIcons = closestProfile.numHotseatIcons; - hotseatAllAppsRank = (int) (numHotseatIcons / 2); - - // Snap to the closest default layout id - defaultLayoutId = closestProfile.defaultLayoutId; - - // Interpolate the icon size - points.clear(); - for (DeviceProfile p : profiles) { - points.add(new DeviceProfileQuery(p, p.iconSize)); - } - iconSize = invDistWeightedInterpolate(minWidth, minHeight, points); - - // AllApps uses the original non-scaled icon size - allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm); - - // Interpolate the icon text size - points.clear(); - for (DeviceProfile p : profiles) { - points.add(new DeviceProfileQuery(p, p.iconTextSize)); - } - iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points); iconDrawablePaddingOriginalPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); + // AllApps uses the original non-scaled icon text size - allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm); + allAppsIconTextSizePx = Utilities.pxFromDp(inv.iconTextSize, dm); - // Interpolate the hotseat icon size - points.clear(); - for (DeviceProfile p : profiles) { - points.add(new DeviceProfileQuery(p, p.hotseatIconSize)); - } - // Hotseat - hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points); + // AllApps uses the original non-scaled icon size + allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm); // If the partner customization apk contains any grid overrides, apply them applyPartnerDeviceProfileOverrides(context, dm); @@ -273,22 +184,7 @@ public class DeviceProfile { private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) { Partner p = Partner.get(ctx.getPackageManager()); if (p != null) { - DeviceProfile partnerDp = p.getDeviceProfileOverride(dm); - if (partnerDp != null) { - if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) { - numRows = numFolderRows = partnerDp.numRows; - numColumns = numFolderColumns = partnerDp.numColumns; - } - if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) { - allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount; - allAppsLongEdgeCount = partnerDp.allAppsLongEdgeCount; - } - if (partnerDp.iconSize > 0) { - iconSize = partnerDp.iconSize; - // AllApps uses the original non-scaled icon size - allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm); - } - } + p.applyDeviceProfileOverrides(this); } } @@ -361,7 +257,7 @@ public class DeviceProfile { float scale = 1f; int drawablePadding = iconDrawablePaddingOriginalPx; updateIconSize(1f, drawablePadding, resources, dm); - float usedHeight = (cellHeightPx * numRows); + float usedHeight = (cellHeightPx * inv.numRows); Rect workspacePadding = getWorkspacePadding(); int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom); @@ -379,10 +275,10 @@ public class DeviceProfile { private void updateIconSize(float scale, int drawablePadding, Resources res, DisplayMetrics dm) { - iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale); - iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale); + iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale); + iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); iconDrawablePaddingPx = drawablePadding; - hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale); + hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale); // Search Bar searchBarSpaceWidthPx = Math.min(widthPx, @@ -468,74 +364,6 @@ public class DeviceProfile { updateAvailableDimensions(context); } - @Thunk float dist(PointF p0, PointF p1) { - return (float) Math.hypot(p1.x - p0.x, p1.y - p0.y); - } - - private float weight(PointF a, PointF b, - float pow) { - float d = dist(a, b); - if (d == 0f) { - return Float.POSITIVE_INFINITY; - } - return (float) (1f / Math.pow(d, pow)); - } - - /** Returns the closest device profile given the width and height and a list of profiles */ - private DeviceProfile findClosestDeviceProfile(float width, float height, - ArrayList points) { - return findClosestDeviceProfiles(width, height, points).get(0).profile; - } - - /** Returns the closest device profiles ordered by closeness to the specified width and height */ - private ArrayList findClosestDeviceProfiles(float width, float height, - ArrayList points) { - final PointF xy = new PointF(width, height); - - // Sort the profiles by their closeness to the dimensions - ArrayList pointsByNearness = points; - Collections.sort(pointsByNearness, new Comparator() { - public int compare(DeviceProfileQuery a, DeviceProfileQuery b) { - return (int) (dist(xy, a.dimens) - dist(xy, b.dimens)); - } - }); - - return pointsByNearness; - } - - private float invDistWeightedInterpolate(float width, float height, - ArrayList points) { - float sum = 0; - float weights = 0; - float pow = 5; - float kNearestNeighbors = 3; - final PointF xy = new PointF(width, height); - - ArrayList pointsByNearness = findClosestDeviceProfiles(width, height, - points); - - for (int i = 0; i < pointsByNearness.size(); ++i) { - DeviceProfileQuery p = pointsByNearness.get(i); - if (i < kNearestNeighbors) { - float w = weight(xy, p.dimens, pow); - if (w == Float.POSITIVE_INFINITY) { - return p.value; - } - weights += w; - } - } - - for (int i = 0; i < pointsByNearness.size(); ++i) { - DeviceProfileQuery p = pointsByNearness.get(i); - if (i < kNearestNeighbors) { - float w = weight(xy, p.dimens, pow); - sum += w * p.value / weights; - } - } - - return sum; - } - /** Returns the search bar top offset */ int getSearchBarTopOffset() { if (isTablet() && !isVerticalBarLayout()) { @@ -571,7 +399,7 @@ public class DeviceProfile { // XXX: If the icon size changes across orientations, we will have to take // that into account here too. int gap = (int) ((width - 2 * edgeMarginPx - - (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); + (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1))); bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(), availableWidthPx - (edgeMarginPx + gap), searchBarSpaceHeightPx); @@ -620,6 +448,7 @@ public class DeviceProfile { Rect getWorkspacePadding() { return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); } + Rect getWorkspacePadding(int orientation) { Rect searchBarBounds = getSearchBarBounds(orientation); Rect padding = new Rect(); @@ -646,10 +475,10 @@ public class DeviceProfile { : Math.min(widthPx, heightPx); int paddingTop = searchBarBounds.bottom; int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx; - int availableWidth = Math.max(0, width - (int) ((numColumns * cellWidthPx) + - (numColumns * gapScale * cellWidthPx))); + int availableWidth = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) + + (inv.numColumns * gapScale * cellWidthPx))); int availableHeight = Math.max(0, height - paddingTop - paddingBottom - - (int) (2 * numRows * cellHeightPx)); + - (int) (2 * inv.numRows * cellHeightPx)); padding.set(availableWidth / 2, paddingTop + availableHeight / 2, availableWidth / 2, paddingBottom + availableHeight / 2); } else { @@ -701,10 +530,10 @@ public class DeviceProfile { } } - int calculateCellWidth(int width, int countX) { + public static int calculateCellWidth(int width, int countX) { return width / countX; } - int calculateCellHeight(int height, int countY) { + public static int calculateCellHeight(int height, int countY) { return height / countY; } diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java deleted file mode 100644 index d22427f44..000000000 --- a/src/com/android/launcher3/DynamicGrid.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2008 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.Context; -import android.content.res.Resources; -import android.util.DisplayMetrics; -import android.util.TypedValue; - -import java.util.ArrayList; - - -public class DynamicGrid { - @SuppressWarnings("unused") - private static final String TAG = "DynamicGrid"; - - private DeviceProfile mProfile; - private float mMinWidth; - private float mMinHeight; - - // This is a static that we use for the default icon size on a 4/5-inch phone - static float DEFAULT_ICON_SIZE_DP = 60; - static float DEFAULT_ICON_SIZE_PX = 0; - - public static float dpiFromPx(int size, DisplayMetrics metrics){ - float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; - return (size / densityRatio); - } - public static int pxFromDp(float size, DisplayMetrics metrics) { - return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - size, metrics)); - } - public static int pxFromSp(float size, DisplayMetrics metrics) { - return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, - size, metrics)); - } - - public DynamicGrid(Context context, Resources resources, - int minWidthPx, int minHeightPx, - int widthPx, int heightPx, - int awPx, int ahPx) { - DisplayMetrics dm = resources.getDisplayMetrics(); - ArrayList deviceProfiles = - new ArrayList(); - DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm); - // Our phone profiles include the bar sizes in each orientation - deviceProfiles.add(new DeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Short Stubby", - 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Stubby", - 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Nexus 4", - 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Nexus 5", - 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); - deviceProfiles.add(new DeviceProfile("Large Phone", - 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); - // The tablet profile is odd in that the landscape orientation - // also includes the nav bar on the side - deviceProfiles.add(new DeviceProfile("Nexus 7", - 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); - // Larger tablet profiles always have system bars on the top & bottom - deviceProfiles.add(new DeviceProfile("Nexus 10", - 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); - deviceProfiles.add(new DeviceProfile("20-inch Tablet", - 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); - mMinWidth = dpiFromPx(minWidthPx, dm); - mMinHeight = dpiFromPx(minHeightPx, dm); - mProfile = new DeviceProfile(context, deviceProfiles, - mMinWidth, mMinHeight, - widthPx, heightPx, - awPx, ahPx, - resources); - } - - public DeviceProfile getDeviceProfile() { - return mProfile; - } - - public String toString() { - return "-------- DYNAMIC GRID ------- \n" + - "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps + - ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx + - " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns + - ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx + - ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx + - ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]"; - } -} diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 678ed0f82..fe50e3a4e 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -92,11 +92,13 @@ public class FocusHelper { final int pageIndex = pagedView.indexOfChild(cellLayout); final int pageCount = pagedView.getPageCount(); + Launcher launcher = (Launcher) v.getContext(); int[][] matrix = FocusLogic.createSparseMatrix(cellLayout); // Process focus. - int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, - iconIndex, pageIndex, pageCount); + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, + countY, matrix, iconIndex, pageIndex, pageCount, + launcher.getDeviceProfile().isLayoutRtl); if (newIconIndex == FocusLogic.NOOP) { handleNoopKey(keyCode, v); return consume; @@ -184,7 +186,8 @@ public class FocusHelper { return consume; } - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile profile = ((Launcher) v.getContext()).getDeviceProfile(); + if (DEBUG) { Log.v(TAG, String.format( "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s", @@ -248,8 +251,8 @@ public class FocusHelper { } // Process the focus. - int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, - iconIndex, pageIndex, pageCount); + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, + countY, matrix, iconIndex, pageIndex, pageCount, profile.isLayoutRtl); View newIcon = null; if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) { @@ -283,8 +286,8 @@ public class FocusHelper { return consume; } - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile profile = app.getDynamicGrid().getDeviceProfile(); + Launcher launcher = (Launcher) v.getContext(); + DeviceProfile profile = launcher.getDeviceProfile(); if (DEBUG) { Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s", @@ -295,9 +298,9 @@ public class FocusHelper { ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent(); CellLayout iconLayout = (CellLayout) parent.getParent(); final Workspace workspace = (Workspace) iconLayout.getParent(); - final ViewGroup launcher = (ViewGroup) workspace.getParent(); - final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar); - final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat); + final ViewGroup dragLayer = (ViewGroup) workspace.getParent(); + final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.search_drop_target_bar); + final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat); final int iconIndex = parent.indexOfChild(v); final int pageIndex = workspace.indexOfChild(iconLayout); @@ -331,8 +334,8 @@ public class FocusHelper { } // Process the focus. - int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix, - iconIndex, pageIndex, pageCount); + int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, + countY, matrix, iconIndex, pageIndex, pageCount, profile.isLayoutRtl); View newIcon = null; switch (newIconIndex) { case FocusLogic.NOOP: @@ -354,8 +357,8 @@ public class FocusHelper { iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, iconLayout.getCountX(), row); - newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, - FocusLogic.PIVOT, newPageIndex, pageCount); + newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, + matrix, FocusLogic.PIVOT, newPageIndex, pageCount, profile.isLayoutRtl); newIcon = parent.getChildAt(newIconIndex); } break; @@ -387,8 +390,8 @@ public class FocusHelper { workspace.snapToPage(newPageIndex); iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row); - newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix, - FocusLogic.PIVOT, newPageIndex, pageCount); + newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, + matrix, FocusLogic.PIVOT, newPageIndex, pageCount, profile.isLayoutRtl); newIcon = parent.getChildAt(newIconIndex); } break; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index d0a7ba3f8..72dc1e94a 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -945,8 +945,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2); int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2); @@ -1003,8 +1002,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } private int getContentAreaHeight() { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); int maxContentAreaHeight = grid.availableHeightPx - diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index b161b1cd3..8652eef40 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -147,8 +147,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " + "is dependent on this"); } - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + + DeviceProfile grid = launcher.getDeviceProfile(); FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false); icon.setClipToPadding(false); @@ -217,8 +217,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { + Thread.currentThread()); } - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = launcher.getDeviceProfile(); sPreviewSize = grid.folderIconSizePx; sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding); sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); @@ -490,8 +489,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void computePreviewDrawingParams(int drawableSize, int totalSize) { if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); mIntrinsicIconSize = drawableSize; mTotalWidth = totalSize; diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 06ed58895..0bd6501ed 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -80,9 +80,9 @@ public class FolderPagedView extends PagedView { super(context, attrs); LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mMaxCountX = (int) grid.numFolderColumns; - mMaxCountY = (int) grid.numFolderRows; + InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); + mMaxCountX = (int) profile.numFolderColumns; + mMaxCountY = (int) profile.numFolderRows; mMaxItemsPerPage = mMaxCountX * mMaxCountY; @@ -229,7 +229,7 @@ public class FolderPagedView extends PagedView { } private CellLayout createAndAddNewPage() { - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile(); CellLayout page = new CellLayout(getContext()); page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index b8337b6a4..1c1342cfe 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -54,10 +54,7 @@ public class Hotseat extends FrameLayout { r.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); mIsLandscape = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - } - - public void setup(Launcher launcher) { - mLauncher = launcher; + mLauncher = (Launcher) context; } CellLayout getLayout() { @@ -108,15 +105,14 @@ public class Hotseat extends FrameLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); - mAllAppsButtonRank = grid.hotseatAllAppsRank; + mAllAppsButtonRank = grid.inv.hotseatAllAppsRank; mContent = (CellLayout) findViewById(R.id.layout); if (grid.isLandscape && !grid.isLargeTablet()) { - mContent.setGridSize(1, (int) grid.numHotseatIcons); + mContent.setGridSize(1, (int) grid.inv.numHotseatIcons); } else { - mContent.setGridSize((int) grid.numHotseatIcons, 1); + mContent.setGridSize((int) grid.inv.numHotseatIcons, 1); } mContent.setIsHotseat(true); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 8b5f747f2..0c91a7113 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -94,7 +94,7 @@ public class IconCache { private final Handler mWorkerHandler; - public IconCache(Context context) { + public IconCache(Context context, InvariantDeviceProfile inv) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); @@ -373,20 +373,6 @@ public class IconCache { mCache.clear(); } - /** - * Empty out the cache that aren't of the correct grid size - */ - public synchronized void flushInvalidIcons(DeviceProfile grid) { - Iterator> it = mCache.entrySet().iterator(); - while (it.hasNext()) { - final CacheEntry e = it.next().getValue(); - if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx - || e.icon.getHeight() < grid.iconSizePx)) { - it.remove(); - } - } - } - /** * Fetches high-res icon for the provided ItemInfo and updates the caller when done. * @return a request ID that can be used to cancel the request. diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java new file mode 100644 index 000000000..fcd6d60f6 --- /dev/null +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -0,0 +1,264 @@ +/* + * 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.Context; +import android.graphics.Point; +import android.graphics.PointF; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Display; +import android.view.WindowManager; + +import com.android.launcher3.util.Thunk; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +public class InvariantDeviceProfile { + private static final String TAG = "InvariantDeviceProfile"; + + // This is a static that we use for the default icon size on a 4/5-inch phone + static float DEFAULT_ICON_SIZE_DP = 60; + + + static ArrayList sDeviceProfiles = + new ArrayList(); + static { + sDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby", + 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby", + 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby", + 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Stubby", + 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Nexus S", + 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4", + 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5", + 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + sDeviceProfiles.add(new InvariantDeviceProfile("Large Phone", + 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); + // The tablet profile is odd in that the landscape orientation + // also includes the nav bar on the side + sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7", + 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); + // Larger tablet profiles always have system bars on the top & bottom + sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10", + 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); + sDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet", + 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); + } + + class DeviceProfileQuery { + InvariantDeviceProfile profile; + float widthDps; + float heightDps; + float value; + PointF dimens; + + DeviceProfileQuery(InvariantDeviceProfile p, float v) { + widthDps = p.minWidthDps; + heightDps = p.minHeightDps; + value = v; + dimens = new PointF(widthDps, heightDps); + profile = p; + } + } + + // Profile-defining invariant properties + String name; + float minWidthDps; + float minHeightDps; + public int numRows; + public int numColumns; + public int numFolderRows; + public int numFolderColumns; + float iconSize; + float iconTextSize; + float numHotseatIcons; + float hotseatIconSize; + int defaultLayoutId; + + // Derived invariant properties + int hotseatAllAppsRank; + + InvariantDeviceProfile() { + } + + InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, + float is, float its, float hs, float his, int dlId) { + // Ensure that we have an odd number of hotseat items (since we need to place all apps) + if (hs % 2 == 0) { + throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); + } + + name = n; + minWidthDps = w; + minHeightDps = h; + numRows = r; + numColumns = c; + numFolderRows = fr; + numFolderColumns = fc; + iconSize = is; + iconTextSize = its; + numHotseatIcons = hs; + hotseatIconSize = his; + defaultLayoutId = dlId; + } + + InvariantDeviceProfile(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); + + Point smallestSize = new Point(); + Point largestSize = new Point(); + display.getCurrentSizeRange(smallestSize, largestSize); + + minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); + minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm); + + ArrayList points = + new ArrayList(); + + // Find the closes profile given the width/height + for (InvariantDeviceProfile p : sDeviceProfiles) { + points.add(new DeviceProfileQuery(p, 0f)); + } + + InvariantDeviceProfile closestProfile = + findClosestDeviceProfile(minWidthDps, minHeightDps, points); + + // The following properties are inherited directly from the nearest archetypal profile + numRows = closestProfile.numRows; + numColumns = closestProfile.numColumns; + numHotseatIcons = closestProfile.numHotseatIcons; + hotseatAllAppsRank = (int) (numHotseatIcons / 2); + defaultLayoutId = closestProfile.defaultLayoutId; + numFolderRows = closestProfile.numFolderRows; + numFolderColumns = closestProfile.numFolderColumns; + + + // The following properties are interpolated based on proximity to nearby archetypal + // profiles + points.clear(); + for (InvariantDeviceProfile p : sDeviceProfiles) { + points.add(new DeviceProfileQuery(p, p.iconSize)); + } + iconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points); + points.clear(); + for (InvariantDeviceProfile p : sDeviceProfiles) { + points.add(new DeviceProfileQuery(p, p.iconTextSize)); + } + iconTextSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points); + points.clear(); + for (InvariantDeviceProfile p : sDeviceProfiles) { + points.add(new DeviceProfileQuery(p, p.hotseatIconSize)); + } + hotseatIconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points); + + // If the partner customization apk contains any grid overrides, apply them + // Supported overrides: numRows, numColumns, iconSize + applyPartnerDeviceProfileOverrides(context, dm); + } + + /** + * Apply any Partner customization grid overrides. + * + * Currently we support: all apps row / column count. + */ + private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) { + Partner p = Partner.get(ctx.getPackageManager()); + if (p != null) { + p.applyInvariantDeviceProfileOverrides(this, dm); + } + } + + @Thunk float dist(PointF p0, PointF p1) { + return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) + + (p1.y-p0.y)*(p1.y-p0.y)); + } + + private float weight(PointF a, PointF b, + float pow) { + float d = dist(a, b); + if (d == 0f) { + return Float.POSITIVE_INFINITY; + } + return (float) (1f / Math.pow(d, pow)); + } + + /** Returns the closest device profile given the width and height and a list of profiles */ + private InvariantDeviceProfile findClosestDeviceProfile(float width, float height, + ArrayList points) { + return findClosestDeviceProfiles(width, height, points).get(0).profile; + } + + /** Returns the closest device profiles ordered by closeness to the specified width and height */ + private ArrayList findClosestDeviceProfiles(float width, float height, + ArrayList points) { + final PointF xy = new PointF(width, height); + + // Sort the profiles by their closeness to the dimensions + ArrayList pointsByNearness = points; + Collections.sort(pointsByNearness, new Comparator() { + public int compare(DeviceProfileQuery a, DeviceProfileQuery b) { + return (int) (dist(xy, a.dimens) - dist(xy, b.dimens)); + } + }); + + return pointsByNearness; + } + + private float invDistWeightedInterpolate(float width, float height, + ArrayList points) { + float sum = 0; + float weights = 0; + float pow = 5; + float kNearestNeighbors = 3; + final PointF xy = new PointF(width, height); + + ArrayList pointsByNearness = findClosestDeviceProfiles(width, height, + points); + + for (int i = 0; i < pointsByNearness.size(); ++i) { + DeviceProfileQuery p = pointsByNearness.get(i); + if (i < kNearestNeighbors) { + float w = weight(xy, p.dimens, pow); + if (w == Float.POSITIVE_INFINITY) { + return p.value; + } + weights += w; + } + } + + for (int i = 0; i < pointsByNearness.size(); ++i) { + DeviceProfileQuery p = pointsByNearness.get(i); + if (i < kNearestNeighbors) { + float w = weight(xy, p.dimens, pow); + sum += w * p.value / weights; + } + } + + return sum; + } +} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a305d101d..6c7373907 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -430,8 +430,9 @@ public class Launcher extends Activity LauncherAppState app = LauncherAppState.getInstance(); LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this); - // Lazy-initialize the dynamic grid - mDeviceProfile = app.initDynamicGrid(this); + // Load configuration-specific DeviceProfile + mDeviceProfile = new DeviceProfile(this, app.getInvariantDeviceProfile()); + mDeviceProfile.addCallback(LauncherAppState.getInstance()); // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), @@ -439,7 +440,7 @@ public class Launcher extends Activity mIsSafeModeEnabled = getPackageManager().isSafeMode(); mModel = app.setLauncher(this); mIconCache = app.getIconCache(); - mIconCache.flushInvalidIcons(mDeviceProfile); + mDragController = new DragController(this); mInflater = getLayoutInflater(); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this); @@ -1415,7 +1416,6 @@ public class Launcher extends Activity // Setup the hotseat mHotseat = (Hotseat) findViewById(R.id.hotseat); if (mHotseat != null) { - mHotseat.setup(this); mHotseat.setOnLongClickListener(this); } @@ -1601,31 +1601,21 @@ public class Launcher extends Activity } } - static int[] getSpanForWidget(Context context, ComponentName component, int minWidth, - int minHeight) { - Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null); + private int[] getSpanForWidget(ComponentName component, int minWidth, int minHeight) { + Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(this, component, null); // We want to account for the extra amount of padding that we are adding to the widget // to ensure that it gets the full amount of space that it has requested int requiredWidth = minWidth + padding.left + padding.right; int requiredHeight = minHeight + padding.top + padding.bottom; - return CellLayout.rectToCell(requiredWidth, requiredHeight, null); - } - - static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) { - return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight); + return CellLayout.rectToCell(this, requiredWidth, requiredHeight, null); } - static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) { - return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight); + public int[] getSpanForWidget(AppWidgetProviderInfo info) { + return getSpanForWidget(info.provider, info.minWidth, info.minHeight); } - static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) { - return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight); - } - - static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) { - return getSpanForWidget(context, info.componentName, info.minResizeWidth, - info.minResizeHeight); + public int[] getMinSpanForWidget(AppWidgetProviderInfo info) { + return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight); } /** @@ -1916,6 +1906,10 @@ public class Launcher extends Activity return mSharedPrefs; } + public DeviceProfile getDeviceProfile() { + return mDeviceProfile; + } + public void closeSystemDialogs() { getWindow().closeAllPanels(); @@ -4021,7 +4015,7 @@ public class Launcher extends Activity // Note: This assumes that the id remap broadcast is received before this step. // If that is not the case, the id remap will be ignored and user may see the // click to setup view. - PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null); + PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null); pendingInfo.spanX = item.spanX; pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index f540eb47d..93753a201 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -57,7 +57,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private static LauncherAppState INSTANCE; - private DynamicGrid mDynamicGrid; + private InvariantDeviceProfile mInvariantDeviceProfile; private LauncherAccessibilityDelegate mAccessibilityDelegate; public static LauncherAppState getInstance() { @@ -96,8 +96,9 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { // set sIsScreenXLarge and mScreenDensity *before* creating icon cache mIsScreenLarge = isScreenLarge(sContext.getResources()); mScreenDensity = sContext.getResources().getDisplayMetrics().density; - mIconCache = new IconCache(sContext); - mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache); + mInvariantDeviceProfile = new InvariantDeviceProfile(sContext); + mIconCache = new IconCache(sContext, mInvariantDeviceProfile); + mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache); mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class)); @@ -172,49 +173,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return LauncherFiles.SHARED_PREFERENCES_KEY; } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - DeviceProfile initDynamicGrid(Context context) { - mDynamicGrid = createDynamicGrid(context, mDynamicGrid); - mDynamicGrid.getDeviceProfile().addCallback(this); - return mDynamicGrid.getDeviceProfile(); - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - static DynamicGrid createDynamicGrid(Context context, DynamicGrid dynamicGrid) { - // Determine the dynamic grid properties - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - - Point realSize = new Point(); - display.getRealSize(realSize); - DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); - - if (dynamicGrid == null) { - Point smallestSize = new Point(); - Point largestSize = new Point(); - display.getCurrentSizeRange(smallestSize, largestSize); - - dynamicGrid = new DynamicGrid(context, - context.getResources(), - Math.min(smallestSize.x, smallestSize.y), - Math.min(largestSize.x, largestSize.y), - realSize.x, realSize.y, - dm.widthPixels, dm.heightPixels); - } - - // Update the icon size - DeviceProfile grid = dynamicGrid.getDeviceProfile(); - grid.updateFromConfiguration(context, context.getResources(), - realSize.x, realSize.y, - dm.widthPixels, dm.heightPixels); - return dynamicGrid; - } - - public DynamicGrid getDynamicGrid() { - return mDynamicGrid; - } - public WidgetPreviewLoader getWidgetCache() { return mWidgetCache; } @@ -251,6 +209,10 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return result; } + public InvariantDeviceProfile getInvariantDeviceProfile() { + return mInvariantDeviceProfile; + } + @Override public void onAvailableSizeChanged(DeviceProfile grid) { Utilities.setIconSize(grid.iconSizePx); diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 954d2d711..71fb2d295 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -152,6 +152,10 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc return info; } + public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() { + return (LauncherAppWidgetProviderInfo) getAppWidgetInfo(); + } + @Override public void onTouchComplete() { if (!mLongPressHelper.hasPerformedLongPress()) { diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index e19bc9534..7ca4fe325 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -18,10 +18,11 @@ import android.os.Parcel; public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { public boolean isCustomWidget = false; - public int spanX = -1; - public int spanY = -1; - public int minSpanX = -1; - public int minSpanY = -1; + + private int mSpanX = -1; + private int mSpanY = -1; + private int mMinSpanX = -1; + private int mMinSpanY = -1; public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context, AppWidgetProviderInfo info) { @@ -35,15 +36,6 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { p.setDataPosition(0); LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p); p.recycle(); - - int[] minResizeSpan = Launcher.getMinSpanForWidget(context, lawpi); - int[] span = Launcher.getSpanForWidget(context, lawpi); - - lawpi.spanX = span[0]; - lawpi.spanY = span[1]; - lawpi.minSpanX = minResizeSpan[0]; - lawpi.minSpanY = minResizeSpan[1]; - return lawpi; } @@ -60,11 +52,6 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { previewImage = widget.getPreviewImage(); initialLayout = widget.getWidgetLayout(); resizeMode = widget.getResizeMode(); - - spanX = widget.getSpanX(); - spanY = widget.getSpanY(); - minSpanX = widget.getMinSpanX(); - minSpanY = widget.getMinSpanY(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @@ -87,7 +74,39 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { if (isCustomWidget) { return "WidgetProviderInfo(" + provider + ")"; } - return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)", - provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY); + return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s", + provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm)); + } + + public int getSpanX(Launcher launcher) { + lazyLoadSpans(launcher); + return mSpanX; + } + + public int getSpanY(Launcher launcher) { + lazyLoadSpans(launcher); + return mSpanY; + } + + public int getMinSpanX(Launcher launcher) { + lazyLoadSpans(launcher); + return mMinSpanX; + } + + public int getMinSpanY(Launcher launcher) { + lazyLoadSpans(launcher); + return mMinSpanY; + } + + private void lazyLoadSpans(Launcher launcher) { + if (mSpanX < 0 || mSpanY < 0 || mMinSpanX < 0 || mMinSpanY < 0) { + int[] minResizeSpan = launcher.getMinSpanForWidget(this); + int[] span = launcher.getSpanForWidget(this); + + mSpanX = span[0]; + mSpanY = span[1]; + mMinSpanX = minResizeSpan[0]; + mMinSpanY = minResizeSpan[1]; + } } } diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 92bbb4019..af4101221 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -141,13 +141,15 @@ public class LauncherBackupHelper implements BackupHelper { private final ItemTypeMatcher[] mItemTypeMatchers; private final long mUserSerial; - private IconCache mIconCache; private BackupManager mBackupManager; private byte[] mBuffer = new byte[512]; private long mLastBackupRestoreTime; private boolean mBackupDataWasUpdated; - private DeviceProfieData mCurrentProfile; + private LauncherAppState mLauncherAppState; + private IconCache mIconCache; + private DeviceProfieData mDeviceProfileData; + boolean restoreSuccessful; int restoredBackupVersion = 1; @@ -197,10 +199,14 @@ public class LauncherBackupHelper implements BackupHelper { Journal in = readJournal(oldState); if (!launcherIsReady()) { + dataChanged(); // Perform backup later. writeJournal(newState, in); return; } + + lazyInitAppState(true /* noCreate */); + Log.v(TAG, "lastBackupTime = " + in.t); mKeys.clear(); applyJournal(in); @@ -296,12 +302,7 @@ public class LauncherBackupHelper implements BackupHelper { if (!restoreSuccessful) { return; } - if (!initializeIconCache()) { - // During restore we do not need an initialized instance of IconCache. We can create - // a temporary icon cache here, as the process will be rebooted after restore - // is complete. - mIconCache = new IconCache(mContext); - } + lazyInitAppState(false /* noCreate */); int dataSize = data.size(); if (mBuffer.length < dataSize) { @@ -395,19 +396,34 @@ public class LauncherBackupHelper implements BackupHelper { * @return the current device profile information. */ private DeviceProfieData getDeviceProfieData() { - if (mCurrentProfile != null) { - return mCurrentProfile; + return mDeviceProfileData; + } + + private void lazyInitAppState(boolean noCreate) { + if (mLauncherAppState != null) { + return; + } + + if (noCreate) { + mLauncherAppState = LauncherAppState.getInstanceNoCreate(); + } else { + LauncherAppState.setApplicationContext(mContext); + mLauncherAppState = LauncherAppState.getInstance(); } - final Context applicationContext = mContext.getApplicationContext(); - DeviceProfile profile = LauncherAppState.createDynamicGrid(applicationContext, null) - .getDeviceProfile(); - - mCurrentProfile = new DeviceProfieData(); - mCurrentProfile.desktopRows = profile.numRows; - mCurrentProfile.desktopCols = profile.numColumns; - mCurrentProfile.hotseatCount = profile.numHotseatIcons; - mCurrentProfile.allappsRank = profile.hotseatAllAppsRank; - return mCurrentProfile; + + mIconCache = mLauncherAppState.getIconCache(); + InvariantDeviceProfile profile = mLauncherAppState.getInvariantDeviceProfile(); + + mDeviceProfileData = initDeviceProfileData(profile); + } + + private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) { + DeviceProfieData data = new DeviceProfieData(); + data.desktopRows = profile.numRows; + data.desktopCols = profile.numColumns; + data.hotseatCount = profile.numHotseatIcons; + data.allappsRank = profile.hotseatAllAppsRank; + return data; } /** @@ -518,11 +534,6 @@ public class LauncherBackupHelper implements BackupHelper { */ private void backupIcons(BackupDataOutput data) throws IOException { // persist icons that haven't been persisted yet - if (!initializeIconCache()) { - dataChanged(); // try again later - if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying icon backup"); - return; - } final ContentResolver cr = mContext.getContentResolver(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); @@ -619,16 +630,9 @@ public class LauncherBackupHelper implements BackupHelper { */ private void backupWidgets(BackupDataOutput data) throws IOException { // persist static widget info that hasn't been persisted yet - final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); - if (appState == null || !initializeIconCache()) { - Log.w(TAG, "Failed to get icon cache during restore"); - return; - } final ContentResolver cr = mContext.getContentResolver(); - final WidgetPreviewLoader previewLoader = appState.getWidgetCache(); + final WidgetPreviewLoader previewLoader = mLauncherAppState.getWidgetCache(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; - final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile(); - if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx); int backupWidgetCount = 0; String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND " @@ -661,8 +665,7 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "saving widget " + backupKey); UserHandleCompat user = UserHandleCompat.myUserHandle(); writeRowToBackup(key, - packWidget(dpi, previewLoader,spanX * profile.cellWidthPx, - mIconCache, provider, user), + packWidget(dpi, previewLoader, mIconCache, provider, user), data); mKeys.add(key); backupWidgetCount ++; @@ -969,8 +972,7 @@ public class LauncherBackupHelper implements BackupHelper { } /** Serialize a widget for persistence, including a checksum wrapper. */ - private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, - int previewWidth, IconCache iconCache, + private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache, ComponentName provider, UserHandleCompat user) { final LauncherAppWidgetProviderInfo info = LauncherModel.getProviderInfo(mContext, provider, user); @@ -985,12 +987,6 @@ public class LauncherBackupHelper implements BackupHelper { widget.icon.data = Utilities.flattenBitmap(icon); widget.icon.dpi = dpi; } - if (info.previewImage != 0) { - widget.preview = new Resource(); - Bitmap preview = previewLoader.generateWidgetPreview(info, previewWidth, null); - widget.preview.data = Utilities.flattenBitmap(preview); - widget.preview.dpi = dpi; - } return widget; } @@ -1136,25 +1132,6 @@ public class LauncherBackupHelper implements BackupHelper { return wrapper.payload; } - private boolean initializeIconCache() { - if (mIconCache != null) { - return true; - } - - final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); - if (appState == null) { - if (DEBUG) { - Throwable stackTrace = new Throwable(); - stackTrace.fillInStackTrace(); - Log.w(TAG, "Failed to get app state during backup/restore", stackTrace); - } - return false; - } - mIconCache = appState.getIconCache(); - return mIconCache != null; - } - - /** * @return true if the launcher is in a state to support backup */ @@ -1167,9 +1144,8 @@ public class LauncherBackupHelper implements BackupHelper { } cursor.close(); - if (!initializeIconCache()) { + if (LauncherAppState.getInstanceNoCreate() == null) { // launcher services are unavailable, try again later - dataChanged(); return false; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 1b9260290..8d321e668 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -422,9 +422,9 @@ public class LauncherModel extends BroadcastReceiver private static boolean findNextAvailableIconSpaceInScreen(ArrayList occupiedPos, int[] xy, int spanX, int spanY) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - final int xCount = (int) grid.numColumns; - final int yCount = (int) grid.numRows; + InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); + final int xCount = (int) profile.numColumns; + final int yCount = (int) profile.numRows; boolean[][] occupied = new boolean[xCount][yCount]; if (occupiedPos != null) { for (ItemInfo r : occupiedPos) { @@ -1663,9 +1663,9 @@ public class LauncherModel extends BroadcastReceiver // check & update map of what's occupied; used to discard overlapping/invalid items private boolean checkItemPlacement(LongArrayMap occupied, ItemInfo item) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - final int countX = (int) grid.numColumns; - final int countY = (int) grid.numRows; + InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); + final int countX = (int) profile.numColumns; + final int countY = (int) profile.numRows; long containerIndex = item.screenId; if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { @@ -1681,10 +1681,10 @@ public class LauncherModel extends BroadcastReceiver final ItemInfo[][] hotseatItems = occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT); - if (item.screenId >= grid.numHotseatIcons) { + if (item.screenId >= profile.numHotseatIcons) { Log.e(TAG, "Error loading shortcut " + item + " into hotseat position " + item.screenId - + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1) + + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1) + ")"); return false; } @@ -1702,7 +1702,7 @@ public class LauncherModel extends BroadcastReceiver return true; } } else { - final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1]; + final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1]; items[(int) item.screenId][0] = item; occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items); return true; @@ -1776,9 +1776,9 @@ public class LauncherModel extends BroadcastReceiver new IntentFilter(StartupReceiver.SYSTEM_READY)) != null; LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - int countX = (int) grid.numColumns; - int countY = (int) grid.numRows; + InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); + int countX = (int) profile.numColumns; + int countY = (int) profile.numRows; if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) { Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true); @@ -2190,13 +2190,6 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, provider.provider); - if (!customWidget) { - int[] minSpan = - Launcher.getMinSpanForWidget(context, provider); - appWidgetInfo.minSpanX = minSpan[0]; - appWidgetInfo.minSpanY = minSpan[1]; - } - int status = restoreStatus; if (!wasProviderReady) { // If provider was not previously ready, update the @@ -2244,12 +2237,6 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo.spanX = c.getInt(spanXIndex); appWidgetInfo.spanY = c.getInt(spanYIndex); - if (!customWidget) { - int[] minSpan = Launcher.getMinSpanForWidget(context, provider); - appWidgetInfo.minSpanX = minSpan[0]; - appWidgetInfo.minSpanY = minSpan[1]; - } - container = c.getInt(containerIndex); if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP && container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) { @@ -2512,13 +2499,13 @@ public class LauncherModel extends BroadcastReceiver * right) */ private void sortWorkspaceItemsSpatially(ArrayList workspaceItems) { final LauncherAppState app = LauncherAppState.getInstance(); - final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + final InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); // XXX: review this Collections.sort(workspaceItems, new Comparator() { @Override public int compare(ItemInfo lhs, ItemInfo rhs) { - int cellCountX = (int) grid.numColumns; - int cellCountY = (int) grid.numRows; + int cellCountX = (int) profile.numColumns; + int cellCountY = (int) profile.numRows; int screenOffset = cellCountX * cellCountY; int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset + diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 8fef32daa..27511527d 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -364,7 +364,7 @@ public class LauncherProvider extends ContentProvider { private DefaultLayoutParser getDefaultLayoutParser() { int defaultLayout = LauncherAppState.getInstance() - .getDynamicGrid().getDeviceProfile().defaultLayoutId; + .getInvariantDeviceProfile().defaultLayoutId; return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost, mOpenHelper, getContext().getResources(), defaultLayout); } @@ -1042,10 +1042,10 @@ public class LauncherProvider extends ContentProvider { int curY = 0; final LauncherAppState app = LauncherAppState.getInstance(); - final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - final int width = (int) grid.numColumns; - final int height = (int) grid.numRows; - final int hotseatWidth = (int) grid.numHotseatIcons; + final InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); + final int width = (int) profile.numColumns; + final int height = (int) profile.numRows; + final int hotseatWidth = (int) profile.numHotseatIcons; final HashSet seenIntents = new HashSet(c.getCount()); @@ -1187,7 +1187,7 @@ public class LauncherProvider extends ContentProvider { int hotseatX = hotseat.keyAt(idx); ContentValues values = hotseat.valueAt(idx); - if (hotseatX == grid.hotseatAllAppsRank) { + if (hotseatX == profile.hotseatAllAppsRank) { // let's drop this in the next available hole in the hotseat while (++hotseatX < hotseatWidth) { if (hotseat.get(hotseatX) == null) { diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 324375444..ce13da66e 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -156,8 +156,7 @@ public class LauncherStateTransitionAnimation { } @Override public float getMaterialRevealViewStartFinalRadius() { - int allAppsButtonSize = LauncherAppState.getInstance(). - getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; + int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize; return allAppsButtonSize / 2; } @Override @@ -456,8 +455,7 @@ public class LauncherStateTransitionAnimation { } @Override float getMaterialRevealViewStartFinalRadius() { - int allAppsButtonSize = LauncherAppState.getInstance(). - getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; + int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize; return allAppsButtonSize / 2; } @Override diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java index e1913193b..c9e837994 100644 --- a/src/com/android/launcher3/Partner.java +++ b/src/com/android/launcher3/Partner.java @@ -116,56 +116,70 @@ public class Partner { return resId != 0 && getResources().getBoolean(resId); } - public DeviceProfile getDeviceProfileOverride(DisplayMetrics dm) { - boolean containsProfileOverrides = false; - - DeviceProfile dp = new DeviceProfile(); - - // We initialize customizable fields to be invalid - dp.numRows = -1; - dp.numColumns = -1; - dp.allAppsShortEdgeCount = -1; - dp.allAppsLongEdgeCount = -1; + public void applyInvariantDeviceProfileOverrides(InvariantDeviceProfile inv, DisplayMetrics dm) { + int numRows = -1; + int numColumns = -1; + float iconSize = -1; try { int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS, "integer", getPackageName()); if (resId > 0) { - containsProfileOverrides = true; - dp.numRows = getResources().getInteger(resId); + numRows = getResources().getInteger(resId); } resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS, "integer", getPackageName()); if (resId > 0) { - containsProfileOverrides = true; - dp.numColumns = getResources().getInteger(resId); + numColumns = getResources().getInteger(resId); } - resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT, - "integer", getPackageName()); + resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP, + "dimen", getPackageName()); if (resId > 0) { - containsProfileOverrides = true; - dp.allAppsShortEdgeCount = getResources().getInteger(resId); + int px = getResources().getDimensionPixelSize(resId); + iconSize = Utilities.dpiFromPx(px, dm); } + } catch (Resources.NotFoundException ex) { + Log.e(TAG, "Invalid Partner grid resource!", ex); + return; + } - resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT, + if (numRows > 0 && numColumns > 0) { + inv.numRows = numRows; + inv.numColumns = numColumns; + } + + if (iconSize > 0) { + inv.iconSize = iconSize; + } + } + + public void applyDeviceProfileOverrides(DeviceProfile dp) { + int allAppsShortEdgeCount = -1; + int allAppsLongEdgeCount = -1; + + try { + int resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT, "integer", getPackageName()); if (resId > 0) { - containsProfileOverrides = true; - dp.allAppsLongEdgeCount = getResources().getInteger(resId); + allAppsShortEdgeCount = getResources().getInteger(resId); } - resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP, - "dimen", getPackageName()); + resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT, + "integer", getPackageName()); if (resId > 0) { - containsProfileOverrides = true; - int px = getResources().getDimensionPixelSize(resId); - dp.iconSize = DynamicGrid.dpiFromPx(px, dm); + allAppsLongEdgeCount = getResources().getInteger(resId); } + } catch (Resources.NotFoundException ex) { Log.e(TAG, "Invalid Partner grid resource!", ex); + return; + } + + if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) { + dp.allAppsShortEdgeCount = allAppsShortEdgeCount; + dp.allAppsLongEdgeCount = allAppsLongEdgeCount; } - return containsProfileOverrides ? dp : null; } } diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index 179c60a98..c8b27efd3 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -42,6 +42,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen private final int mStartState; private final Intent mIconLookupIntent; private final boolean mDisabledForSafeMode; + private Launcher mLauncher; private Bitmap mIcon; @@ -56,6 +57,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, boolean disabledForSafeMode) { super(context); + + mLauncher = (Launcher) context; mInfo = info; mStartState = info.restoreStatus; mIconLookupIntent = new Intent().setComponent(info.providerName); @@ -64,7 +67,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen mPaint = new TextPaint(); mPaint.setColor(0xFFFFFFFF); mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, - getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); + mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); setBackgroundResource(R.drawable.quantum_panel_dark); setWillNotDraw(false); } @@ -173,12 +176,12 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen return; } + DeviceProfile grid = mLauncher.getDeviceProfile(); if (mTopCornerDrawable == null) { if (mDrawableSizeChanged) { int outset = (mCenterDrawable instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0; - int maxSize = LauncherAppState.getInstance().getDynamicGrid() - .getDeviceProfile().iconSizePx + 2 * outset; + int maxSize = grid.iconSizePx + 2 * outset; int size = Math.min(maxSize, Math.min( getWidth() - getPaddingLeft() - getPaddingRight(), getHeight() - getPaddingTop() - getPaddingBottom())); @@ -193,7 +196,6 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen } else { // Draw the top corner icon and "Setup" text is possible if (mDrawableSizeChanged) { - DeviceProfile grid = getDeviceProfile(); int iconSize = grid.iconSizePx; int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); @@ -251,8 +253,4 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen } } } - - private DeviceProfile getDeviceProfile() { - return LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); - } } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index 490ed6a73..157b48a39 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -45,10 +45,13 @@ public class ShortcutAndWidgetContainer extends ViewGroup { private int mCountX; private int mCountY; + private Launcher mLauncher; + private boolean mInvertIfRtl = false; public ShortcutAndWidgetContainer(Context context) { super(context); + mLauncher = (Launcher) context; mWallpaperManager = WallpaperManager.getInstance(context); } @@ -125,22 +128,19 @@ public class ShortcutAndWidgetContainer extends ViewGroup { } int getCellContentWidth() { - final LauncherAppState app = LauncherAppState.getInstance(); - final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + final DeviceProfile grid = mLauncher.getDeviceProfile(); return Math.min(getMeasuredHeight(), mIsHotseatLayout ? grid.hotseatCellWidthPx: grid.cellWidthPx); } int getCellContentHeight() { - final LauncherAppState app = LauncherAppState.getInstance(); - final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + final DeviceProfile grid = mLauncher.getDeviceProfile(); return Math.min(getMeasuredHeight(), mIsHotseatLayout ? grid.hotseatCellHeightPx : grid.cellHeightPx); } public void measureChild(View child) { - final LauncherAppState app = LauncherAppState.getInstance(); - final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + final DeviceProfile grid = mLauncher.getDeviceProfile(); final int cellWidth = mCellWidth; final int cellHeight = mCellHeight; CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 9f47e138d..6c4b7207b 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -46,9 +46,11 @@ import android.graphics.drawable.PaintDrawable; import android.os.Build; import android.os.Process; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.TypedValue; import android.view.View; import android.widget.Toast; @@ -669,4 +671,17 @@ public final class Utilities { && launchIntent.getExtras() == null && TextUtils.isEmpty(launchIntent.getDataString()); } + + public static float dpiFromPx(int size, DisplayMetrics metrics){ + float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; + return (size / densityRatio); + } + public static int pxFromDp(float size, DisplayMetrics metrics) { + return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + size, metrics)); + } + public static int pxFromSp(float size, DisplayMetrics metrics) { + return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, + size, metrics)); + } } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 5ee1f2615..1cf3bc469 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -66,17 +66,19 @@ public class WidgetPreviewLoader { private final UserManagerCompat mUserManager; private final AppWidgetManagerCompat mManager; private final CacheDb mDb; + private final InvariantDeviceProfile mDeviceProfile; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); private final Handler mWorkerHandler; - public WidgetPreviewLoader(Context context, IconCache iconCache) { + public WidgetPreviewLoader(Context context, InvariantDeviceProfile inv, IconCache iconCache) { mContext = context; mIconCache = iconCache; mManager = AppWidgetManagerCompat.getInstance(context); mUserManager = UserManagerCompat.getInstance(context); mDb = new CacheDb(context); mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); + mDeviceProfile = inv; } /** @@ -86,8 +88,8 @@ public class WidgetPreviewLoader { * @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo} * @return a request id which can be used to cancel the request. */ - public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight, - WidgetCell caller) { + public PreviewLoadRequest getPreview(final Object o, int previewWidth, + int previewHeight, WidgetCell caller) { String size = previewWidth + "x" + previewHeight; WidgetCacheKey key = getObjectKey(o, size); @@ -329,23 +331,18 @@ public class WidgetPreviewLoader { return null; } - private Bitmap generatePreview(Object info, Bitmap recycle, int previewWidth, int previewHeight) { + private Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle, + int previewWidth, int previewHeight) { if (info instanceof LauncherAppWidgetProviderInfo) { - return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, previewWidth, recycle); + return generateWidgetPreview(launcher, (LauncherAppWidgetProviderInfo) info, + previewWidth, recycle, null); } else { - return generateShortcutPreview( + return generateShortcutPreview(launcher, (ResolveInfo) info, previewWidth, previewHeight, recycle); } } - public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, - int previewWidth, Bitmap preview) { - int maxWidth = Math.min(previewWidth, info.spanX - * LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().cellWidthPx); - return generateWidgetPreview(info, maxWidth, preview, null); - } - - public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, + public Bitmap generateWidgetPreview(Launcher launcher, LauncherAppWidgetProviderInfo info, int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) { // Load the preview image if possible if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE; @@ -362,8 +359,8 @@ public class WidgetPreviewLoader { } final boolean widgetPreviewExists = (drawable != null); - final int spanX = info.spanX < 1 ? 1 : info.spanX; - final int spanY = info.spanY < 1 ? 1 : info.spanY; + final int spanX = info.getSpanX(launcher) < 1 ? 1 : info.getSpanX(launcher); + final int spanY = info.getSpanY(launcher) < 1 ? 1 : info.getSpanY(launcher); int previewWidth; int previewHeight; @@ -413,8 +410,7 @@ public class WidgetPreviewLoader { } else { final Paint p = new Paint(); p.setFilterBitmap(true); - int appIconSize = LauncherAppState.getInstance().getDynamicGrid() - .getDeviceProfile().iconSizePx; + int appIconSize = launcher.getDeviceProfile().iconSizePx; // draw the spanX x spanY tiles final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight()); @@ -455,7 +451,7 @@ public class WidgetPreviewLoader { } private Bitmap generateShortcutPreview( - ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) { + Launcher launcher, ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) { final Canvas c = new Canvas(); if (preview == null) { preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888); @@ -488,8 +484,8 @@ public class WidgetPreviewLoader { // Draw the final icon at top left corner. // TODO: use top right for RTL - int appIconSize = LauncherAppState.getInstance().getDynamicGrid() - .getDeviceProfile().iconSizePx; + int appIconSize = launcher.getDeviceProfile().iconSizePx; + icon.setAlpha(255); icon.setColorFilter(null); icon.setBounds(0, 0, appIconSize, appIconSize); @@ -626,8 +622,10 @@ public class WidgetPreviewLoader { // which would gets re-written next time. mVersions = getPackageVersion(mKey.componentName.getPackageName()); + Launcher launcher = (Launcher) mCaller.getContext(); + // it's not in the db... we need to generate it - preview = generatePreview(mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight); + preview = generatePreview(launcher, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight); } return preview; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 55742f655..67155d0b1 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -307,13 +307,11 @@ public class Workspace extends SmoothPagedView mLauncher = (Launcher) context; mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); final Resources res = getResources(); - mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid(). - getDeviceProfile().shouldFadeAdjacentWorkspaceScreens(); + DeviceProfile grid = mLauncher.getDeviceProfile(); + mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); mFadeInAdjacentScreens = false; mWallpaperManager = WallpaperManager.getInstance(context); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); mSpringLoadedShrinkFactor = @@ -422,7 +420,7 @@ public class Workspace extends SmoothPagedView protected void initWorkspace() { mCurrentPage = mDefaultPage; LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); mIconCache = app.getIconCache(); setWillNotDraw(false); setClipChildren(false); @@ -1901,8 +1899,7 @@ public class Workspace extends SmoothPagedView public void onExternalDragStartedWithItem(View v) { // Compose a drag bitmap with the view scaled to the icon size - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int iconSize = grid.iconSizePx; int bmpWidth = v.getMeasuredWidth(); int bmpHeight = v.getMeasuredHeight(); @@ -1984,7 +1981,7 @@ public class Workspace extends SmoothPagedView int getOverviewModeTranslationY() { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); Rect overviewBar = grid.getOverviewModeButtonBarRect(); int availableHeight = getViewportHeight(); @@ -2285,8 +2282,7 @@ public class Workspace extends SmoothPagedView int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 - padding.get() / 2); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { @@ -2343,7 +2339,7 @@ public class Workspace extends SmoothPagedView public void beginExternalDragShared(View child, DragSource source) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int iconSize = grid.iconSizePx; // Notify launcher of drag start @@ -2852,14 +2848,14 @@ public class Workspace extends SmoothPagedView * widthGap/heightGap (right, bottom) */ static Rect getCellLayoutMetrics(Launcher launcher, int orientation) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = launcher.getDeviceProfile(); Display display = launcher.getWindowManager().getDefaultDisplay(); Point smallestSize = new Point(); Point largestSize = new Point(); display.getCurrentSizeRange(smallestSize, largestSize); - int countX = (int) grid.numColumns; - int countY = (int) grid.numRows; + int countX = (int) grid.inv.numColumns; + int countY = (int) grid.inv.numRows; if (orientation == CellLayout.LANDSCAPE) { if (mLandscapeCellLayoutMetrics == null) { Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE); @@ -3023,7 +3019,7 @@ public class Workspace extends SmoothPagedView mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true); LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); r = grid.getHotseatRect(); if (r.contains(mTempPt[0], mTempPt[1])) { return true; diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 61a64e3f3..5744531af 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -162,7 +162,7 @@ public class WorkspaceStateTransitionAnimation { mWorkspace = workspace; LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); Resources res = launcher.getResources(); mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime); mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index a84e7df03..696eabe00 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -23,6 +23,7 @@ import android.view.ViewGroup; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -80,8 +81,8 @@ public class FocusLogic { keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL); } - public static int handleKeyEvent(int keyCode, int cntX, int cntY, int [][] map, - int iconIdx, int pageIndex, int pageCount) { + public static int handleKeyEvent(int keyCode, int cntX, int cntY, + int [][] map, int iconIdx, int pageIndex, int pageCount, boolean isRtl) { if (DEBUG) { Log.v(TAG, String.format( @@ -89,23 +90,21 @@ public class FocusLogic { cntX, cntY, iconIdx, pageIndex, pageCount)); } - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid() - .getDeviceProfile(); int newIndex = NOOP; switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/); - if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) { + if (isRtl && newIndex == NOOP && pageIndex > 0) { newIndex = PREVIOUS_PAGE_RIGHT_COLUMN; - } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) { + } else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) { newIndex = NEXT_PAGE_RIGHT_COLUMN; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/); - if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) { + if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) { newIndex = NEXT_PAGE_LEFT_COLUMN; - } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) { + } else if (isRtl && newIndex == NOOP && pageIndex > 0) { newIndex = PREVIOUS_PAGE_LEFT_COLUMN; } break; diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index db1699818..36cc2b111 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -19,6 +19,7 @@ import android.appwidget.AppWidgetHostView; import android.os.Bundle; import android.os.Parcelable; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherSettings; import com.android.launcher3.PendingAddItemInfo; @@ -39,7 +40,7 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { public AppWidgetHostView boundWidget; public Bundle bindOptions = null; - public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) { + public PendingAddWidgetInfo(Launcher launcher, LauncherAppWidgetProviderInfo i, Parcelable data) { if (i.isCustomWidget) { itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } else { @@ -54,10 +55,10 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { previewImage = i.previewImage; icon = i.icon; - spanX = i.spanX; - spanY = i.spanY; - minSpanX = i.minSpanX; - minSpanY = i.minSpanY; + spanX = i.getSpanX(launcher); + spanY = i.getSpanY(launcher); + minSpanX = i.getMinSpanX(launcher); + minSpanY = i.getMinSpanY(launcher); } public boolean isCustomWidget() { diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index dcaf1f211..3ec164506 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -29,7 +29,9 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; @@ -72,6 +74,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private WidgetPreviewLoader mWidgetPreviewLoader; private PreviewLoadRequest mActiveRequest; + private Launcher mLauncher; + public WidgetCell(Context context) { this(context, null); } @@ -84,8 +88,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { super(context, attrs, defStyle); final Resources r = context.getResources(); - mDimensionsFormatString = r.getString(R.string.widget_dims_format); + mLauncher = (Launcher) context; + mDimensionsFormatString = r.getString(R.string.widget_dims_format); setContainerWidth(); setWillNotDraw(false); setClipToPadding(false); @@ -93,9 +98,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } private void setContainerWidth() { - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile profile = mLauncher.getDeviceProfile(); cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE); - mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE); + mPresetPreviewSize = (int) (cellSize * PREVIEW_SCALE); } @Override @@ -130,14 +135,14 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { */ public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, WidgetPreviewLoader loader) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + InvariantDeviceProfile profile = + LauncherAppState.getInstance().getInvariantDeviceProfile(); mInfo = info; // TODO(hyunyoungs): setup a cache for these labels. mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); - int hSpan = Math.min(info.spanX, grid.numColumns); - int vSpan = Math.min(info.spanY, grid.numRows); + int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns); + int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows); mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); mWidgetPreviewLoader = loader; } @@ -192,8 +197,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { public int getActualItemWidth() { ItemInfo info = (ItemInfo) getTag(); int[] size = getPreviewSize(); - int cellWidth = LauncherAppState.getInstance() - .getDynamicGrid().getDeviceProfile().cellWidthPx; + int cellWidth = mLauncher.getDeviceProfile().cellWidthPx; return Math.min(size[0], info.spanX * cellWidth); } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 5a879faa0..05e842e71 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -109,7 +109,7 @@ public class WidgetsContainerView extends BaseContainerView mView.setLayoutManager(new LinearLayoutManager(getContext()) { @Override protected int getExtraLayoutSpace(State state) { - DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); return super.getExtraLayoutSpace(state) + grid.availableHeightPx * PRELOAD_SCREEN_HEIGHT_MULTIPLE; } @@ -230,8 +230,8 @@ public class WidgetsContainerView extends BaseContainerView int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]); int[] previewSizeBeforeScale = new int[1]; - preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, - maxWidth, null, previewSizeBeforeScale); + preview = getWidgetPreviewLoader().generateWidgetPreview(mLauncher, + createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale); if (previewSizeBeforeScale[0] < icon.getWidth()) { // The icon has extra padding around it. diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 918ec1bda..7439a44f8 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -32,11 +32,12 @@ import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.DynamicGrid; +import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.WidgetsModel; @@ -56,7 +57,6 @@ public class WidgetsListAdapter extends Adapter { private static final String TAG = "WidgetsListAdapter"; private static final boolean DEBUG = true; - private Context mContext; private Launcher mLauncher; private LayoutInflater mLayoutInflater; @@ -74,7 +74,6 @@ public class WidgetsListAdapter extends Adapter { View.OnLongClickListener iconLongClickListener, Launcher launcher) { mLayoutInflater = LayoutInflater.from(context); - mContext = context; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; @@ -141,7 +140,7 @@ public class WidgetsListAdapter extends Adapter { WidgetCell widget = (WidgetCell) row.getChildAt(i); if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) { LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i); - PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null); + PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(mLauncher, info, null); widget.setTag(pawi); widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader); } else if (infoList.get(i) instanceof ResolveInfo) { @@ -206,10 +205,10 @@ public class WidgetsListAdapter extends Adapter { } private void setContainerHeight() { - Resources r = mContext.getResources(); - DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + Resources r = mLauncher.getResources(); + DeviceProfile profile = mLauncher.getDeviceProfile(); if (profile.isLargeTablet || profile.isTablet) { - mIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); + mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); } } } diff --git a/util/com/android/launcher3/DecoderRing.java b/util/com/android/launcher3/DecoderRing.java index 86431d9b7..0732fe889 100644 --- a/util/com/android/launcher3/DecoderRing.java +++ b/util/com/android/launcher3/DecoderRing.java @@ -220,9 +220,6 @@ class DecoderRing { if (widget.icon != null) { writeImageData(widget.icon.data, prefix + "_icon.png"); } - if (widget.preview != null) { - writeImageData(widget.preview.data, prefix + "_preview.png"); - } } } } -- cgit v1.2.3 From 2805e639cdea6ae0051155611d122ed27556e658 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 20 May 2015 15:35:32 -0700 Subject: Animating workspace active background change > Removing workspace_overscroll_drawable_padding which is always 0 Change-Id: I89900738371458a6eb7cbfee215d98b267d6a0b0 --- res/drawable/bg_screenpanel.xml | 26 +++++++++++ res/values/dimens.xml | 1 - src/com/android/launcher3/CellLayout.java | 75 ++++++++++++------------------- src/com/android/launcher3/Workspace.java | 2 +- 4 files changed, 56 insertions(+), 48 deletions(-) create mode 100644 res/drawable/bg_screenpanel.xml diff --git a/res/drawable/bg_screenpanel.xml b/res/drawable/bg_screenpanel.xml new file mode 100644 index 000000000..cdb71dfa1 --- /dev/null +++ b/res/drawable/bg_screenpanel.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 60591e14a..3e5fe3a83 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -43,7 +43,6 @@ 16dp - 0dp 4dip diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 27f1ac611..4bf862d0d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -34,6 +34,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; import android.os.Build; import android.os.Parcelable; import android.support.v4.view.ViewCompat; @@ -87,31 +88,27 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // These are temporary variables to prevent having to allocate a new object just to // return an (x, y) value from helper functions. Do NOT use them to maintain other state. - private final int[] mTmpXY = new int[2]; @Thunk final int[] mTmpPoint = new int[2]; - int[] mTempLocation = new int[2]; + @Thunk final int[] mTempLocation = new int[2]; boolean[][] mOccupied; boolean[][] mTmpOccupied; - private boolean mLastDownOnOccupiedCell = false; private OnTouchListener mInterceptTouchListener; private ArrayList mFolderOuterRings = new ArrayList(); private int[] mFolderLeaveBehindCell = {-1, -1}; - private float FOREGROUND_ALPHA_DAMPER = 0.65f; + private static final float FOREGROUND_ALPHA_DAMPER = 0.65f; private int mForegroundAlpha = 0; private float mBackgroundAlpha; - private Drawable mNormalBackground; - private Drawable mActiveGlowBackground; + private static final int BACKGROUND_ACTIVATE_DURATION = 120; + private final TransitionDrawable mBackground; + + private final Drawable mOverScrollLeft; + private final Drawable mOverScrollRight; private Drawable mOverScrollForegroundDrawable; - private Drawable mOverScrollLeft; - private Drawable mOverScrollRight; - private Rect mBackgroundRect; - private Rect mForegroundRect; - private int mForegroundPadding; // These values allow a fixed measurement to be set on the CellLayout. private int mFixedWidth = -1; @@ -173,7 +170,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private static final int INVALID_DIRECTION = -100; private DropTarget.DragEnforcer mDragEnforcer; - private Rect mTempRect = new Rect(); + private final Rect mTempRect = new Rect(); private final static Paint sPaint = new Paint(); @@ -221,20 +218,15 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { final Resources res = getResources(); mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx; - mNormalBackground = res.getDrawable(R.drawable.screenpanel); - mActiveGlowBackground = res.getDrawable(R.drawable.screenpanel_hover); + mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel); + mBackground.setCallback(this); mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left); mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right); - mForegroundPadding = - res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding); mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx); - mNormalBackground.setFilterBitmap(true); - mActiveGlowBackground.setFilterBitmap(true); - // Initialize the data structures used for the drag visualization. mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out mDragCell[0] = mDragCell[1] = -1; @@ -292,9 +284,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mDragOutlineAnims[i] = anim; } - mBackgroundRect = new Rect(); - mForegroundRect = new Rect(); - mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context); mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap, mCountX, mCountY); @@ -431,6 +420,11 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { void setIsDragOverlapping(boolean isDragOverlapping) { if (mIsDragOverlapping != isDragOverlapping) { mIsDragOverlapping = isDragOverlapping; + if (mIsDragOverlapping) { + mBackground.startTransition(BACKGROUND_ACTIVATE_DURATION); + } else { + mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION); + } invalidate(); } } @@ -451,18 +445,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // a drag). However, we also drag the mini hover background *over* one of those two // backgrounds if (mBackgroundAlpha > 0.0f) { - Drawable bg; - - if (mIsDragOverlapping) { - // In the mini case, we draw the active_glow bg *over* the active background - bg = mActiveGlowBackground; - } else { - bg = mNormalBackground; - } - - bg.setAlpha((int) (mBackgroundAlpha * 255)); - bg.setBounds(mBackgroundRect); - bg.draw(canvas); + mBackground.draw(canvas); } final Paint paint = mDragOutlinePaint; @@ -561,7 +544,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mForegroundAlpha > 0) { - mOverScrollForegroundDrawable.setBounds(mForegroundRect); mOverScrollForegroundDrawable.draw(canvas); } } @@ -924,12 +906,12 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { super.onSizeChanged(w, h, oldw, oldh); // Expand the background drawing bounds by the padding baked into the background drawable - Rect padding = new Rect(); - mNormalBackground.getPadding(padding); - mBackgroundRect.set(-padding.left, -padding.top, w + padding.right, h + padding.bottom); + mBackground.getPadding(mTempRect); + mBackground.setBounds(-mTempRect.left, -mTempRect.top, + w + mTempRect.right, h + mTempRect.bottom); - mForegroundRect.set(mForegroundPadding, mForegroundPadding, - w - mForegroundPadding, h - mForegroundPadding); + mOverScrollLeft.setBounds(0, 0, w, h); + mOverScrollRight.setBounds(0, 0, w, h); } @Override @@ -949,10 +931,15 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { public void setBackgroundAlpha(float alpha) { if (mBackgroundAlpha != alpha) { mBackgroundAlpha = alpha; - invalidate(); + mBackground.setAlpha((int) (mBackgroundAlpha * 255)); } } + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || (mIsDragTarget && who == mBackground); + } + public void setShortcutAndWidgetAlpha(float alpha) { mShortcutsAndWidgets.setAlpha(alpha); } @@ -1265,7 +1252,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { hitMaxX = xSize >= spanX; hitMaxY = ySize >= spanY; } - final int[] cellXY = mTmpXY; + final int[] cellXY = mTmpPoint; cellToCenterPoint(x, y, cellXY); // We verify that the current rect is not a sub-rect of any of our previous @@ -3022,10 +3009,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - public boolean lastDownOnOccupiedCell() { - return mLastDownOnOccupiedCell; - } - public boolean findVacantCell(int spanX, int spanY, int[] outXY) { return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 67155d0b1..7a52e5865 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1066,7 +1066,7 @@ public class Workspace extends SmoothPagedView case MotionEvent.ACTION_UP: if (mTouchState == TOUCH_STATE_REST) { final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); - if (currentPage != null && !currentPage.lastDownOnOccupiedCell()) { + if (currentPage != null) { onWallpaperTap(ev); } } -- cgit v1.2.3 From c6205603efe1f2987caf96504c87d720a25b5a94 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 21 May 2015 20:46:33 -0700 Subject: Creating landscape and portrait device profiles at app initialization Change-Id: Ide9d007adc36b348e19b05cdf49e87f8b02db60e --- .../android/launcher3/AlphabeticalAppsList.java | 2 +- src/com/android/launcher3/AppsContainerView.java | 2 +- src/com/android/launcher3/AppsGridAdapter.java | 2 +- src/com/android/launcher3/CellLayout.java | 14 +- src/com/android/launcher3/DeviceProfile.java | 386 ++++++--------------- src/com/android/launcher3/FocusHelper.java | 15 +- src/com/android/launcher3/Folder.java | 6 +- src/com/android/launcher3/Hotseat.java | 2 +- .../android/launcher3/InvariantDeviceProfile.java | 30 +- src/com/android/launcher3/Launcher.java | 11 +- src/com/android/launcher3/LauncherAppState.java | 13 +- src/com/android/launcher3/Partner.java | 30 -- src/com/android/launcher3/Workspace.java | 24 +- .../WorkspaceStateTransitionAnimation.java | 3 +- 14 files changed, 172 insertions(+), 368 deletions(-) diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java index 808bddbf3..623e4aa73 100644 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/AlphabeticalAppsList.java @@ -219,7 +219,7 @@ public class AlphabeticalAppsList { public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) { // Update the merge algorithm DeviceProfile grid = mLauncher.getDeviceProfile(); - if (grid.isPhone()) { + if (grid.isPhone) { mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f), MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); } else { diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 9ff3bfabc..3bfe26b56 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -453,7 +453,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, // Update the apps recycler view, inset it by the container inset as well DeviceProfile grid = mLauncher.getDeviceProfile(); - int startMargin = grid.isPhone() ? mContentMarginStart : 0; + int startMargin = grid.isPhone ? mContentMarginStart : 0; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; if (isRtl) { mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset, diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index 9c1c46b80..a593a5715 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -139,7 +139,7 @@ class AppsGridAdapter extends RecyclerView.Adapter { mPredictedAppsDividerPaint); hasDrawnPredictedAppsDivider = true; - } else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) { + } else if (grid.isPhone && shouldDrawItemSection(holder, i, items)) { // At this point, we only draw sections for each section break; int viewTopOffset = (2 * child.getPaddingTop()); int pos = holder.getPosition(); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 4bf862d0d..2b1cfe0e4 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -821,8 +821,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - DeviceProfile grid = mLauncher.getDeviceProfile(); - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); @@ -830,8 +828,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight()); int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom()); if (mFixedCellWidth < 0 || mFixedCellHeight < 0) { - int cw = grid.calculateCellWidth(childWidthSize, mCountX); - int ch = grid.calculateCellHeight(childHeightSize, mCountY); + int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX); + int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY); if (cw != mCellWidth || ch != mCellHeight) { mCellWidth = cw; mCellHeight = ch; @@ -2714,16 +2712,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { * @param result An array of length 2 in which to store the result (may be null). */ public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) { - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = launcher.getDeviceProfile(); - Rect padding = grid.getWorkspacePadding(grid.isLandscape ? - CellLayout.LANDSCAPE : CellLayout.PORTRAIT); + Rect padding = grid.getWorkspacePadding(Utilities.isRtl(launcher.getResources())); // Always assume we're working with the smallest span to make sure we // reserve enough space in both orientations. - int parentWidth = grid.calculateCellWidth(grid.widthPx + int parentWidth = DeviceProfile.calculateCellWidth(grid.widthPx - padding.left - padding.right, (int) grid.inv.numColumns); - int parentHeight = grid.calculateCellHeight(grid.heightPx + int parentHeight = DeviceProfile.calculateCellHeight(grid.heightPx - padding.top - padding.bottom, (int) grid.inv.numRows); int smallerSize = Math.min(parentWidth, parentHeight); diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 8ab58b9c0..8f2056569 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -19,118 +19,104 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; import android.content.ComponentName; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.Display; import android.view.Gravity; -import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; -import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; -import com.android.launcher3.util.Thunk; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; - public class DeviceProfile { - public static interface DeviceProfileCallbacks { - public void onAvailableSizeChanged(DeviceProfile grid); - } - public final InvariantDeviceProfile inv; - private int iconDrawablePaddingOriginalPx; - - boolean isLandscape; - public boolean isTablet; - public boolean isLargeTablet; - public boolean isLayoutRtl; - - boolean transposeLayoutWithOrientation; - int desiredWorkspaceLeftRightMarginPx; - public int edgeMarginPx; - Rect defaultWidgetPadding; - - int widthPx; - int heightPx; - public int availableWidthPx; - public int availableHeightPx; - int defaultPageSpacingPx; - - int overviewModeMinIconZoneHeightPx; - int overviewModeMaxIconZoneHeightPx; - int overviewModeBarItemWidthPx; - int overviewModeBarSpacerWidthPx; - float overviewModeIconZoneRatio; - float overviewModeScaleFactor; - public int cellWidthPx; - public int cellHeightPx; - - int iconDrawablePaddingPx; - int allAppsCellWidthPx; - int allAppsCellHeightPx; - int allAppsCellPaddingPx; - int folderBackgroundOffset; - int folderIconSizePx; - int folderCellWidthPx; - int folderCellHeightPx; - int hotseatCellWidthPx; - int hotseatCellHeightPx; - int hotseatIconSizePx; - int hotseatBarHeightPx; - int allAppsNumRows; - int allAppsNumCols; - int appsViewNumCols; - int appsViewNumPredictiveCols; - int searchBarSpaceWidthPx; - int searchBarSpaceHeightPx; - int pageIndicatorHeightPx; - int allAppsButtonVisualSize; - - int iconSizePx; + // Device properties + public final boolean isTablet; + public final boolean isLargeTablet; + public final boolean isPhone; + public final boolean transposeLayoutWithOrientation; + + // Device properties in current orientation + public final boolean isLandscape; + public final int widthPx; + public final int heightPx; + public final int availableWidthPx; + public final int availableHeightPx; + + // Overview mode + private final int overviewModeMinIconZoneHeightPx; + private final int overviewModeMaxIconZoneHeightPx; + private final int overviewModeBarItemWidthPx; + private final int overviewModeBarSpacerWidthPx; + private final float overviewModeIconZoneRatio; + private final float overviewModeScaleFactor; + + // Workspace + private int desiredWorkspaceLeftRightMarginPx; + public final int edgeMarginPx; + public final Rect defaultWidgetPadding; + private final int pageIndicatorHeightPx; + private final int defaultPageSpacingPx; + private float dragViewScale; + + // Workspace icons + public int iconSizePx; public int iconTextSizePx; - int allAppsIconSizePx; - int allAppsIconTextSizePx; - - float dragViewScale; + public int iconDrawablePaddingPx; + private final int iconDrawablePaddingOriginalPx; - int allAppsShortEdgeCount = -1; - int allAppsLongEdgeCount = -1; - - private ArrayList mCallbacks = new ArrayList(); - - public DeviceProfile(Context context, InvariantDeviceProfile inv) { - // Determine the dynamic grid properties - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); + public int cellWidthPx; + public int cellHeightPx; - Point realSize = new Point(); - display.getRealSize(realSize); - DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); + // Folder + public int folderBackgroundOffset; + public int folderIconSizePx; + public int folderCellWidthPx; + public int folderCellHeightPx; + + // Hotseat + public int hotseatCellWidthPx; + public int hotseatCellHeightPx; + public int hotseatIconSizePx; + private int hotseatBarHeightPx; + + // All apps + private int allAppsCellWidthPx; + public int allAppsCellHeightPx; + private final int allAppsCellPaddingPx; + public int appsViewNumCols; + public int appsViewNumPredictiveCols; + public int allAppsButtonVisualSize; + public final int allAppsIconSizePx; + public final int allAppsIconTextSizePx; + + // QSB + private int searchBarSpaceWidthPx; + private int searchBarSpaceHeightPx; + + public DeviceProfile(Context context, InvariantDeviceProfile inv, + Point minSize, Point maxSize, + int width, int height, boolean isLandscape) { this.inv = inv; - init(context, realSize.x, realSize.y, dm.widthPixels, dm.heightPixels); - } + this.isLandscape = isLandscape; - private void init(Context context, int wPx, int hPx, int awPx, int ahPx) { Resources res = context.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); + // Constants from resources + isTablet = res.getBoolean(R.bool.is_tablet); + isLargeTablet = res.getBoolean(R.bool.is_large_tablet); + isPhone = !isTablet && !isLargeTablet; + + // Some more constants transposeLayoutWithOrientation = res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); @@ -167,27 +153,22 @@ public class DeviceProfile { // AllApps uses the original non-scaled icon size allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm); - // If the partner customization apk contains any grid overrides, apply them - applyPartnerDeviceProfileOverrides(context, dm); + // Determine sizes. + widthPx = width; + heightPx = height; + if (isLandscape) { + availableWidthPx = maxSize.x; + availableHeightPx = minSize.y; + } else { + availableWidthPx = minSize.x; + availableHeightPx = maxSize.y; + } // Calculate the remaining vars - updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx); - updateAvailableDimensions(context); + updateAvailableDimensions(dm, res); computeAllAppsButtonSize(context); } - /** - * Apply any Partner customization grid overrides. - * - * Currently we support: all apps row / column count. - */ - private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) { - Partner p = Partner.get(ctx.getPackageManager()); - if (p != null) { - p.applyDeviceProfileOverrides(this); - } - } - /** * Determine the exact visual footprint of the all apps button, taking into account scaling * and internal padding of the drawable. @@ -195,82 +176,25 @@ public class DeviceProfile { private void computeAllAppsButtonSize(Context context) { Resources res = context.getResources(); float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f; - LauncherAppState app = LauncherAppState.getInstance(); allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)); } - void addCallback(DeviceProfileCallbacks cb) { - mCallbacks.add(cb); - cb.onAvailableSizeChanged(this); - } - void removeCallback(DeviceProfileCallbacks cb) { - mCallbacks.remove(cb); - } - - private int getDeviceOrientation(Context context) { - WindowManager windowManager = (WindowManager) - context.getSystemService(Context.WINDOW_SERVICE); - Resources resources = context.getResources(); - DisplayMetrics dm = resources.getDisplayMetrics(); - Configuration config = resources.getConfiguration(); - int rotation = windowManager.getDefaultDisplay().getRotation(); - - boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) && - (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180); - boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) && - (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); - if (isLandscape || isRotatedPortrait) { - return CellLayout.LANDSCAPE; - } else { - return CellLayout.PORTRAIT; - } - } - - private void updateAvailableDimensions(Context context) { - WindowManager windowManager = (WindowManager) - context.getSystemService(Context.WINDOW_SERVICE); - Display display = windowManager.getDefaultDisplay(); - Resources resources = context.getResources(); - DisplayMetrics dm = resources.getDisplayMetrics(); - Configuration config = resources.getConfiguration(); - - // There are three possible configurations that the dynamic grid accounts for, portrait, - // landscape with the nav bar at the bottom, and landscape with the nav bar at the side. - // To prevent waiting for fitSystemWindows(), we make the observation that in landscape, - // the height is the smallest height (either with the nav bar at the bottom or to the - // side) and otherwise, the height is simply the largest possible height for a portrait - // device. - Point size = new Point(); - Point smallestSize = new Point(); - Point largestSize = new Point(); - display.getSize(size); - display.getCurrentSizeRange(smallestSize, largestSize); - availableWidthPx = size.x; - if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { - availableHeightPx = smallestSize.y; - } else { - availableHeightPx = largestSize.y; - } - + private void updateAvailableDimensions(DisplayMetrics dm, Resources res) { // Check to see if the icons fit in the new available height. If not, then we need to // shrink the icon size. float scale = 1f; int drawablePadding = iconDrawablePaddingOriginalPx; - updateIconSize(1f, drawablePadding, resources, dm); + updateIconSize(1f, drawablePadding, res, dm); float usedHeight = (cellHeightPx * inv.numRows); - Rect workspacePadding = getWorkspacePadding(); + // We only care about the top and bottom workspace padding, which is not affected by RTL. + Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */); int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom); if (usedHeight > maxHeight) { scale = maxHeight / usedHeight; drawablePadding = 0; } - updateIconSize(scale, drawablePadding, resources, dm); - - // Make the callbacks - for (DeviceProfileCallbacks cb : mCallbacks) { - cb.onAvailableSizeChanged(this); - } + updateIconSize(scale, drawablePadding, res, dm); } private void updateIconSize(float scale, int drawablePadding, Resources res, @@ -309,25 +233,6 @@ public class DeviceProfile { // All Apps allAppsCellWidthPx = allAppsIconSizePx; allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + allAppsIconTextSizePx; - int maxLongEdgeCellCount = - res.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count); - int maxShortEdgeCellCount = - res.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count); - int minEdgeCellCount = - res.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count); - int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount); - int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount); - - if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) { - allAppsNumRows = isLandscape ? allAppsShortEdgeCount : allAppsLongEdgeCount; - allAppsNumCols = isLandscape ? allAppsLongEdgeCount : allAppsShortEdgeCount; - } else { - allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) / - (allAppsCellHeightPx + allAppsCellPaddingPx); - allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows)); - allAppsNumCols = (availableWidthPx) / (allAppsCellWidthPx + allAppsCellPaddingPx); - allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols)); - } int appsContainerViewWidthPx = res.getDimensionPixelSize(R.dimen.apps_container_width); updateAppsViewNumCols(res, appsContainerViewWidthPx); @@ -339,7 +244,7 @@ public class DeviceProfile { int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) / (allAppsCellWidthPx + 2 * allAppsCellPaddingPx); - int numPredictiveAppCols = isPhone() ? 4 : numAppsCols; + int numPredictiveAppCols = isPhone ? 4 : numAppsCols; if ((numAppsCols != appsViewNumCols) || (numPredictiveAppCols != appsViewNumPredictiveCols)) { appsViewNumCols = numAppsCols; @@ -349,24 +254,9 @@ public class DeviceProfile { return false; } - void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx, - int awPx, int ahPx) { - Configuration configuration = resources.getConfiguration(); - isLandscape = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE); - isTablet = resources.getBoolean(R.bool.is_tablet); - isLargeTablet = resources.getBoolean(R.bool.is_large_tablet); - isLayoutRtl = Utilities.isRtl(resources); - widthPx = wPx; - heightPx = hPx; - availableWidthPx = awPx; - availableHeightPx = ahPx; - - updateAvailableDimensions(context); - } - /** Returns the search bar top offset */ - int getSearchBarTopOffset() { - if (isTablet() && !isVerticalBarLayout()) { + private int getSearchBarTopOffset() { + if (isTablet && !isVerticalBarLayout()) { return 4 * edgeMarginPx; } else { return 2 * edgeMarginPx; @@ -374,14 +264,9 @@ public class DeviceProfile { } /** Returns the search bar bounds in the current orientation */ - Rect getSearchBarBounds() { - return getSearchBarBounds(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); - } - /** Returns the search bar bounds in the specified orientation */ - Rect getSearchBarBounds(int orientation) { + public Rect getSearchBarBounds(boolean isLayoutRtl) { Rect bounds = new Rect(); - if (orientation == CellLayout.LANDSCAPE && - transposeLayoutWithOrientation) { + if (isLandscape && transposeLayoutWithOrientation) { if (isLayoutRtl) { bounds.set(availableWidthPx - searchBarSpaceHeightPx, edgeMarginPx, availableWidthPx, availableHeightPx - edgeMarginPx); @@ -390,12 +275,10 @@ public class DeviceProfile { availableHeightPx - edgeMarginPx); } } else { - if (isTablet()) { + if (isTablet) { // Pad the left and right of the workspace to ensure consistent spacing // between all icons - int width = (orientation == CellLayout.LANDSCAPE) - ? Math.max(widthPx, heightPx) - : Math.min(widthPx, heightPx); + int width = isLandscape ? Math.max(widthPx, heightPx) : Math.min(widthPx, heightPx); // XXX: If the icon size changes across orientations, we will have to take // that into account here too. int gap = (int) ((width - 2 * edgeMarginPx - @@ -413,47 +296,11 @@ public class DeviceProfile { return bounds; } - /** Returns the bounds of the workspace page indicators. */ - Rect getWorkspacePageIndicatorBounds(Rect insets) { - Rect workspacePadding = getWorkspacePadding(); - if (isLandscape && transposeLayoutWithOrientation) { - if (isLayoutRtl) { - return new Rect(workspacePadding.left, workspacePadding.top, - workspacePadding.left + pageIndicatorHeightPx, - heightPx - workspacePadding.bottom - insets.bottom); - } else { - int pageIndicatorLeft = widthPx - workspacePadding.right; - return new Rect(pageIndicatorLeft, workspacePadding.top, - pageIndicatorLeft + pageIndicatorHeightPx, - heightPx - workspacePadding.bottom - insets.bottom); - } - } else { - int pageIndicatorTop = heightPx - insets.bottom - workspacePadding.bottom; - return new Rect(workspacePadding.left, pageIndicatorTop, - widthPx - workspacePadding.right, pageIndicatorTop + pageIndicatorHeightPx); - } - } - - public int getWorkspaceGridHeight() { - Rect p = getWorkspacePadding(); - return availableHeightPx - p.top - p.bottom; - } - - public int getWorkspaceGridWidth() { - Rect p = getWorkspacePadding(); - return availableWidthPx - p.left - p.right; - } - /** Returns the workspace padding in the specified orientation */ - Rect getWorkspacePadding() { - return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); - } - - Rect getWorkspacePadding(int orientation) { - Rect searchBarBounds = getSearchBarBounds(orientation); + Rect getWorkspacePadding(boolean isLayoutRtl) { + Rect searchBarBounds = getSearchBarBounds(isLayoutRtl); Rect padding = new Rect(); - if (orientation == CellLayout.LANDSCAPE && - transposeLayoutWithOrientation) { + if (isLandscape && transposeLayoutWithOrientation) { // Pad the left and right of the workspace with search/hotseat bar sizes if (isLayoutRtl) { padding.set(hotseatBarHeightPx, edgeMarginPx, @@ -463,14 +310,14 @@ public class DeviceProfile { hotseatBarHeightPx, edgeMarginPx); } } else { - if (isTablet()) { + if (isTablet) { // Pad the left and right of the workspace to ensure consistent spacing // between all icons float gapScale = 1f + (dragViewScale - 1f) / 2f; - int width = (orientation == CellLayout.LANDSCAPE) + int width = isLandscape ? Math.max(widthPx, heightPx) : Math.min(widthPx, heightPx); - int height = (orientation != CellLayout.LANDSCAPE) + int height = isLandscape ? Math.max(widthPx, heightPx) : Math.min(widthPx, heightPx); int paddingTop = searchBarBounds.bottom; @@ -492,16 +339,15 @@ public class DeviceProfile { return padding; } - int getWorkspacePageSpacing(int orientation) { - if ((orientation == CellLayout.LANDSCAPE && - transposeLayoutWithOrientation) || isLargeTablet()) { + private int getWorkspacePageSpacing(boolean isLayoutRtl) { + if ((isLandscape && transposeLayoutWithOrientation) || isLargeTablet) { // In landscape mode the page spacing is set to the default. return defaultPageSpacingPx; } else { // In portrait, we want the pages spaced such that there is no // overhang of the previous / next page into the current page viewport. // We assume symmetrical padding in portrait mode. - return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding().left); + return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding(isLayoutRtl).left); } } @@ -512,8 +358,8 @@ public class DeviceProfile { return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx); } - float getOverviewModeScale() { - Rect workspacePadding = getWorkspacePadding(); + public float getOverviewModeScale(boolean isLayoutRtl) { + Rect workspacePadding = getWorkspacePadding(isLayoutRtl); Rect overviewBar = getOverviewModeButtonBarRect(); int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom; return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace; @@ -537,16 +383,6 @@ public class DeviceProfile { return height / countY; } - boolean isPhone() { - return !isTablet && !isLargeTablet; - } - boolean isTablet() { - return isTablet; - } - boolean isLargeTablet() { - return isLargeTablet; - } - /** * When {@code true}, hotseat is on the bottom row when in landscape mode. * If {@code false}, hotseat is on the right column when in landscape mode. @@ -556,10 +392,10 @@ public class DeviceProfile { } boolean shouldFadeAdjacentWorkspaceScreens() { - return isVerticalBarLayout() || isLargeTablet(); + return isVerticalBarLayout() || isLargeTablet; } - int getVisibleChildCount(ViewGroup parent) { + private int getVisibleChildCount(ViewGroup parent) { int visibleChildren = 0; for (int i = 0; i < parent.getChildCount(); i++) { if (parent.getChildAt(i).getVisibility() != View.GONE) { @@ -572,6 +408,7 @@ public class DeviceProfile { public void layout(Launcher launcher) { FrameLayout.LayoutParams lp; boolean hasVerticalBarLayout = isVerticalBarLayout(); + final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources()); // Layout the search bar space View searchBar = launcher.getSearchBar(); @@ -601,11 +438,10 @@ public class DeviceProfile { PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace); lp = (FrameLayout.LayoutParams) workspace.getLayoutParams(); lp.gravity = Gravity.CENTER; - int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT; - Rect padding = getWorkspacePadding(orientation); + Rect padding = getWorkspacePadding(isLayoutRtl); workspace.setLayoutParams(lp); workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom); - workspace.setPageSpacing(getWorkspacePageSpacing(orientation)); + workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl)); // Layout the hotseat View hotseat = launcher.findViewById(R.id.hotseat); @@ -616,7 +452,7 @@ public class DeviceProfile { lp.width = hotseatBarHeightPx; lp.height = LayoutParams.MATCH_PARENT; hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx); - } else if (isTablet()) { + } else if (isTablet) { // Pad the hotseat with the workspace padding calculated above lp.gravity = Gravity.BOTTOM; lp.width = LayoutParams.MATCH_PARENT; diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index fe50e3a4e..46e4902f9 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -92,13 +92,12 @@ public class FocusHelper { final int pageIndex = pagedView.indexOfChild(cellLayout); final int pageCount = pagedView.getPageCount(); - Launcher launcher = (Launcher) v.getContext(); + final boolean isLayoutRtl = Utilities.isRtl(v.getResources()); int[][] matrix = FocusLogic.createSparseMatrix(cellLayout); // Process focus. int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, - countY, matrix, iconIndex, pageIndex, pageCount, - launcher.getDeviceProfile().isLayoutRtl); + countY, matrix, iconIndex, pageIndex, pageCount, isLayoutRtl); if (newIconIndex == FocusLogic.NOOP) { handleNoopKey(keyCode, v); return consume; @@ -252,7 +251,7 @@ public class FocusHelper { // Process the focus. int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, - countY, matrix, iconIndex, pageIndex, pageCount, profile.isLayoutRtl); + countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources())); View newIcon = null; if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) { @@ -335,7 +334,7 @@ public class FocusHelper { // Process the focus. int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, - countY, matrix, iconIndex, pageIndex, pageCount, profile.isLayoutRtl); + countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources())); View newIcon = null; switch (newIconIndex) { case FocusLogic.NOOP: @@ -358,7 +357,8 @@ public class FocusHelper { matrix = FocusLogic.createSparseMatrix(iconLayout, iconLayout.getCountX(), row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, - matrix, FocusLogic.PIVOT, newPageIndex, pageCount, profile.isLayoutRtl); + matrix, FocusLogic.PIVOT, newPageIndex, pageCount, + Utilities.isRtl(v.getResources())); newIcon = parent.getChildAt(newIconIndex); } break; @@ -391,7 +391,8 @@ public class FocusHelper { iconLayout = (CellLayout) parent.getParent(); matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row); newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, - matrix, FocusLogic.PIVOT, newPageIndex, pageCount, profile.isLayoutRtl); + matrix, FocusLogic.PIVOT, newPageIndex, pageCount, + Utilities.isRtl(v.getResources())); newIcon = parent.getChildAt(newIconIndex); } break; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 72dc1e94a..ec4ea044c 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -36,7 +36,6 @@ import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -968,7 +967,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList bounds.left + bounds.width() - width); int top = Math.min(Math.max(bounds.top, centeredTop), bounds.top + bounds.height() - height); - if (grid.isPhone() && (grid.availableWidthPx - width) < grid.iconSizePx) { + if (grid.isPhone && (grid.availableWidthPx - width) < grid.iconSizePx) { // Center the folder if it is full (on phones only) left = (grid.availableWidthPx - width) / 2; } else if (width >= bounds.width()) { @@ -1003,8 +1002,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private int getContentAreaHeight() { DeviceProfile grid = mLauncher.getDeviceProfile(); - Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ? - CellLayout.LANDSCAPE : CellLayout.PORTRAIT); + Rect workspacePadding = grid.getWorkspacePadding(mContent.mIsRtl); int maxContentAreaHeight = grid.availableHeightPx - workspacePadding.top - workspacePadding.bottom - mFooterHeight; diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 1c1342cfe..ce33164fa 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -109,7 +109,7 @@ public class Hotseat extends FrameLayout { mAllAppsButtonRank = grid.inv.hotseatAllAppsRank; mContent = (CellLayout) findViewById(R.id.layout); - if (grid.isLandscape && !grid.isLargeTablet()) { + if (grid.isLandscape && !grid.isLargeTablet) { mContent.setGridSize(1, (int) grid.inv.numHotseatIcons); } else { mContent.setGridSize((int) grid.inv.numHotseatIcons, 1); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index fcd6d60f6..92fdbde85 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -16,29 +16,25 @@ package com.android.launcher3; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Point; import android.graphics.PointF; +import android.os.Build; import android.util.DisplayMetrics; -import android.util.TypedValue; import android.view.Display; import android.view.WindowManager; - import com.android.launcher3.util.Thunk; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class InvariantDeviceProfile { - private static final String TAG = "InvariantDeviceProfile"; // This is a static that we use for the default icon size on a 4/5-inch phone - static float DEFAULT_ICON_SIZE_DP = 60; - + private static float DEFAULT_ICON_SIZE_DP = 60; - static ArrayList sDeviceProfiles = - new ArrayList(); + private static final ArrayList sDeviceProfiles = new ArrayList<>(); static { sDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby", 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); @@ -67,7 +63,7 @@ public class InvariantDeviceProfile { 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); } - class DeviceProfileQuery { + private class DeviceProfileQuery { InvariantDeviceProfile profile; float widthDps; float heightDps; @@ -100,6 +96,9 @@ public class InvariantDeviceProfile { // Derived invariant properties int hotseatAllAppsRank; + DeviceProfile landscapeProfile; + DeviceProfile portraitProfile; + InvariantDeviceProfile() { } @@ -124,6 +123,7 @@ public class InvariantDeviceProfile { defaultLayoutId = dlId; } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) InvariantDeviceProfile(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); @@ -179,6 +179,18 @@ public class InvariantDeviceProfile { // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize applyPartnerDeviceProfileOverrides(context, dm); + + Point realSize = new Point(); + display.getRealSize(realSize); + // The real size never changes. smallSide and largeSize will remain the + // same in any orientation. + int smallSide = Math.min(realSize.x, realSize.y); + int largeSide = Math.max(realSize.x, realSize.y); + + landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize, + largeSide, smallSide, true /* isLandscape */); + portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize, + smallSide, largeSide, false /* isLandscape */); } /** diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 6c7373907..c923c9582 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -431,8 +431,13 @@ public class Launcher extends Activity LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this); // Load configuration-specific DeviceProfile - mDeviceProfile = new DeviceProfile(this, app.getInvariantDeviceProfile()); - mDeviceProfile.addCallback(LauncherAppState.getInstance()); + mDeviceProfile = getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE ? + app.getInvariantDeviceProfile().landscapeProfile + : app.getInvariantDeviceProfile().portraitProfile; + + // TODO: Move this to icon cache. + Utilities.setIconSize(mDeviceProfile.iconSizePx); // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), @@ -4199,7 +4204,7 @@ public class Launcher extends Activity } protected Rect getSearchBarBounds() { - return mDeviceProfile.getSearchBarBounds(); + return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources())); } public void bindSearchablesChanged() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 93753a201..d4b41e671 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.annotation.TargetApi; import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; @@ -24,12 +23,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Point; -import android.os.Build; -import android.util.DisplayMetrics; import android.util.Log; -import android.view.Display; -import android.view.WindowManager; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; @@ -38,7 +32,7 @@ import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; -public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { +public class LauncherAppState { private final AppFilter mAppFilter; private final BuildInfo mBuildInfo; @@ -213,11 +207,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mInvariantDeviceProfile; } - @Override - public void onAvailableSizeChanged(DeviceProfile grid) { - Utilities.setIconSize(grid.iconSizePx); - } - public static boolean isDogfoodBuild() { return getInstance().mBuildInfo.isDogfoodBuild(); } diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java index c9e837994..380078b26 100644 --- a/src/com/android/launcher3/Partner.java +++ b/src/com/android/launcher3/Partner.java @@ -47,8 +47,6 @@ public class Partner { public static final String RES_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow"; /** These resources are used to override the device profile */ - public static final String RES_GRID_AA_SHORT_EDGE_COUNT = "grid_aa_short_edge_count"; - public static final String RES_GRID_AA_LONG_EDGE_COUNT = "grid_aa_long_edge_count"; public static final String RES_GRID_NUM_ROWS = "grid_num_rows"; public static final String RES_GRID_NUM_COLUMNS = "grid_num_columns"; public static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp"; @@ -154,32 +152,4 @@ public class Partner { inv.iconSize = iconSize; } } - - public void applyDeviceProfileOverrides(DeviceProfile dp) { - int allAppsShortEdgeCount = -1; - int allAppsLongEdgeCount = -1; - - try { - int resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT, - "integer", getPackageName()); - if (resId > 0) { - allAppsShortEdgeCount = getResources().getInteger(resId); - } - - resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT, - "integer", getPackageName()); - if (resId > 0) { - allAppsLongEdgeCount = getResources().getInteger(resId); - } - - } catch (Resources.NotFoundException ex) { - Log.e(TAG, "Invalid Partner grid resource!", ex); - return; - } - - if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) { - dp.allAppsShortEdgeCount = allAppsShortEdgeCount; - dp.allAppsLongEdgeCount = allAppsLongEdgeCount; - } - } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 7a52e5865..d2c37d209 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -316,7 +316,7 @@ public class Workspace extends SmoothPagedView R.styleable.Workspace, defStyle, 0); mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; - mOverviewModeShrinkFactor = grid.getOverviewModeScale(); + mOverviewModeShrinkFactor = grid.getOverviewModeScale(mIsRtl); mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); a.recycle(); @@ -1980,7 +1980,6 @@ public class Workspace extends SmoothPagedView } int getOverviewModeTranslationY() { - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); Rect overviewBar = grid.getOverviewModeButtonBarRect(); @@ -2338,7 +2337,6 @@ public class Workspace extends SmoothPagedView } public void beginExternalDragShared(View child, DragSource source) { - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); int iconSize = grid.iconSizePx; @@ -2848,34 +2846,35 @@ public class Workspace extends SmoothPagedView * widthGap/heightGap (right, bottom) */ static Rect getCellLayoutMetrics(Launcher launcher, int orientation) { LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = launcher.getDeviceProfile(); + InvariantDeviceProfile inv = app.getInvariantDeviceProfile(); Display display = launcher.getWindowManager().getDefaultDisplay(); Point smallestSize = new Point(); Point largestSize = new Point(); display.getCurrentSizeRange(smallestSize, largestSize); - int countX = (int) grid.inv.numColumns; - int countY = (int) grid.inv.numRows; + int countX = (int) inv.numColumns; + int countY = (int) inv.numRows; + boolean isLayoutRtl = Utilities.isRtl(launcher.getResources()); if (orientation == CellLayout.LANDSCAPE) { if (mLandscapeCellLayoutMetrics == null) { - Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE); + Rect padding = inv.landscapeProfile.getWorkspacePadding(isLayoutRtl); int width = largestSize.x - padding.left - padding.right; int height = smallestSize.y - padding.top - padding.bottom; mLandscapeCellLayoutMetrics = new Rect(); mLandscapeCellLayoutMetrics.set( - grid.calculateCellWidth(width, countX), - grid.calculateCellHeight(height, countY), 0, 0); + DeviceProfile.calculateCellWidth(width, countX), + DeviceProfile.calculateCellHeight(height, countY), 0, 0); } return mLandscapeCellLayoutMetrics; } else if (orientation == CellLayout.PORTRAIT) { if (mPortraitCellLayoutMetrics == null) { - Rect padding = grid.getWorkspacePadding(CellLayout.PORTRAIT); + Rect padding = inv.portraitProfile.getWorkspacePadding(isLayoutRtl); int width = smallestSize.x - padding.left - padding.right; int height = largestSize.y - padding.top - padding.bottom; mPortraitCellLayoutMetrics = new Rect(); mPortraitCellLayoutMetrics.set( - grid.calculateCellWidth(width, countX), - grid.calculateCellHeight(height, countY), 0, 0); + DeviceProfile.calculateCellWidth(width, countX), + DeviceProfile.calculateCellHeight(height, countY), 0, 0); } return mPortraitCellLayoutMetrics; } @@ -3018,7 +3017,6 @@ public class Workspace extends SmoothPagedView mTempPt[1] = y; mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true); - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); r = grid.getHotseatRect(); if (r.contains(mTempPt[0], mTempPt[1])) { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 5744531af..42ba36ef6 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -161,7 +161,6 @@ public class WorkspaceStateTransitionAnimation { mLauncher = launcher; mWorkspace = workspace; - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); Resources res = launcher.getResources(); mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime); @@ -170,7 +169,7 @@ public class WorkspaceStateTransitionAnimation { mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; - mOverviewModeShrinkFactor = grid.getOverviewModeScale(); + mOverviewModeShrinkFactor = grid.getOverviewModeScale(Utilities.isRtl(res)); mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); } -- cgit v1.2.3 From 5f4e0fdd2e4edeb9211e2dcd1c99497f175731f8 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 22 May 2015 11:12:27 -0700 Subject: Moving all apps code into sub package. - Renaming resources, dimens, etc to be more consistent - Removing old AppsCustomize resources and other unused code Change-Id: I15ce35e7cb7a9b9344fc7103963e4e4c9e45d89a --- res/drawable-ldrtl/all_apps_fastscroll_bg.xml | 27 + res/drawable-ldrtl/apps_list_fastscroll_bg.xml | 27 - res/drawable/all_apps_fastscroll_bg.xml | 27 + res/drawable/all_apps_scrollbar_thumb.xml | 21 + res/drawable/all_apps_search_bg.xml | 23 + res/drawable/apps_list_fastscroll_bg.xml | 27 - res/drawable/apps_list_scrollbar_thumb.xml | 21 - res/drawable/apps_list_search_bg.xml | 23 - res/drawable/apps_search_bg.xml | 23 - res/layout-land/launcher.xml | 2 +- res/layout-port/launcher.xml | 2 +- res/layout-sw600dp/all_apps.xml | 33 + res/layout-sw600dp/apps_view.xml | 33 - res/layout-sw720dp/launcher.xml | 2 +- res/layout/all_apps.xml | 35 + res/layout/all_apps_container.xml | 107 ++ res/layout/all_apps_empty_search.xml | 28 + res/layout/all_apps_icon.xml | 29 + res/layout/all_apps_prediction_bar_icon.xml | 28 + res/layout/all_apps_reveal.xml | 24 + res/layout/app_icon.xml | 19 + res/layout/application.xml | 19 - res/layout/apps_empty_view.xml | 28 - res/layout/apps_grid_icon_view.xml | 29 - res/layout/apps_list_view.xml | 107 -- res/layout/apps_prediction_bar_icon_view.xml | 28 - res/layout/apps_reveal_view.xml | 24 - res/layout/apps_view.xml | 35 - res/layout/widgets_view.xml | 2 +- res/values-sw340dp-port/config.xml | 18 - res/values-sw600dp/dimens.xml | 10 +- res/values-sw600dp/styles.xml | 21 - res/values-sw720dp-land/dimens.xml | 4 - res/values-sw720dp/config.xml | 2 +- res/values-sw720dp/dimens.xml | 6 +- res/values/attrs.xml | 27 - res/values/colors.xml | 6 +- res/values/config.xml | 18 +- res/values/dimens.xml | 69 +- res/values/strings.xml | 10 +- .../android/launcher3/AlphabeticalAppsList.java | 574 ----------- src/com/android/launcher3/AppInfo.java | 2 +- .../launcher3/AppsContainerRecyclerView.java | 523 ---------- .../launcher3/AppsContainerSearchEditTextView.java | 65 -- src/com/android/launcher3/AppsContainerView.java | 983 ------------------- src/com/android/launcher3/AppsGridAdapter.java | 465 --------- .../launcher3/AppsRecyclerViewContainer.java | 61 -- .../launcher3/BaseContainerRecyclerView.java | 102 -- src/com/android/launcher3/BaseRecyclerView.java | 102 ++ src/com/android/launcher3/DeviceProfile.java | 33 +- src/com/android/launcher3/Launcher.java | 21 +- .../LauncherStateTransitionAnimation.java | 15 +- .../android/launcher3/LauncherTransitionable.java | 30 + src/com/android/launcher3/WidgetPreviewLoader.java | 2 - .../WorkspaceStateTransitionAnimation.java | 4 +- .../launcher3/allapps/AllAppsContainerView.java | 1018 ++++++++++++++++++++ .../launcher3/allapps/AllAppsGridAdapter.java | 471 +++++++++ .../launcher3/allapps/AllAppsRecyclerView.java | 528 ++++++++++ .../allapps/AllAppsRecyclerViewContainerView.java | 69 ++ .../launcher3/allapps/AllAppsSearchEditView.java | 65 ++ .../launcher3/allapps/AlphabeticalAppsList.java | 577 +++++++++++ .../widget/WidgetsContainerRecyclerView.java | 40 - .../launcher3/widget/WidgetsRecyclerView.java | 40 + 63 files changed, 3392 insertions(+), 3422 deletions(-) create mode 100644 res/drawable-ldrtl/all_apps_fastscroll_bg.xml delete mode 100644 res/drawable-ldrtl/apps_list_fastscroll_bg.xml create mode 100644 res/drawable/all_apps_fastscroll_bg.xml create mode 100644 res/drawable/all_apps_scrollbar_thumb.xml create mode 100644 res/drawable/all_apps_search_bg.xml delete mode 100644 res/drawable/apps_list_fastscroll_bg.xml delete mode 100644 res/drawable/apps_list_scrollbar_thumb.xml delete mode 100644 res/drawable/apps_list_search_bg.xml delete mode 100644 res/drawable/apps_search_bg.xml create mode 100644 res/layout-sw600dp/all_apps.xml delete mode 100644 res/layout-sw600dp/apps_view.xml create mode 100644 res/layout/all_apps.xml create mode 100644 res/layout/all_apps_container.xml create mode 100644 res/layout/all_apps_empty_search.xml create mode 100644 res/layout/all_apps_icon.xml create mode 100644 res/layout/all_apps_prediction_bar_icon.xml create mode 100644 res/layout/all_apps_reveal.xml create mode 100644 res/layout/app_icon.xml delete mode 100644 res/layout/application.xml delete mode 100644 res/layout/apps_empty_view.xml delete mode 100644 res/layout/apps_grid_icon_view.xml delete mode 100644 res/layout/apps_list_view.xml delete mode 100644 res/layout/apps_prediction_bar_icon_view.xml delete mode 100644 res/layout/apps_reveal_view.xml delete mode 100644 res/layout/apps_view.xml delete mode 100644 res/values-sw340dp-port/config.xml delete mode 100644 res/values-sw600dp/styles.xml delete mode 100644 src/com/android/launcher3/AlphabeticalAppsList.java delete mode 100644 src/com/android/launcher3/AppsContainerRecyclerView.java delete mode 100644 src/com/android/launcher3/AppsContainerSearchEditTextView.java delete mode 100644 src/com/android/launcher3/AppsContainerView.java delete mode 100644 src/com/android/launcher3/AppsGridAdapter.java delete mode 100644 src/com/android/launcher3/AppsRecyclerViewContainer.java delete mode 100644 src/com/android/launcher3/BaseContainerRecyclerView.java create mode 100644 src/com/android/launcher3/BaseRecyclerView.java create mode 100644 src/com/android/launcher3/LauncherTransitionable.java create mode 100644 src/com/android/launcher3/allapps/AllAppsContainerView.java create mode 100644 src/com/android/launcher3/allapps/AllAppsGridAdapter.java create mode 100644 src/com/android/launcher3/allapps/AllAppsRecyclerView.java create mode 100644 src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java create mode 100644 src/com/android/launcher3/allapps/AllAppsSearchEditView.java create mode 100644 src/com/android/launcher3/allapps/AlphabeticalAppsList.java delete mode 100644 src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java create mode 100644 src/com/android/launcher3/widget/WidgetsRecyclerView.java diff --git a/res/drawable-ldrtl/all_apps_fastscroll_bg.xml b/res/drawable-ldrtl/all_apps_fastscroll_bg.xml new file mode 100644 index 000000000..4777f70bb --- /dev/null +++ b/res/drawable-ldrtl/all_apps_fastscroll_bg.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable-ldrtl/apps_list_fastscroll_bg.xml b/res/drawable-ldrtl/apps_list_fastscroll_bg.xml deleted file mode 100644 index 772975a71..000000000 --- a/res/drawable-ldrtl/apps_list_fastscroll_bg.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/drawable/all_apps_fastscroll_bg.xml b/res/drawable/all_apps_fastscroll_bg.xml new file mode 100644 index 000000000..6b7448459 --- /dev/null +++ b/res/drawable/all_apps_fastscroll_bg.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable/all_apps_scrollbar_thumb.xml b/res/drawable/all_apps_scrollbar_thumb.xml new file mode 100644 index 000000000..649a963b1 --- /dev/null +++ b/res/drawable/all_apps_scrollbar_thumb.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/all_apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml new file mode 100644 index 000000000..57eb5825e --- /dev/null +++ b/res/drawable/all_apps_search_bg.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/apps_list_fastscroll_bg.xml b/res/drawable/apps_list_fastscroll_bg.xml deleted file mode 100644 index 780d3b0c3..000000000 --- a/res/drawable/apps_list_fastscroll_bg.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/drawable/apps_list_scrollbar_thumb.xml b/res/drawable/apps_list_scrollbar_thumb.xml deleted file mode 100644 index 318d40678..000000000 --- a/res/drawable/apps_list_scrollbar_thumb.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/drawable/apps_list_search_bg.xml b/res/drawable/apps_list_search_bg.xml deleted file mode 100644 index 9bb6d81b2..000000000 --- a/res/drawable/apps_list_search_bg.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/drawable/apps_search_bg.xml b/res/drawable/apps_search_bg.xml deleted file mode 100644 index 57eb5825e..000000000 --- a/res/drawable/apps_search_bg.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index 113b319ff..bf9296f7d 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -61,7 +61,7 @@ android:layout_height="match_parent" android:visibility="invisible" /> - - + + + + + \ No newline at end of file diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml deleted file mode 100644 index e6e0ec358..000000000 --- a/res/layout-sw600dp/apps_view.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml index 8a9f3e913..b27f33bbf 100644 --- a/res/layout-sw720dp/launcher.xml +++ b/res/layout-sw720dp/launcher.xml @@ -71,7 +71,7 @@ android:layout_height="match_parent" android:visibility="invisible" /> - + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml new file mode 100644 index 000000000..a20ab46d8 --- /dev/null +++ b/res/layout/all_apps_container.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_empty_search.xml b/res/layout/all_apps_empty_search.xml new file mode 100644 index 000000000..f60c4a09a --- /dev/null +++ b/res/layout/all_apps_empty_search.xml @@ -0,0 +1,28 @@ + + + + diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml new file mode 100644 index 000000000..10ed25c47 --- /dev/null +++ b/res/layout/all_apps_icon.xml @@ -0,0 +1,29 @@ + + + + diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml new file mode 100644 index 000000000..1e75d14b6 --- /dev/null +++ b/res/layout/all_apps_prediction_bar_icon.xml @@ -0,0 +1,28 @@ + + + + diff --git a/res/layout/all_apps_reveal.xml b/res/layout/all_apps_reveal.xml new file mode 100644 index 000000000..2951ea4f4 --- /dev/null +++ b/res/layout/all_apps_reveal.xml @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/res/layout/app_icon.xml b/res/layout/app_icon.xml new file mode 100644 index 000000000..831cee5b0 --- /dev/null +++ b/res/layout/app_icon.xml @@ -0,0 +1,19 @@ + + + + diff --git a/res/layout/application.xml b/res/layout/application.xml deleted file mode 100644 index 831cee5b0..000000000 --- a/res/layout/application.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - diff --git a/res/layout/apps_empty_view.xml b/res/layout/apps_empty_view.xml deleted file mode 100644 index e4c4e2e8c..000000000 --- a/res/layout/apps_empty_view.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - diff --git a/res/layout/apps_grid_icon_view.xml b/res/layout/apps_grid_icon_view.xml deleted file mode 100644 index 7165f383d..000000000 --- a/res/layout/apps_grid_icon_view.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml deleted file mode 100644 index 040498359..000000000 --- a/res/layout/apps_list_view.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/apps_prediction_bar_icon_view.xml b/res/layout/apps_prediction_bar_icon_view.xml deleted file mode 100644 index 1e75d14b6..000000000 --- a/res/layout/apps_prediction_bar_icon_view.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - diff --git a/res/layout/apps_reveal_view.xml b/res/layout/apps_reveal_view.xml deleted file mode 100644 index 2951ea4f4..000000000 --- a/res/layout/apps_reveal_view.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml deleted file mode 100644 index 7f09f7795..000000000 --- a/res/layout/apps_view.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 196dc45e5..2615ddbdd 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -34,7 +34,7 @@ android:focusable="false" android:visibility="invisible" /> - - - - - diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index c366ef31b..994c1925f 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -17,11 +17,11 @@ 64dp - - 18dp - 0dp - 26sp - 12dp + + 18dp + 0dp + 26sp + 12dp 400dp diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml deleted file mode 100644 index bcbbafdbb..000000000 --- a/res/values-sw600dp/styles.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml index ca13db05b..514980fe0 100644 --- a/res/values-sw720dp-land/dimens.xml +++ b/res/values-sw720dp-land/dimens.xml @@ -15,10 +15,6 @@ --> - - 4 - 2 - 100dip diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml index af6751e60..94cffcb55 100644 --- a/res/values-sw720dp/config.xml +++ b/res/values-sw720dp/config.xml @@ -2,7 +2,7 @@ true true - + 90 diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index 39b0c8006..89942f741 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -16,8 +16,10 @@ 72dp - 54dp - 16dp + + + 54dp + 16dp 8dip diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 782d050b5..827332ad7 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -72,16 +72,6 @@ - - - - - - - - - - @@ -93,23 +83,6 @@ - - - - - - - - - - - - - - - - diff --git a/res/values/colors.xml b/res/values/colors.xml index a5db2fc40..e2b8a2ea7 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -36,9 +36,9 @@ #FFFFFFFF #FF374248 - - #009688 - #009688 + + #009688 + #009688 #FFFFFF diff --git a/res/values/config.xml b/res/values/config.xml index b6e633c10..fbce3a41f 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -1,8 +1,5 @@ - 6 - 5 - 3 20 @@ -24,21 +21,20 @@ -1500 - + 55 - 100 + 100 250 80 - - 220 - 300 - - 60 + + 220 + 300 + 60 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 3e5fe3a83..246adcdad 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -15,13 +15,14 @@ --> + 48dp + 6dp 500dp 56dp 20dp 4dp - 18dp 8dp 80dp 120dp @@ -48,48 +49,29 @@ 4dip 12dip - - 0dp - 8dp - 56dp - 8dp - 24sp - 4dp - 64dp - -16dp - 72dp - 48dp - 52dp - 8dp - 12dp - + 8dp - - 48dp - - - 14dp - 14sp + 8dp + 56dp + 8dp + 24sp + 52dp + 8dp + 18dp + 12dp + + 4dp + 64dp + -16dp + 72dp + 48dp 4dp 16dp 6dp - - - 20dp - - - 12dp - - - 30dp - 8dp 8dp @@ -109,6 +91,23 @@ 0dp 0dp + + + 14dp + 14sp + + + 20dp + + + 12dp + + + 30dp + 4dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 18f97c84d..a8c668d61 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -47,20 +47,20 @@ Show Mem - + Touch & hold to pick up a widget. %1$d \u00d7 %2$d - + - Search Apps + Search Apps - Loading Apps… + Loading Apps… - No Apps found matching \"%1$s\" + No Apps found matching \"%1$s\" diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java deleted file mode 100644 index 623e4aa73..000000000 --- a/src/com/android/launcher3/AlphabeticalAppsList.java +++ /dev/null @@ -1,574 +0,0 @@ -package com.android.launcher3; - -import android.content.ComponentName; -import android.content.Context; -import android.support.v7.widget.RecyclerView; -import android.util.Log; -import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.model.AppNameComparator; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; - -/** - * The alphabetically sorted list of applications. - */ -public class AlphabeticalAppsList { - - public static final String TAG = "AlphabeticalAppsList"; - private static final boolean DEBUG = false; - - /** - * Info about a section in the alphabetic list - */ - public static class SectionInfo { - // The number of applications in this section - public int numApps; - // The section break AdapterItem for this section - public AdapterItem sectionBreakItem; - // The first app AdapterItem for this section - public AdapterItem firstAppItem; - } - - /** - * Info about a fast scroller section, depending if sections are merged, the fast scroller - * sections will not be the same set as the section headers. - */ - public static class FastScrollSectionInfo { - // The section name - public String sectionName; - // To map the touch (from 0..1) to the index in the app list to jump to in the fast - // scroller, we use the fraction in range (0..1) of the app index / total app count. - public float appRangeFraction; - // The AdapterItem to scroll to for this section - public AdapterItem appItem; - - public FastScrollSectionInfo(String sectionName, float appRangeFraction) { - this.sectionName = sectionName; - this.appRangeFraction = appRangeFraction; - } - } - - /** - * Info about a particular adapter item (can be either section or app) - */ - public static class AdapterItem { - /** Common properties */ - // The index of this adapter item in the list - public int position; - // The type of this item - public int viewType; - - /** Section & App properties */ - // The section for this item - public SectionInfo sectionInfo; - - /** App-only properties */ - // The section name of this app. Note that there can be multiple items with different - // sectionNames in the same section - public String sectionName = null; - // The index of this app in the section - public int sectionAppIndex = -1; - // The associated AppInfo for the app - public AppInfo appInfo = null; - // The index of this app not including sections - public int appIndex = -1; - - public static AdapterItem asSectionBreak(int pos, SectionInfo section) { - AdapterItem item = new AdapterItem(); - item.viewType = AppsGridAdapter.SECTION_BREAK_VIEW_TYPE; - item.position = pos; - item.sectionInfo = section; - section.sectionBreakItem = item; - return item; - } - - public static AdapterItem asPredictionBarSpacer(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE; - item.position = pos; - return item; - } - - public static AdapterItem asApp(int pos, SectionInfo section, String sectionName, - int sectionAppIndex, AppInfo appInfo, int appIndex) { - AdapterItem item = new AdapterItem(); - item.viewType = AppsGridAdapter.ICON_VIEW_TYPE; - item.position = pos; - item.sectionInfo = section; - item.sectionName = sectionName; - item.sectionAppIndex = sectionAppIndex; - item.appInfo = appInfo; - item.appIndex = appIndex; - return item; - } - } - - /** - * A filter interface to limit the set of applications in the apps list. - */ - public interface Filter { - boolean retainApp(AppInfo info, String sectionName); - } - - /** - * Callback to notify when the set of adapter items have changed. - */ - public interface AdapterChangedCallback { - void onAdapterItemsChanged(); - } - - /** - * Common interface for different merging strategies. - */ - private interface MergeAlgorithm { - boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount); - } - - /** - * The logic we use to merge sections on tablets. - */ - private static class TabletMergeAlgorithm implements MergeAlgorithm { - - @Override - public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { - // Merge EVERYTHING - return true; - } - } - - /** - * The logic we use to merge sections on phones. - */ - private static class PhoneMergeAlgorithm implements MergeAlgorithm { - - private int mMinAppsPerRow; - private int mMinRowsInMergedSection; - private int mMaxAllowableMerges; - - public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { - mMinAppsPerRow = minAppsPerRow; - mMinRowsInMergedSection = minRowsInMergedSection; - mMaxAllowableMerges = maxNumMerges; - } - - @Override - public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { - // Continue merging if the number of hanging apps on the final row is less than some - // fixed number (ragged), the merged rows has yet to exceed some minimum row count, - // and while the number of merged sections is less than some fixed number of merges - int rows = sectionAppCount / numAppsPerRow; - int cols = sectionAppCount % numAppsPerRow; - return (0 < cols && cols < mMinAppsPerRow) && - rows < mMinRowsInMergedSection && - mergeCount < mMaxAllowableMerges; - } - } - - private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3; - private static final int MAX_NUM_MERGES_PHONE = 2; - - private Launcher mLauncher; - - // The set of apps from the system not including predictions - private List mApps = new ArrayList<>(); - // The set of filtered apps with the current filter - private List mFilteredApps = new ArrayList<>(); - // The current set of adapter items - private List mAdapterItems = new ArrayList<>(); - // The set of sections for the apps with the current filter - private List mSections = new ArrayList<>(); - // The set of sections that we allow fast-scrolling to (includes non-merged sections) - private List mFastScrollerSections = new ArrayList<>(); - // The set of predicted app component names - private List mPredictedAppComponents = new ArrayList<>(); - // The set of predicted apps resolved from the component names and the current set of apps - private List mPredictedApps = new ArrayList<>(); - private HashMap mCachedSectionNames = new HashMap<>(); - private RecyclerView.Adapter mAdapter; - private Filter mFilter; - private AlphabeticIndexCompat mIndexer; - private AppNameComparator mAppNameComparator; - private MergeAlgorithm mMergeAlgorithm; - private AdapterChangedCallback mAdapterChangedCallback; - private int mNumAppsPerRow; - private int mNumPredictedAppsPerRow; - - public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) { - mLauncher = (Launcher) context; - mIndexer = new AlphabeticIndexCompat(context); - mAppNameComparator = new AppNameComparator(context); - setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow); - } - - /** - * Sets the apps updated callback. - */ - public void setAdapterChangedCallback(AdapterChangedCallback cb) { - mAdapterChangedCallback = cb; - } - - /** - * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. - */ - public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) { - // Update the merge algorithm - DeviceProfile grid = mLauncher.getDeviceProfile(); - if (grid.isPhone) { - mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f), - MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); - } else { - mMergeAlgorithm = new TabletMergeAlgorithm(); - } - - mNumAppsPerRow = numAppsPerRow; - mNumPredictedAppsPerRow = numPredictedAppsPerRow; - - onAppsUpdated(); - } - - /** - * Sets the adapter to notify when this dataset changes. - */ - public void setAdapter(RecyclerView.Adapter adapter) { - mAdapter = adapter; - } - - /** - * Returns sections of all the current filtered applications. - */ - public List getSections() { - return mSections; - } - - /** - * Returns fast scroller sections of all the current filtered applications. - */ - public List getFastScrollerSections() { - return mFastScrollerSections; - } - - /** - * Returns the current filtered list of applications broken down into their sections. - */ - public List getAdapterItems() { - return mAdapterItems; - } - - /** - * Returns the number of applications in this list. - */ - public int getSize() { - return mFilteredApps.size(); - } - - /** - * Returns whether there are is a filter set. - */ - public boolean hasFilter() { - return (mFilter != null); - } - - /** - * Returns whether there are no filtered results. - */ - public boolean hasNoFilteredResults() { - return (mFilter != null) && mFilteredApps.isEmpty(); - } - - /** - * Sets the current filter for this list of apps. - */ - public void setFilter(Filter f) { - if (mFilter != f) { - mFilter = f; - updateAdapterItems(); - } - } - - /** - * 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. - */ - public void setPredictedApps(List apps) { - mPredictedAppComponents.clear(); - mPredictedAppComponents.addAll(apps); - onAppsUpdated(); - } - - /** - * Returns the current set of predicted apps. - */ - public List getPredictedApps() { - return mPredictedApps; - } - - /** - * Sets the current set of apps. - */ - public void setApps(List apps) { - mApps.clear(); - mApps.addAll(apps); - onAppsUpdated(); - } - - /** - * Adds new apps to the list. - */ - public void addApps(List apps) { - // We add it in place, in alphabetical order - for (AppInfo info : apps) { - mApps.add(info); - } - onAppsUpdated(); - } - - /** - * Updates existing apps in the list - */ - public void updateApps(List apps) { - for (AppInfo info : apps) { - int index = mApps.indexOf(info); - if (index != -1) { - mApps.set(index, info); - } else { - mApps.add(info); - } - } - onAppsUpdated(); - } - - /** - * Removes some apps from the list. - */ - public void removeApps(List apps) { - for (AppInfo info : apps) { - int removeIndex = findAppByComponent(mApps, info); - if (removeIndex != -1) { - mApps.remove(removeIndex); - } - } - onAppsUpdated(); - } - - /** - * Finds the index of an app given a target AppInfo. - */ - private int findAppByComponent(List apps, AppInfo targetInfo) { - ComponentName targetComponent = targetInfo.intent.getComponent(); - int length = apps.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = apps.get(i); - if (info.user.equals(targetInfo.user) - && info.intent.getComponent().equals(targetComponent)) { - return i; - } - } - return -1; - } - - /** - * Updates internals when the set of apps are updated. - */ - private void onAppsUpdated() { - // Sort the list of apps - Collections.sort(mApps, mAppNameComparator.getAppInfoComparator()); - - // As a special case for some languages (currently only Simplified Chinese), we may need to - // coalesce sections - Locale curLocale = mLauncher.getResources().getConfiguration().locale; - TreeMap> sectionMap = null; - boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE); - if (localeRequiresSectionSorting) { - // Compute the section headers. We use a TreeMap with the section name comparator to - // ensure that the sections are ordered when we iterate over it later - sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator()); - for (AppInfo info : mApps) { - // Add the section to the cache - String sectionName = getAndUpdateCachedSectionName(info.title); - - // Add it to the mapping - ArrayList sectionApps = sectionMap.get(sectionName); - if (sectionApps == null) { - sectionApps = new ArrayList<>(); - sectionMap.put(sectionName, sectionApps); - } - sectionApps.add(info); - } - - // Add each of the section apps to the list in order - List allApps = new ArrayList<>(mApps.size()); - for (Map.Entry> entry : sectionMap.entrySet()) { - allApps.addAll(entry.getValue()); - } - mApps = allApps; - } else { - // Just compute the section headers for use below - for (AppInfo info : mApps) { - // Add the section to the cache - getAndUpdateCachedSectionName(info.title); - } - } - - // Recompose the set of adapter items from the current set of apps - updateAdapterItems(); - } - - /** - * Updates the set of filtered apps with the current filter. At this point, we expect - * mCachedSectionNames to have been calculated for the set of all apps in mApps. - */ - private void updateAdapterItems() { - SectionInfo lastSectionInfo = null; - String lastSectionName = null; - FastScrollSectionInfo lastFastScrollerSectionInfo = null; - int position = 0; - int appIndex = 0; - - // Prepare to update the list of sections, filtered apps, etc. - mFilteredApps.clear(); - mFastScrollerSections.clear(); - mAdapterItems.clear(); - mSections.clear(); - - // Process the predicted app components - mPredictedApps.clear(); - if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { - for (ComponentName cn : mPredictedAppComponents) { - for (AppInfo info : mApps) { - if (cn.equals(info.componentName)) { - mPredictedApps.add(info); - break; - } - } - // Stop at the number of predicted apps - if (mPredictedApps.size() == mNumPredictedAppsPerRow) { - break; - } - } - - if (!mPredictedApps.isEmpty()) { - // Create a new spacer for the prediction bar - AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++); - mAdapterItems.add(sectionItem); - } - } - - // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the - // ordered set of sections - int numApps = mApps.size(); - for (int i = 0; i < numApps; i++) { - AppInfo info = mApps.get(i); - String sectionName = getAndUpdateCachedSectionName(info.title); - - // Check if we want to retain this app - if (mFilter != null && !mFilter.retainApp(info, sectionName)) { - continue; - } - - // Create a new section if the section names do not match - if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { - lastSectionName = sectionName; - lastSectionInfo = new SectionInfo(); - lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, - (float) appIndex / numApps); - mSections.add(lastSectionInfo); - mFastScrollerSections.add(lastFastScrollerSectionInfo); - - // Create a new section item to break the flow of items in the list - if (!hasFilter()) { - AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); - mAdapterItems.add(sectionItem); - } - } - - // Create an app item - AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, - lastSectionInfo.numApps++, info, appIndex++); - if (lastSectionInfo.firstAppItem == null) { - lastSectionInfo.firstAppItem = appItem; - lastFastScrollerSectionInfo.appItem = appItem; - } - mAdapterItems.add(appItem); - mFilteredApps.add(info); - } - - // Merge multiple sections together as requested by the merge strategy for this device - mergeSections(); - - // Refresh the recycler view - if (mAdapter != null) { - mAdapter.notifyDataSetChanged(); - } - - if (mAdapterChangedCallback != null) { - mAdapterChangedCallback.onAdapterItemsChanged(); - } - } - - /** - * Merges multiple sections to reduce visual raggedness. - */ - private void mergeSections() { - // Go through each section and try and merge some of the sections - if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { - int sectionAppCount = 0; - for (int i = 0; i < mSections.size(); i++) { - SectionInfo section = mSections.get(i); - sectionAppCount = section.numApps; - int mergeCount = 1; - - // Merge rows based on the current strategy - while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) && - (i + 1) < mSections.size()) { - SectionInfo nextSection = mSections.remove(i + 1); - - // Remove the next section break - mAdapterItems.remove(nextSection.sectionBreakItem); - int pos = mAdapterItems.indexOf(section.firstAppItem); - // Point the section for these new apps to the merged section - int nextPos = pos + section.numApps; - for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) { - AdapterItem item = mAdapterItems.get(j); - item.sectionInfo = section; - item.sectionAppIndex += section.numApps; - } - - // Update the following adapter items of the removed section item - pos = mAdapterItems.indexOf(nextSection.firstAppItem); - for (int j = pos; j < mAdapterItems.size(); j++) { - AdapterItem item = mAdapterItems.get(j); - item.position--; - } - section.numApps += nextSection.numApps; - sectionAppCount += nextSection.numApps; - - if (DEBUG) { - Log.d(TAG, "Merging: " + nextSection.firstAppItem.sectionName + - " to " + section.firstAppItem.sectionName + - " mergedNumRows: " + (sectionAppCount / mNumAppsPerRow)); - } - mergeCount++; - } - } - } - } - - /** - * Returns the cached section name for the given title, recomputing and updating the cache if - * the title has no cached section name. - */ - private String getAndUpdateCachedSectionName(CharSequence title) { - String sectionName = mCachedSectionNames.get(title); - if (sectionName == null) { - sectionName = mIndexer.computeSectionName(title); - mCachedSectionNames.put(title, sectionName); - } - return sectionName; - } -} diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 58a57a1fe..9c87ced54 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -38,7 +38,7 @@ public class AppInfo extends ItemInfo { /** * The intent used to start the application. */ - Intent intent; + public Intent intent; /** * A bitmap version of the application icon. diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java deleted file mode 100644 index 109be7e02..000000000 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ /dev/null @@ -1,523 +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.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; - -import java.util.List; - -/** - * A RecyclerView with custom fastscroll support. This is the main container for the all apps - * icons. - */ -public class AppsContainerRecyclerView extends BaseContainerRecyclerView { - - /** - * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() - * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so - * that we can calculate what the scroll bar looks like, and where to jump to from the fast - * scroller. - */ - private static class ScrollPositionState { - // The index of the first visible row - int rowIndex; - // The offset of the first visible row - int rowTopOffset; - // The height of a given row (they are currently all the same height) - int rowHeight; - } - - private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - - private AlphabeticalAppsList mApps; - private int mNumAppsPerRow; - private int mNumPredictedAppsPerRow; - - private Drawable mScrollbar; - private Drawable mFastScrollerBg; - private Rect mTmpFastScrollerInvalidateRect = new Rect(); - private Rect mFastScrollerBounds = new Rect(); - private Rect mVerticalScrollbarBounds = new Rect(); - private boolean mDraggingFastScroller; - private String mFastScrollSectionName; - private Paint mFastScrollTextPaint; - private Rect mFastScrollTextBounds = new Rect(); - private float mFastScrollAlpha; - private int mPredictionBarHeight; - private int mDownX; - private int mDownY; - private int mLastX; - private int mLastY; - private int mScrollbarWidth; - private int mScrollbarMinHeight; - private int mScrollbarInset; - private Rect mBackgroundPadding = new Rect(); - private ScrollPositionState mScrollPosState = new ScrollPositionState(); - - private Launcher mLauncher; - - public AppsContainerRecyclerView(Context context) { - this(context, null); - } - - public AppsContainerRecyclerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr); - - mLauncher = (Launcher) context; - Resources res = context.getResources(); - int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size); - mScrollbar = res.getDrawable(R.drawable.apps_list_scrollbar_thumb); - mFastScrollerBg = res.getDrawable(R.drawable.apps_list_fastscroll_bg); - mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); - mFastScrollTextPaint = new Paint(); - mFastScrollTextPaint.setColor(Color.WHITE); - mFastScrollTextPaint.setAntiAlias(true); - mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( - R.dimen.apps_view_fast_scroll_text_size)); - mScrollbarWidth = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_bar_width); - mScrollbarMinHeight = - res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_bar_min_height); - mScrollbarInset = - res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset); - setFastScrollerAlpha(getFastScrollerAlpha()); - setOverScrollMode(View.OVER_SCROLL_NEVER); - } - - /** - * Sets the list of apps in this view, used to determine the fastscroll position. - */ - public void setApps(AlphabeticalAppsList apps) { - mApps = apps; - } - - /** - * Sets the number of apps per row in this recycler view. - */ - public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) { - mNumAppsPerRow = numAppsPerRow; - mNumPredictedAppsPerRow = numPredictedAppsPerRow; - - DeviceProfile grid = mLauncher.getDeviceProfile(); - RecyclerView.RecycledViewPool pool = getRecycledViewPool(); - int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); - pool.setMaxRecycledViews(AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1); - pool.setMaxRecycledViews(AppsGridAdapter.EMPTY_VIEW_TYPE, 1); - pool.setMaxRecycledViews(AppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow); - pool.setMaxRecycledViews(AppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows); - } - - public void updateBackgroundPadding(Drawable background) { - background.getPadding(mBackgroundPadding); - } - - /** - * Sets the prediction bar height. - */ - public void setPredictionBarHeight(int height) { - mPredictionBarHeight = height; - } - - /** - * Sets the fast scroller alpha. - */ - public void setFastScrollerAlpha(float alpha) { - mFastScrollAlpha = alpha; - invalidateFastScroller(mFastScrollerBounds); - } - - /** - * Gets the fast scroller alpha. - */ - public float getFastScrollerAlpha() { - return mFastScrollAlpha; - } - - /** - * Returns the scroll bar width. - */ - public int getScrollbarWidth() { - return mScrollbarWidth; - } - - /** - * Scrolls this recycler view to the top. - */ - public void scrollToTop() { - scrollToPosition(0); - } - - /** - * Returns the current scroll position. - */ - public int getScrollPosition() { - List items = mApps.getAdapterItems(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { - int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - return getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + - predictionBarHeight - mScrollPosState.rowTopOffset; - } - return 0; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - addOnItemTouchListener(this); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - drawVerticalScrubber(canvas); - drawFastScrollerPopup(canvas); - } - - /** - * We intercept the touch handling only to support fast scrolling when initiated from the - * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. - */ - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - return handleTouchEvent(ev); - } - - @Override - public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - handleTouchEvent(ev); - } - - /** - * Handles the touch event and determines whether to show the fast scroller (or updates it if - * it is already showing). - */ - private boolean handleTouchEvent(MotionEvent ev) { - ViewConfiguration config = ViewConfiguration.get(getContext()); - - int action = ev.getAction(); - int x = (int) ev.getX(); - int y = (int) ev.getY(); - switch (action) { - case MotionEvent.ACTION_DOWN: - // Keep track of the down positions - mDownX = mLastX = x; - mDownY = mLastY = y; - if (shouldStopScroll(ev)) { - stopScroll(); - } - break; - case MotionEvent.ACTION_MOVE: - // Check if we are scrolling - if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && - Math.abs(y - mDownY) > config.getScaledTouchSlop()) { - getParent().requestDisallowInterceptTouchEvent(true); - mDraggingFastScroller = true; - animateFastScrollerVisibility(true); - } - if (mDraggingFastScroller) { - mLastX = x; - mLastY = y; - - // Scroll to the right position, and update the section name - int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); - int bottom = getHeight() - getPaddingBottom() - - (mFastScrollerBg.getBounds().height() / 2); - float boundedY = (float) Math.max(top, Math.min(bottom, y)); - mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / - (bottom - top)); - - // Combine the old and new fast scroller bounds to create the full invalidate - // rect - mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); - updateFastScrollerBounds(); - mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); - invalidateFastScroller(mTmpFastScrollerInvalidateRect); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mDraggingFastScroller = false; - animateFastScrollerVisibility(false); - break; - } - return mDraggingFastScroller; - } - - /** - * Animates the visibility of the fast scroller popup. - */ - private void animateFastScrollerVisibility(boolean visible) { - ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); - anim.setDuration(visible ? 200 : 150); - anim.start(); - } - - /** - * Returns whether a given point is near the scrollbar. - */ - private boolean isPointNearScrollbar(int x, int y) { - // Check if we are scrolling - updateVerticalScrollbarBounds(); - mVerticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); - return mVerticalScrollbarBounds.contains(x, y); - } - - /** - * Draws the fast scroller popup. - */ - private void drawFastScrollerPopup(Canvas canvas) { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - // Draw the fast scroller popup - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); - mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollerBg.draw(canvas); - mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, - mFastScrollSectionName.length(), mFastScrollTextBounds); - float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); - canvas.drawText(mFastScrollSectionName, - (mFastScrollerBounds.width() - textWidth) / 2, - mFastScrollerBounds.height() - - (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, - mFastScrollTextPaint); - canvas.restoreToCount(restoreCount); - } - } - - /** - * Draws the vertical scrollbar. - */ - private void drawVerticalScrubber(Canvas canvas) { - updateVerticalScrollbarBounds(); - - // Draw the scroll bar - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mVerticalScrollbarBounds.left, mVerticalScrollbarBounds.top); - mScrollbar.setBounds(0, 0, mScrollbarWidth, mVerticalScrollbarBounds.height()); - mScrollbar.draw(canvas); - canvas.restoreToCount(restoreCount); - } - - /** - * Invalidates the fast scroller popup. - */ - private void invalidateFastScroller(Rect bounds) { - invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); - } - - /** - * Maps the touch (from 0..1) to the adapter position that should be visible. - */ - private String scrollToPositionAtProgress(float touchFraction) { - // Ensure that we have any sections - List fastScrollSections = - mApps.getFastScrollerSections(); - if (fastScrollSections.isEmpty()) { - return ""; - } - - // Stop the scroller if it is scrolling - LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); - stopScroll(); - - // If there is a prediction bar, then capture the appropriate area for the prediction bar - float predictionBarFraction = 0f; - if (!mApps.getPredictedApps().isEmpty()) { - predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize(); - if (touchFraction <= predictionBarFraction) { - // Scroll to the top of the view, where the prediction bar is - layoutManager.scrollToPositionWithOffset(0, 0); - return ""; - } - } - - // Since the app ranges are from 0..1, we need to map the touch fraction back to 0..1 from - // predictionBarFraction..1 - touchFraction = (touchFraction - predictionBarFraction) * - (1f / (1f - predictionBarFraction)); - AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0); - for (int i = 1; i < fastScrollSections.size(); i++) { - AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i); - if (lastScrollSection.appRangeFraction <= touchFraction && - touchFraction < scrollSection.appRangeFraction) { - break; - } - lastScrollSection = scrollSection; - } - - // Scroll to the view at the position, anchored at the top of the screen. We call the scroll - // method on the LayoutManager directly since it is not exposed by RecyclerView. - layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); - - return lastScrollSection.sectionName; - } - - /** - * Updates the bounds for the scrollbar. - */ - private void updateVerticalScrollbarBounds() { - List items = mApps.getAdapterItems(); - - // Skip early if there are no items - if (items.isEmpty()) { - mVerticalScrollbarBounds.setEmpty(); - return; - } - - // Find the index and height of the first visible row (all rows have the same height) - int x; - int y; - int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - int rowCount = getNumRows(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { - int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * mScrollPosState.rowHeight + predictionBarHeight; - if (totalScrollHeight > height) { - int scrollbarHeight = Math.max(mScrollbarMinHeight, - (int) (height / ((float) totalScrollHeight / height))); - - // Calculate the position and size of the scroll bar - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left; - } else { - x = getWidth() - mBackgroundPadding.right - mScrollbarWidth; - } - - // To calculate the offset, we compute the percentage of the total scrollable height - // that the user has already scrolled and then map that to the scroll bar bounds - int availableY = totalScrollHeight - height; - int availableScrollY = height - scrollbarHeight; - y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + predictionBarHeight - - mScrollPosState.rowTopOffset; - y = getPaddingTop() + - (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); - - mVerticalScrollbarBounds.set(x, y, x + mScrollbarWidth, y + scrollbarHeight); - return; - } - } - mVerticalScrollbarBounds.setEmpty(); - } - - /** - * Updates the bounds for the fast scroller. - */ - private void updateFastScrollerBounds() { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - int x; - int y; - - // Calculate the position for the fast scroller popup - Rect bgBounds = mFastScrollerBg.getBounds(); - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left + getScrollBarSize(); - } else { - x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); - } - y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); - y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - - bgBounds.height())); - mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); - } else { - mFastScrollerBounds.setEmpty(); - } - } - - /** - * Returns the row index for a app index in the list. - */ - private int findRowForAppIndex(int index) { - List sections = mApps.getSections(); - int appIndex = 0; - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - if (appIndex + info.numApps > index) { - return rowCount + ((index - appIndex) / mNumAppsPerRow); - } - appIndex += info.numApps; - rowCount += numRowsInSection; - } - return appIndex; - } - - /** - * Returns the total number of rows in the list. - */ - private int getNumRows() { - List sections = mApps.getSections(); - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - rowCount += numRowsInSection; - } - return rowCount; - } - - /** - * Returns the current scroll state. - */ - private void getCurScrollState(ScrollPositionState stateOut, - List items) { - stateOut.rowIndex = -1; - stateOut.rowTopOffset = -1; - stateOut.rowHeight = -1; - - // Return early if there are no items - if (items.isEmpty()) { - return; - } - - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - int position = getChildPosition(child); - if (position != NO_POSITION) { - AlphabeticalAppsList.AdapterItem item = items.get(position); - if (item.viewType == AppsGridAdapter.ICON_VIEW_TYPE) { - stateOut.rowIndex = findRowForAppIndex(item.appIndex); - stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); - stateOut.rowHeight = child.getHeight(); - break; - } - } - } - } -} diff --git a/src/com/android/launcher3/AppsContainerSearchEditTextView.java b/src/com/android/launcher3/AppsContainerSearchEditTextView.java deleted file mode 100644 index c688237b2..000000000 --- a/src/com/android/launcher3/AppsContainerSearchEditTextView.java +++ /dev/null @@ -1,65 +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.Context; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.widget.EditText; - - -/** - * The edit text for the search container - */ -public class AppsContainerSearchEditTextView extends EditText { - - /** - * Implemented by listeners of the back key. - */ - public interface OnBackKeyListener { - public void onBackKey(); - } - - private OnBackKeyListener mBackKeyListener; - - public AppsContainerSearchEditTextView(Context context) { - this(context, null); - } - - public AppsContainerSearchEditTextView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AppsContainerSearchEditTextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public void setOnBackKeyListener(OnBackKeyListener listener) { - mBackKeyListener = listener; - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - // If this is a back key, propagate the key back to the listener - if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { - if (mBackKeyListener != null) { - mBackKeyListener.onBackKey(); - } - return false; - } - return super.onKeyPreIme(keyCode, event); - } -} diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java deleted file mode 100644 index 3bfe26b56..000000000 --- a/src/com/android/launcher3/AppsContainerView.java +++ /dev/null @@ -1,983 +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.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.ComponentName; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.InsetDrawable; -import android.os.Build; -import android.support.v7.widget.RecyclerView; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.FrameLayout; -import android.widget.TextView; - -import com.android.launcher3.util.Thunk; - -import java.util.List; -import java.util.regex.Pattern; - - -/** - * Interface for controlling the header elevation in response to RecyclerView scroll. - */ -interface HeaderElevationController { - void onScroll(int scrollY); - void disable(); -} - -/** - * Implementation of the header elevation mechanism for pre-L devices. It simulates elevation - * by drawing a gradient under the header bar. - */ -final class HeaderElevationControllerV16 implements HeaderElevationController { - - private final View mShadow; - - private final float mScrollToElevation; - - public HeaderElevationControllerV16(View header) { - Resources res = header.getContext().getResources(); - mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); - - mShadow = new View(header.getContext()); - mShadow.setBackground(new GradientDrawable( - GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000})); - mShadow.setAlpha(0); - - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height)); - lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height; - - ((ViewGroup) header.getParent()).addView(mShadow, lp); - } - - @Override - public void onScroll(int scrollY) { - float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / - mScrollToElevation; - mShadow.setAlpha(elevationPct); - } - - @Override - public void disable() { - ViewGroup parent = (ViewGroup) mShadow.getParent(); - if (parent != null) { - parent.removeView(mShadow); - } - } -} - -/** - * Implementation of the header elevation mechanism for L+ devices, which makes use of the native - * view elevation. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -final class HeaderElevationControllerVL implements HeaderElevationController { - - private final View mHeader; - private final float mMaxElevation; - private final float mScrollToElevation; - - public HeaderElevationControllerVL(View header) { - mHeader = header; - - Resources res = header.getContext().getResources(); - mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation); - mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); - } - - @Override - public void onScroll(int scrollY) { - float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / - mScrollToElevation; - float newElevation = mMaxElevation * elevationPct; - if (Float.compare(mHeader.getElevation(), newElevation) != 0) { - mHeader.setElevation(newElevation); - } - } - - @Override - public void disable() { } -} - -/** - * The all apps view container. - */ -public class AppsContainerView extends BaseContainerView implements DragSource, Insettable, - TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, - AlphabeticalAppsList.AdapterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks, - View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, - ViewTreeObserver.OnPreDrawListener { - - public static final boolean GRID_MERGE_SECTIONS = true; - - private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; - private static final boolean DYNAMIC_HEADER_ELEVATION = true; - private static final boolean DISMISS_SEARCH_ON_BACK = true; - - private static final int FADE_IN_DURATION = 175; - private static final int FADE_OUT_DURATION = 100; - private static final int SEARCH_TRANSLATION_X_DP = 18; - - private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); - - @Thunk Launcher mLauncher; - @Thunk AlphabeticalAppsList mApps; - private LayoutInflater mLayoutInflater; - private AppsGridAdapter mAdapter; - private RecyclerView.LayoutManager mLayoutManager; - private RecyclerView.ItemDecoration mItemDecoration; - - private FrameLayout mContentView; - @Thunk AppsContainerRecyclerView mAppsRecyclerView; - private ViewGroup mPredictionBarView; - private View mHeaderView; - private View mSearchBarContainerView; - private View mSearchButtonView; - private View mDismissSearchButtonView; - private AppsContainerSearchEditTextView mSearchBarEditView; - - private HeaderElevationController mElevationController; - - private int mNumAppsPerRow; - private int mNumPredictedAppsPerRow; - // This coordinate is relative to this container view - private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1); - // This coordinate is relative to its parent - private final Point mIconLastTouchPos = new Point(); - // This coordinate is used to proxy click and long-click events to the prediction bar icons - private final Point mPredictionIconTouchDownPos = new Point(); - private int mContentMarginStart; - // Normal container insets - private int mContainerInset; - private int mPredictionBarHeight; - private int mLastRecyclerViewScrollPos = -1; - private boolean mFocusPredictionBarOnFirstBind; - - private CheckLongPressHelper mPredictionIconCheckForLongPress; - private View mPredictionIconUnderTouch; - - public AppsContainerView(Context context) { - this(context, null); - } - - public AppsContainerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - LauncherAppState app = LauncherAppState.getInstance(); - Resources res = context.getResources(); - - mLauncher = (Launcher) context; - DeviceProfile grid = mLauncher.getDeviceProfile(); - - mContainerInset = context.getResources().getDimensionPixelSize( - R.dimen.apps_container_inset); - mPredictionBarHeight = grid.allAppsCellHeightPx + - 2 * res.getDimensionPixelSize(R.dimen.apps_prediction_icon_top_bottom_padding); - - mLayoutInflater = LayoutInflater.from(context); - - mNumAppsPerRow = grid.appsViewNumCols; - mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols; - mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow); - mApps.setAdapterChangedCallback(this); - mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this); - mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message)); - mAdapter.setNumAppsPerRow(mNumAppsPerRow); - mAdapter.setPredictionRowHeight(mPredictionBarHeight); - mLayoutManager = mAdapter.getLayoutManager(); - mItemDecoration = mAdapter.getItemDecoration(); - mContentMarginStart = mAdapter.getContentMarginStart(); - - mApps.setAdapter(mAdapter); - } - - /** - * Sets the current set of predicted apps. - */ - public void setPredictedApps(List apps) { - mApps.setPredictedApps(apps); - } - - /** - * Sets the current set of apps. - */ - public void setApps(List apps) { - mApps.setApps(apps); - } - - /** - * Adds new apps to the list. - */ - public void addApps(List apps) { - mApps.addApps(apps); - } - - /** - * Updates existing apps in the list - */ - public void updateApps(List apps) { - mApps.updateApps(apps); - } - - /** - * Removes some apps from the list. - */ - public void removeApps(List apps) { - mApps.removeApps(apps); - } - - /** - * Hides the header bar - */ - public void hideHeaderBar() { - mHeaderView.setVisibility(View.GONE); - mElevationController.disable(); - onUpdateBackgrounds(); - onUpdatePaddings(); - } - - /** - * Scrolls this list view to the top. - */ - public void scrollToTop() { - mAppsRecyclerView.scrollToTop(); - } - - /** - * Returns the content view used for the launcher transitions. - */ - public View getContentView() { - return mContentView; - } - - /** - * Returns the reveal view used for the launcher transitions. - */ - public View getRevealView() { - return findViewById(R.id.apps_view_transition_overlay); - } - - @Override - protected void onFinishInflate() { - boolean isRtl = Utilities.isRtl(getResources()); - mAdapter.setRtl(isRtl); - - // Work around the search box getting first focus and showing the cursor by - // proxying the focus from the content view to the recycler view directly - mContentView = (FrameLayout) findViewById(R.id.apps_list); - mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (v == mContentView && hasFocus) { - if (!mApps.getPredictedApps().isEmpty()) { - // If the prediction bar is going to be bound, then defer focusing until - // it is first bound - if (mPredictionBarView.getChildCount() == 0) { - mFocusPredictionBarOnFirstBind = true; - } else { - mPredictionBarView.requestFocus(); - } - } else { - mAppsRecyclerView.requestFocus(); - } - } - } - }); - - // Fix the header view elevation if not dynamically calculating it - mHeaderView = findViewById(R.id.header); - mHeaderView.setOnClickListener(this); - - mElevationController = Utilities.isLmpOrAbove() ? - new HeaderElevationControllerVL(mHeaderView) : - new HeaderElevationControllerV16(mHeaderView); - if (!DYNAMIC_HEADER_ELEVATION) { - mElevationController.onScroll(getResources() - .getDimensionPixelSize(R.dimen.all_apps_header_scroll_to_elevation)); - } - - // Fix the prediction bar size - mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); - lp.height = mPredictionBarHeight; - - mSearchButtonView = mHeaderView.findViewById(R.id.search_button); - mSearchBarContainerView = findViewById(R.id.app_search_container); - mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button); - mDismissSearchButtonView.setOnClickListener(this); - mSearchBarEditView = (AppsContainerSearchEditTextView) findViewById(R.id.app_search_box); - if (mSearchBarEditView != null) { - mSearchBarEditView.addTextChangedListener(this); - mSearchBarEditView.setOnEditorActionListener(this); - if (DISMISS_SEARCH_ON_BACK) { - mSearchBarEditView.setOnBackKeyListener( - new AppsContainerSearchEditTextView.OnBackKeyListener() { - @Override - public void onBackKey() { - // Only hide the search field if there is no query, or if there - // are no filtered results - String query = Utilities.trim( - mSearchBarEditView.getEditableText().toString()); - if (query.isEmpty() || mApps.hasNoFilteredResults()) { - hideSearchField(true, true); - } - } - }); - } - } - mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view); - mAppsRecyclerView.setApps(mApps); - mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); - mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight); - mAppsRecyclerView.setLayoutManager(mLayoutManager); - mAppsRecyclerView.setAdapter(mAdapter); - mAppsRecyclerView.setHasFixedSize(true); - if (mItemDecoration != null) { - mAppsRecyclerView.addItemDecoration(mItemDecoration); - } - onUpdateBackgrounds(); - onUpdatePaddings(); - } - - @Override - public void onBindPredictionBar() { - updatePredictionBarVisibility(); - - List predictedApps = mApps.getPredictedApps(); - int childCount = mPredictionBarView.getChildCount(); - for (int i = 0; i < mNumPredictedAppsPerRow; i++) { - BubbleTextView icon; - if (i < childCount) { - // If a child at that index exists, then get that child - icon = (BubbleTextView) mPredictionBarView.getChildAt(i); - } else { - // Otherwise, inflate a new icon - icon = (BubbleTextView) mLayoutInflater.inflate( - R.layout.apps_prediction_bar_icon_view, mPredictionBarView, false); - icon.setFocusable(true); - mPredictionBarView.addView(icon); - } - - // Either apply the app info to the child, or hide the view - if (i < predictedApps.size()) { - if (icon.getVisibility() != View.VISIBLE) { - icon.setVisibility(View.VISIBLE); - } - icon.applyFromApplicationInfo(predictedApps.get(i)); - } else { - icon.setVisibility(View.INVISIBLE); - } - } - - if (mFocusPredictionBarOnFirstBind) { - mFocusPredictionBarOnFirstBind = false; - mPredictionBarView.requestFocus(); - } - } - - @Override - protected void onFixedBoundsUpdated() { - // Update the number of items in the grid - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = mLauncher.getDeviceProfile(); - if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) { - mNumAppsPerRow = grid.appsViewNumCols; - mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols; - mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); - mAdapter.setNumAppsPerRow(mNumAppsPerRow); - mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); - } - } - - /** - * Update the padding of the Apps view and children. To ensure that the RecyclerView has the - * full width to handle touches right to the edge of the screen, we only apply the top and - * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView - * itself. In particular, the left/right padding is applied to the background of the view, - * and then additionally inset by the start margin. - */ - @Override - protected void onUpdatePaddings() { - boolean isRtl = Utilities.isRtl(getResources()); - boolean hasSearchBar = (mSearchBarEditView != null) && - (mSearchBarEditView.getVisibility() == View.VISIBLE); - - // Set the background on the container, but let the recyclerView extend the full screen, - // so that the fast-scroller works on the edge as well. - mContentView.setPadding(0, 0, 0, 0); - - if (mFixedBounds.isEmpty()) { - // If there are no fixed bounds, then use the default padding and insets - setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right, - mContainerInset + mInsets.bottom); - } else { - // If there are fixed bounds, then we update the padding to reflect the fixed bounds. - setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, - mFixedBounds.bottom); - } - - // Update the apps recycler view, inset it by the container inset as well - DeviceProfile grid = mLauncher.getDeviceProfile(); - int startMargin = grid.isPhone ? mContentMarginStart : 0; - int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; - if (isRtl) { - mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset, - inset + startMargin, inset); - } else { - mAppsRecyclerView.setPadding(inset + startMargin, inset, - inset + mAppsRecyclerView.getScrollbarWidth(), inset); - } - - // Update the header bar - if (hasSearchBar) { - FrameLayout.LayoutParams lp = - (FrameLayout.LayoutParams) mHeaderView.getLayoutParams(); - lp.leftMargin = lp.rightMargin = inset; - mHeaderView.requestLayout(); - } - - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); - lp.leftMargin = inset + mAppsRecyclerView.getScrollbarWidth(); - lp.rightMargin = inset + mAppsRecyclerView.getScrollbarWidth(); - mPredictionBarView.requestLayout(); - } - - /** - * Update the background of the Apps view and children. - */ - @Override - protected void onUpdateBackgrounds() { - int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; - - // Update the background of the reveal view and list to be inset with the fixed bound - // insets instead of the default insets - // TODO: Use quantum_panel instead of quantum_panel_shape. - InsetDrawable background = new InsetDrawable( - getContext().getResources().getDrawable(R.drawable.quantum_panel_shape), - inset, 0, inset, 0); - mContentView.setBackground(background); - mAppsRecyclerView.updateBackgroundPadding(background); - mAdapter.updateBackgroundPadding(background); - getRevealView().setBackground(background.getConstantState().newDrawable()); - } - - @Override - public boolean onPreDraw() { - synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition()); - return true; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return handleTouchEvent(ev); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent ev) { - return handleTouchEvent(ev); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View v, MotionEvent ev) { - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY()); - break; - } - return false; - } - - @Override - public void onClick(View v) { - if (v == mHeaderView) { - showSearchField(); - } else if (v == mDismissSearchButtonView) { - hideSearchField(true, true); - } - } - - @Override - public boolean onLongClick(View v) { - // Return early if this is not initiated from a touch - if (!v.isInTouchMode()) return false; - // When we have exited all apps or are in transition, disregard long clicks - if (!mLauncher.isAppsViewVisible() || - mLauncher.getWorkspace().isSwitchingState()) return false; - // Return if global dragging is not enabled - if (!mLauncher.isDraggingEnabled()) return false; - - // Start the drag - mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false); - // Enter spring loaded mode - mLauncher.enterSpringLoadedDragMode(); - - return false; - } - - @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override - public boolean supportsAppInfoDropTarget() { - return true; - } - - @Override - public boolean supportsDeleteDropTarget() { - return false; - } - - @Override - public float getIntrinsicIconScaleFactor() { - DeviceProfile grid = mLauncher.getDeviceProfile(); - return (float) grid.allAppsIconSizePx / grid.iconSizePx; - } - - @Override - public void onFlingToDeleteCompleted() { - // We just dismiss the drag when we fling, so cleanup here - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - mLauncher.unlockScreenOrientation(false); - } - - @Override - public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, - boolean success) { - if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && - !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { - // Exit spring loaded mode if we have not successfully dropped or have not handled the - // drop in Workspace - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - } - mLauncher.unlockScreenOrientation(false); - - // Display an error message if the drag failed due to there not being enough space on the - // target layout we were dropping on. - if (!success) { - boolean showOutOfSpaceMessage = false; - if (target instanceof Workspace) { - int currentScreen = mLauncher.getCurrentWorkspaceScreen(); - Workspace workspace = (Workspace) target; - CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); - ItemInfo itemInfo = (ItemInfo) d.dragInfo; - if (layout != null) { - layout.calculateSpans(itemInfo); - showOutOfSpaceMessage = - !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); - } - } - if (showOutOfSpaceMessage) { - mLauncher.showOutOfSpaceMessage(false); - } - - d.deferDragViewCleanupPostAnimation = false; - } - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // Do nothing - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - // Do nothing - } - - @Override - public void afterTextChanged(final Editable s) { - String queryText = s.toString(); - if (queryText.isEmpty()) { - mApps.setFilter(null); - } else { - String formatStr = getResources().getString(R.string.apps_view_no_search_results); - mAdapter.setEmptySearchText(String.format(formatStr, queryText)); - - // Do an intersection of the words in the query and each title, and filter out all the - // apps that don't match all of the words in the query. - final String queryTextLower = queryText.toLowerCase(); - final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); - mApps.setFilter(new AlphabeticalAppsList.Filter() { - @Override - public boolean retainApp(AppInfo info, String sectionName) { - if (sectionName.toLowerCase().contains(queryTextLower)) { - return true; - } - String title = info.title.toString(); - String[] words = SPLIT_PATTERN.split(title.toLowerCase()); - for (int qi = 0; qi < queryWords.length; qi++) { - boolean foundMatch = false; - for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(queryWords[qi])) { - foundMatch = true; - break; - } - } - if (!foundMatch) { - // If there is a word in the query that does not match any words in this - // title, so skip it. - return false; - } - } - return true; - } - }); - } - scrollToTop(); - } - - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { - // Skip the quick-launch if there isn't exactly one item - if (mApps.getSize() != 1) { - return false; - } - - List items = mApps.getAdapterItems(); - for (int i = 0; i < items.size(); i++) { - AlphabeticalAppsList.AdapterItem item = items.get(i); - if (item.viewType == AppsGridAdapter.ICON_VIEW_TYPE) { - mAppsRecyclerView.getChildAt(i).performClick(); - getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); - return true; - } - } - } - return false; - } - - @Override - public void onAdapterItemsChanged() { - updatePredictionBarVisibility(); - } - - @Override - public View getContent() { - return null; - } - - @Override - public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - // Register for a pre-draw listener to synchronize the recycler view scroll to other views - // in this container - if (!toWorkspace) { - getViewTreeObserver().addOnPreDrawListener(this); - } - } - - @Override - public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { - // Do nothing - } - - @Override - public void onLauncherTransitionStep(Launcher l, float t) { - // Do nothing - } - - @Override - public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - if (mSearchBarEditView != null) { - if (toWorkspace) { - hideSearchField(false, false); - } - } - if (toWorkspace) { - getViewTreeObserver().removeOnPreDrawListener(this); - mLastRecyclerViewScrollPos = -1; - } - } - - /** - * Updates the container when the recycler view is scrolled. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void synchronizeToRecyclerViewScrollPosition(int scrollY) { - if (mLastRecyclerViewScrollPos != scrollY) { - mLastRecyclerViewScrollPos = scrollY; - if (DYNAMIC_HEADER_ELEVATION) { - mElevationController.onScroll(scrollY); - } - - // Scroll the prediction bar with the contents of the recycler view - mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop()); - } - } - - @Override - public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // If we were waiting for long-click, cancel the request once a child has started handling - // the scrolling - if (mPredictionIconCheckForLongPress != null) { - mPredictionIconCheckForLongPress.cancelLongPress(); - } - super.requestDisallowInterceptTouchEvent(disallowIntercept); - } - - /** - * Handles the touch events to dismiss all apps when clicking outside the bounds of the - * recycler view. - */ - private boolean handleTouchEvent(MotionEvent ev) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = mLauncher.getDeviceProfile(); - int x = (int) ev.getX(); - int y = (int) ev.getY(); - - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - // We workaround the fact that the recycler view needs the touches for the scroll - // and we want to intercept it for clicks in the prediction bar by handling clicks - // and long clicks in the prediction bar ourselves. - if (mPredictionBarView != null && mPredictionBarView.getVisibility() == View.VISIBLE) { - mPredictionIconTouchDownPos.set(x, y); - mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y); - if (mPredictionIconUnderTouch != null) { - mPredictionIconCheckForLongPress = - new CheckLongPressHelper(mPredictionIconUnderTouch, this); - mPredictionIconCheckForLongPress.postCheckForLongPress(); - } - } - - if (!mFixedBounds.isEmpty()) { - // Outset the fixed bounds and check if the touch is outside all apps - Rect tmpRect = new Rect(mFixedBounds); - tmpRect.inset(-grid.allAppsIconSizePx / 2, 0); - if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) { - mBoundsCheckLastTouchDownPos.set(x, y); - return true; - } - } else { - // Check if the touch is outside all apps - if (ev.getX() < getPaddingLeft() || - ev.getX() > (getWidth() - getPaddingRight())) { - mBoundsCheckLastTouchDownPos.set(x, y); - return true; - } - } - break; - case MotionEvent.ACTION_MOVE: - if (mPredictionIconUnderTouch != null) { - float dist = (float) Math.hypot(x - mPredictionIconTouchDownPos.x, - y - mPredictionIconTouchDownPos.y); - if (dist > ViewConfiguration.get(getContext()).getScaledTouchSlop()) { - if (mPredictionIconCheckForLongPress != null) { - mPredictionIconCheckForLongPress.cancelLongPress(); - } - mPredictionIconCheckForLongPress = null; - mPredictionIconUnderTouch = null; - } - } - break; - case MotionEvent.ACTION_UP: - if (mBoundsCheckLastTouchDownPos.x > -1) { - ViewConfiguration viewConfig = ViewConfiguration.get(getContext()); - float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x; - float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y; - float distance = (float) Math.hypot(dx, dy); - if (distance < viewConfig.getScaledTouchSlop()) { - // The background was clicked, so just go home - Launcher launcher = (Launcher) getContext(); - launcher.showWorkspace(true); - return true; - } - } - - // Trigger the click on the prediction bar icon if that's where we touched - if (mPredictionIconUnderTouch != null && - !mPredictionIconCheckForLongPress.hasPerformedLongPress()) { - mLauncher.onClick(mPredictionIconUnderTouch); - } - - // Fall through - case MotionEvent.ACTION_CANCEL: - mBoundsCheckLastTouchDownPos.set(-1, -1); - mPredictionIconTouchDownPos.set(-1, -1); - - // On touch up/cancel, cancel the long press on the prediction bar icon if it has - // not yet been performed - if (mPredictionIconCheckForLongPress != null) { - mPredictionIconCheckForLongPress.cancelLongPress(); - mPredictionIconCheckForLongPress = null; - } - mPredictionIconUnderTouch = null; - - break; - } - return false; - } - - /** - * Returns the predicted app in the prediction bar given a set of local coordinates. - */ - private View findPredictedAppAtCoordinate(int x, int y) { - Rect hitRect = new Rect(); - - // Ensure we aren't hitting the search bar - int[] coord = {x, y}; - Utilities.mapCoordInSelfToDescendent(mHeaderView, this, coord); - mHeaderView.getHitRect(hitRect); - if (hitRect.contains(coord[0], coord[1])) { - return null; - } - - // Check against the children of the prediction bar - coord[0] = x; - coord[1] = y; - Utilities.mapCoordInSelfToDescendent(mPredictionBarView, this, coord); - for (int i = 0; i < mPredictionBarView.getChildCount(); i++) { - View child = mPredictionBarView.getChildAt(i); - if (child.getVisibility() != View.VISIBLE) { - continue; - } - child.getHitRect(hitRect); - if (hitRect.contains(coord[0], coord[1])) { - return child; - } - } - return null; - } - - /** - * Shows the search field. - */ - private void showSearchField() { - // Show the search bar and focus the search - final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, - getContext().getResources().getDisplayMetrics()); - mSearchBarContainerView.setVisibility(View.VISIBLE); - mSearchBarContainerView.setAlpha(0f); - mSearchBarContainerView.setTranslationX(translationX); - mSearchBarContainerView.animate() - .alpha(1f) - .translationX(0) - .setDuration(FADE_IN_DURATION) - .withLayer() - .withEndAction(new Runnable() { - @Override - public void run() { - mSearchBarEditView.requestFocus(); - getInputMethodManager().showSoftInput(mSearchBarEditView, - InputMethodManager.SHOW_IMPLICIT); - } - }); - mSearchButtonView.animate() - .alpha(0f) - .translationX(-translationX) - .setDuration(FADE_OUT_DURATION) - .withLayer(); - } - - /** - * Hides the search field. - */ - private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { - final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; - final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, - getContext().getResources().getDisplayMetrics()); - if (animated) { - // Hide the search bar and focus the recycler view - mSearchBarContainerView.animate() - .alpha(0f) - .translationX(0) - .setDuration(FADE_IN_DURATION) - .withLayer() - .withEndAction(new Runnable() { - @Override - public void run() { - mSearchBarContainerView.setVisibility(View.INVISIBLE); - if (resetTextField) { - mSearchBarEditView.setText(""); - } - mApps.setFilter(null); - if (returnFocusToRecyclerView) { - mAppsRecyclerView.requestFocus(); - } - } - }); - mSearchButtonView.setTranslationX(-translationX); - mSearchButtonView.animate() - .alpha(1f) - .translationX(0) - .setDuration(FADE_OUT_DURATION) - .withLayer(); - } else { - mSearchBarContainerView.setVisibility(View.INVISIBLE); - if (resetTextField) { - mSearchBarEditView.setText(""); - } - mApps.setFilter(null); - mSearchButtonView.setAlpha(1f); - mSearchButtonView.setTranslationX(0f); - if (returnFocusToRecyclerView) { - mAppsRecyclerView.requestFocus(); - } - } - getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); - } - - /** - * Updates the visibility of the prediction bar. - * @return whether the prediction bar is visible - */ - private boolean updatePredictionBarVisibility() { - boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() && (!mApps.hasFilter() || - mSearchBarEditView.getEditableText().toString().isEmpty()); - if (showPredictionBar) { - mPredictionBarView.setVisibility(View.VISIBLE); - } else if (!showPredictionBar) { - mPredictionBarView.setVisibility(View.INVISIBLE); - } - return showPredictionBar; - } - - /** - * Returns an input method manager. - */ - private InputMethodManager getInputMethodManager() { - return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - } -} diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java deleted file mode 100644 index a593a5715..000000000 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ /dev/null @@ -1,465 +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.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import com.android.launcher3.util.Thunk; - -import java.util.HashMap; -import java.util.List; - - -/** - * The grid view adapter of all the apps. - */ -class AppsGridAdapter extends RecyclerView.Adapter { - - public static final String TAG = "AppsGridAdapter"; - private static final boolean DEBUG = false; - - // A section break in the grid - public static final int SECTION_BREAK_VIEW_TYPE = 0; - // A normal icon - public static final int ICON_VIEW_TYPE = 1; - // The message shown when there are no filtered results - public static final int EMPTY_VIEW_TYPE = 2; - // The spacer used for the prediction bar - public static final int PREDICTION_BAR_SPACER_TYPE = 3; - - /** - * Callback for when the prediction bar spacer is bound. - */ - public interface PredictionBarSpacerCallbacks { - void onBindPredictionBar(); - } - - /** - * ViewHolder for each icon. - */ - public static class ViewHolder extends RecyclerView.ViewHolder { - public View mContent; - - public ViewHolder(View v) { - super(v); - mContent = v; - } - } - - /** - * Helper class to size the grid items. - */ - public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup { - - public GridSpanSizer() { - super(); - setSpanIndexCacheEnabled(true); - } - - @Override - public int getSpanSize(int position) { - if (mApps.hasNoFilteredResults()) { - // Empty view spans full width - return mAppsPerRow; - } - - if (mApps.getAdapterItems().get(position).viewType != AppsGridAdapter.ICON_VIEW_TYPE) { - // Both the section breaks and predictive bar span the full width - return mAppsPerRow; - } else { - return 1; - } - } - } - - /** - * Helper class to draw the section headers - */ - public class GridItemDecoration extends RecyclerView.ItemDecoration { - - private static final boolean FADE_OUT_SECTIONS = false; - - private HashMap mCachedSectionBounds = new HashMap<>(); - private Rect mTmpBounds = new Rect(); - private Launcher mLauncher; - - public GridItemDecoration(Context context) { - mLauncher = (Launcher) context; - } - - @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - if (mApps.hasFilter()) { - return; - } - - DeviceProfile grid = mLauncher.getDeviceProfile(); - List items = mApps.getAdapterItems(); - boolean hasDrawnPredictedAppsDivider = false; - int childCount = parent.getChildCount(); - int lastSectionTop = 0; - int lastSectionHeight = 0; - for (int i = 0; i < childCount; i++) { - View child = parent.getChildAt(i); - ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); - if (!isValidHolderAndChild(holder, child, items)) { - continue; - } - - if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) { - // Draw the divider under the predicted apps - int top = child.getTop() + child.getHeight(); - c.drawLine(mBackgroundPadding.left, top, - parent.getWidth() - mBackgroundPadding.right, top, - mPredictedAppsDividerPaint); - hasDrawnPredictedAppsDivider = true; - - } else if (grid.isPhone && shouldDrawItemSection(holder, i, items)) { - // At this point, we only draw sections for each section break; - int viewTopOffset = (2 * child.getPaddingTop()); - int pos = holder.getPosition(); - AlphabeticalAppsList.AdapterItem item = items.get(pos); - AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo; - - // Draw all the sections for this index - String lastSectionName = item.sectionName; - for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) { - AlphabeticalAppsList.AdapterItem nextItem = items.get(pos); - String sectionName = nextItem.sectionName; - if (nextItem.sectionInfo != sectionInfo) { - break; - } - if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) { - continue; - } - - - // Find the section name bounds - PointF sectionBounds = getAndCacheSectionBounds(sectionName); - - // Calculate where to draw the section - int sectionBaseline = (int) (viewTopOffset + sectionBounds.y); - int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : - mPaddingStart; - x += (int) ((mStartMargin - sectionBounds.x) / 2f); - int y = child.getTop() + sectionBaseline; - - // Determine whether this is the last row with apps in that section, if - // so, then fix the section to the row allowing it to scroll past the - // baseline, otherwise, bound it to the baseline so it's in the viewport - int appIndexInSection = items.get(pos).sectionAppIndex; - int nextRowPos = Math.min(items.size() - 1, - pos + mAppsPerRow - (appIndexInSection % mAppsPerRow)); - AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos); - boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName); - if (!fixedToRow) { - y = Math.max(sectionBaseline, y); - } - - // In addition, if it overlaps with the last section that was drawn, then - // offset it so that it does not overlap - if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) { - y += lastSectionTop - y + lastSectionHeight; - } - - // Draw the section header - if (FADE_OUT_SECTIONS) { - int alpha = 255; - if (fixedToRow) { - alpha = Math.min(255, - (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); - } - mSectionTextPaint.setAlpha(alpha); - } - c.drawText(sectionName, x, y, mSectionTextPaint); - - lastSectionTop = y; - lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset); - lastSectionName = sectionName; - } - i += (sectionInfo.numApps - item.sectionAppIndex); - } - } - } - - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, - RecyclerView.State state) { - // Do nothing - } - - /** - * Given a section name, return the bounds of the given section name. - */ - private PointF getAndCacheSectionBounds(String sectionName) { - PointF bounds = mCachedSectionBounds.get(sectionName); - if (bounds == null) { - mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds); - bounds = new PointF(mSectionTextPaint.measureText(sectionName), mTmpBounds.height()); - mCachedSectionBounds.put(sectionName, bounds); - } - return bounds; - } - - /** - * Returns whether we consider this a valid view holder for us to draw a divider or section for. - */ - private boolean isValidHolderAndChild(ViewHolder holder, View child, - List items) { - // Ensure item is not already removed - GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) - child.getLayoutParams(); - if (lp.isItemRemoved()) { - return false; - } - // Ensure we have a valid holder - if (holder == null) { - return false; - } - // Ensure we have a holder position - int pos = holder.getPosition(); - if (pos < 0 || pos >= items.size()) { - return false; - } - return true; - } - - /** - * Returns whether to draw the divider for a given child. - */ - private boolean shouldDrawItemDivider(ViewHolder holder, - List items) { - int pos = holder.getPosition(); - return items.get(pos).viewType == AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE; - } - - /** - * Returns whether to draw the section for the given child. - */ - private boolean shouldDrawItemSection(ViewHolder holder, int childIndex, - List items) { - int pos = holder.getPosition(); - AlphabeticalAppsList.AdapterItem item = items.get(pos); - - // Ensure it's an icon - if (item.viewType != AppsGridAdapter.ICON_VIEW_TYPE) { - return false; - } - // Draw the section header for the first item in each section - return (childIndex == 0) || - (items.get(pos - 1).viewType == AppsGridAdapter.SECTION_BREAK_VIEW_TYPE); - } - } - - private Handler mHandler; - private LayoutInflater mLayoutInflater; - @Thunk AlphabeticalAppsList mApps; - private GridLayoutManager mGridLayoutMgr; - private GridSpanSizer mGridSizer; - private GridItemDecoration mItemDecoration; - private PredictionBarSpacerCallbacks mPredictionBarCb; - private View.OnTouchListener mTouchListener; - private View.OnClickListener mIconClickListener; - private View.OnLongClickListener mIconLongClickListener; - @Thunk final Rect mBackgroundPadding = new Rect(); - @Thunk int mPredictionBarHeight; - @Thunk int mAppsPerRow; - @Thunk boolean mIsRtl; - private String mEmptySearchText; - - // Section drawing - @Thunk int mPaddingStart; - @Thunk int mStartMargin; - @Thunk int mSectionHeaderOffset; - @Thunk Paint mSectionTextPaint; - @Thunk Paint mPredictedAppsDividerPaint; - - public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, - PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener, - View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) { - Resources res = context.getResources(); - mHandler = new Handler(); - mApps = apps; - mAppsPerRow = appsPerRow; - mPredictionBarCb = pbCb; - mGridSizer = new GridSpanSizer(); - mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL, - false); - mGridLayoutMgr.setSpanSizeLookup(mGridSizer); - mItemDecoration = new GridItemDecoration(context); - mLayoutInflater = LayoutInflater.from(context); - mTouchListener = touchListener; - mIconClickListener = iconClickListener; - mIconLongClickListener = iconLongClickListener; - mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); - mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset); - mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset); - - mSectionTextPaint = new Paint(); - mSectionTextPaint.setTextSize(res.getDimensionPixelSize( - R.dimen.apps_view_section_text_size)); - mSectionTextPaint.setColor(res.getColor(R.color.apps_view_section_text_color)); - mSectionTextPaint.setAntiAlias(true); - - mPredictedAppsDividerPaint = new Paint(); - mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics())); - mPredictedAppsDividerPaint.setColor(0x1E000000); - mPredictedAppsDividerPaint.setAntiAlias(true); - } - - /** - * Sets the number of apps per row. - */ - public void setNumAppsPerRow(int appsPerRow) { - mAppsPerRow = appsPerRow; - mGridLayoutMgr.setSpanCount(appsPerRow); - } - - /** - * Sets the prediction row height. - */ - public void setPredictionRowHeight(int height) { - mPredictionBarHeight = height; - } - - /** - * Sets whether we are in RTL mode. - */ - public void setRtl(boolean rtl) { - mIsRtl = rtl; - } - - /** - * Sets the text to show when there are no apps. - */ - public void setEmptySearchText(String query) { - mEmptySearchText = query; - } - - /** - * Notifies the adapter of the background padding so that it can draw things correctly in the - * item decorator. - */ - public void updateBackgroundPadding(Drawable background) { - background.getPadding(mBackgroundPadding); - } - - /** - * Returns the grid layout manager. - */ - public GridLayoutManager getLayoutManager() { - return mGridLayoutMgr; - } - - /** - * Returns the item decoration for the recycler view. - */ - public RecyclerView.ItemDecoration getItemDecoration() { - // We don't draw any headers when we are uncomfortably dense - return mItemDecoration; - } - - /** - * Returns the left padding for the recycler view. - */ - public int getContentMarginStart() { - return mStartMargin; - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - switch (viewType) { - case EMPTY_VIEW_TYPE: - return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent, - false)); - case SECTION_BREAK_VIEW_TYPE: - return new ViewHolder(new View(parent.getContext())); - case PREDICTION_BAR_SPACER_TYPE: - // Create a view of a specific height to match the floating prediction bar - View v = new View(parent.getContext()); - ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, mPredictionBarHeight); - v.setLayoutParams(lp); - return new ViewHolder(v); - case ICON_VIEW_TYPE: - BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - R.layout.apps_grid_icon_view, parent, false); - icon.setOnTouchListener(mTouchListener); - icon.setOnClickListener(mIconClickListener); - icon.setOnLongClickListener(mIconLongClickListener); - icon.setFocusable(true); - return new ViewHolder(icon); - default: - throw new RuntimeException("Unexpected view type"); - } - } - - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - switch (holder.getItemViewType()) { - case ICON_VIEW_TYPE: - AppInfo info = mApps.getAdapterItems().get(position).appInfo; - BubbleTextView icon = (BubbleTextView) holder.mContent; - icon.applyFromApplicationInfo(info); - break; - case PREDICTION_BAR_SPACER_TYPE: - mHandler.post(new Runnable() { - @Override - public void run() { - if (mPredictionBarCb != null) { - mPredictionBarCb.onBindPredictionBar(); - } - } - }); - break; - case EMPTY_VIEW_TYPE: - TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text); - emptyViewText.setText(mEmptySearchText); - break; - } - } - - @Override - public int getItemCount() { - if (mApps.hasNoFilteredResults()) { - // For the empty view - return 1; - } - return mApps.getAdapterItems().size(); - } - - @Override - public int getItemViewType(int position) { - if (mApps.hasNoFilteredResults()) { - return EMPTY_VIEW_TYPE; - } - - AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); - return item.viewType; - } -} diff --git a/src/com/android/launcher3/AppsRecyclerViewContainer.java b/src/com/android/launcher3/AppsRecyclerViewContainer.java deleted file mode 100644 index 6411bace4..000000000 --- a/src/com/android/launcher3/AppsRecyclerViewContainer.java +++ /dev/null @@ -1,61 +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.Context; -import android.graphics.Bitmap; -import android.util.AttributeSet; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler; - -public class AppsRecyclerViewContainer extends FrameLayout implements BubbleTextShadowHandler { - - private final ClickShadowView mTouchFeedbackView; - - public AppsRecyclerViewContainer(Context context) { - this(context, null); - } - - public AppsRecyclerViewContainer(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AppsRecyclerViewContainer(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - Launcher launcher = (Launcher) context; - DeviceProfile grid = launcher.getDeviceProfile(); - - mTouchFeedbackView = new ClickShadowView(context); - - // Make the feedback view large enough to hold the blur bitmap. - int size = grid.allAppsIconSizePx + mTouchFeedbackView.getExtraSize(); - addView(mTouchFeedbackView, size, size); - } - - @Override - public void setPressedIcon(BubbleTextView icon, Bitmap background) { - if (icon == null || background == null) { - mTouchFeedbackView.setBitmap(null); - mTouchFeedbackView.animate().cancel(); - } else if (mTouchFeedbackView.setBitmap(background)) { - mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent()); - mTouchFeedbackView.animateShadow(); - } - } -} diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java deleted file mode 100644 index e52d88708..000000000 --- a/src/com/android/launcher3/BaseContainerRecyclerView.java +++ /dev/null @@ -1,102 +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.Context; -import android.support.v7.widget.RecyclerView; -import android.util.AttributeSet; -import android.view.MotionEvent; -import com.android.launcher3.util.Thunk; - -/** - * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling - * velocity is below a predefined threshold. - */ -public class BaseContainerRecyclerView extends RecyclerView - implements RecyclerView.OnItemTouchListener { - - private static final int SCROLL_DELTA_THRESHOLD_DP = 4; - - /** Keeps the last known scrolling delta/velocity along y-axis. */ - @Thunk int mDy = 0; - private float mDeltaThreshold; - - public BaseContainerRecyclerView(Context context) { - this(context, null); - } - - public BaseContainerRecyclerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public BaseContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP; - - ScrollListener listener = new ScrollListener(); - setOnScrollListener(listener); - } - - private class ScrollListener extends OnScrollListener { - public ScrollListener() { - // Do nothing - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mDy = dy; - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - addOnItemTouchListener(this); - } - - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - if (shouldStopScroll(ev)) { - stopScroll(); - } - return false; - } - - @Override - public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - // Do nothing. - } - - public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS - } - - /** - * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. - */ - protected boolean shouldStopScroll(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if ((Math.abs(mDy) < mDeltaThreshold && - getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { - // now the touch events are being passed to the {@link WidgetCell} until the - // touch sequence goes over the touch slop. - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java new file mode 100644 index 000000000..b63ef788a --- /dev/null +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -0,0 +1,102 @@ +/* + * 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.Context; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.MotionEvent; +import com.android.launcher3.util.Thunk; + +/** + * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling + * velocity is below a predefined threshold. + */ +public class BaseRecyclerView extends RecyclerView + implements RecyclerView.OnItemTouchListener { + + private static final int SCROLL_DELTA_THRESHOLD_DP = 4; + + /** Keeps the last known scrolling delta/velocity along y-axis. */ + @Thunk int mDy = 0; + private float mDeltaThreshold; + + public BaseRecyclerView(Context context) { + this(context, null); + } + + public BaseRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP; + + ScrollListener listener = new ScrollListener(); + setOnScrollListener(listener); + } + + private class ScrollListener extends OnScrollListener { + public ScrollListener() { + // Do nothing + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mDy = dy; + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + addOnItemTouchListener(this); + } + + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { + if (shouldStopScroll(ev)) { + stopScroll(); + } + return false; + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent ev) { + // Do nothing. + } + + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS + } + + /** + * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. + */ + protected boolean shouldStopScroll(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if ((Math.abs(mDy) < mDeltaThreshold && + getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) { + // now the touch events are being passed to the {@link WidgetCell} until the + // touch sequence goes over the touch slop. + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 8f2056569..bee6cb093 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -70,7 +70,7 @@ public class DeviceProfile { public int iconSizePx; public int iconTextSizePx; public int iconDrawablePaddingPx; - private final int iconDrawablePaddingOriginalPx; + public int iconDrawablePaddingOriginalPx; public int cellWidthPx; public int cellHeightPx; @@ -88,11 +88,8 @@ public class DeviceProfile { private int hotseatBarHeightPx; // All apps - private int allAppsCellWidthPx; - public int allAppsCellHeightPx; - private final int allAppsCellPaddingPx; - public int appsViewNumCols; - public int appsViewNumPredictiveCols; + public int allAppsNumCols; + public int allAppsNumPredictiveCols; public int allAppsButtonVisualSize; public final int allAppsIconSizePx; public final int allAppsIconTextSizePx; @@ -129,8 +126,6 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height); defaultPageSpacingPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing); - allAppsCellPaddingPx = - res.getDimensionPixelSize(R.dimen.dynamic_grid_all_apps_cell_padding); overviewModeMinIconZoneHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); overviewModeMaxIconZoneHeightPx = @@ -143,7 +138,6 @@ public class DeviceProfile { res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f; overviewModeScaleFactor = res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f; - iconDrawablePaddingOriginalPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); @@ -230,25 +224,22 @@ public class DeviceProfile { folderBackgroundOffset = -edgeMarginPx; folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset; - // All Apps - allAppsCellWidthPx = allAppsIconSizePx; - allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + allAppsIconTextSizePx; - - int appsContainerViewWidthPx = res.getDimensionPixelSize(R.dimen.apps_container_width); - updateAppsViewNumCols(res, appsContainerViewWidthPx); + updateAppsViewNumCols(res, 0); } public boolean updateAppsViewNumCols(Resources res, int containerWidth) { int appsViewLeftMarginPx = - res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin); + res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); + int allAppsCellPaddingPx = + res.getDimensionPixelSize(R.dimen.all_apps_icon_left_right_padding); int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) / - (allAppsCellWidthPx + 2 * allAppsCellPaddingPx); + (allAppsIconSizePx + 2 * allAppsCellPaddingPx); int numPredictiveAppCols = isPhone ? 4 : numAppsCols; - if ((numAppsCols != appsViewNumCols) || - (numPredictiveAppCols != appsViewNumPredictiveCols)) { - appsViewNumCols = numAppsCols; - appsViewNumPredictiveCols = numPredictiveAppCols; + if ((numAppsCols != allAppsNumCols) || + (numPredictiveAppCols != allAppsNumPredictiveCols)) { + allAppsNumCols = numAppsCols; + allAppsNumPredictiveCols = numPredictiveAppCols; return true; } return false; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index c923c9582..5dd64e0e2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -98,6 +98,7 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; @@ -272,7 +273,7 @@ public class Launcher extends Activity private SearchDropTargetBar mSearchDropTargetBar; // Main container view for the all apps screen. - @Thunk AppsContainerView mAppsView; + @Thunk AllAppsContainerView mAppsView; // Main container view and the model for the widget tray screen. @Thunk WidgetsContainerView mWidgetsView; @@ -1475,7 +1476,7 @@ public class Launcher extends Activity mDragLayer.findViewById(R.id.search_drop_target_bar); // Setup Apps - mAppsView = (AppsContainerView) findViewById(R.id.apps_view); + mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view); if (isAllAppsSearchOverridden()) { mAppsView.hideHeaderBar(); } @@ -1524,8 +1525,6 @@ public class Launcher extends Activity * Creates a view representing a shortcut. * * @param info The data structure describing the shortcut. - * - * @return A View inflated from R.layout.application. */ View createShortcut(ShortcutInfo info) { return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); @@ -1540,7 +1539,7 @@ public class Launcher extends Activity * @return A View inflated from layoutResId. */ public View createShortcut(ViewGroup parent, ShortcutInfo info) { - BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.application, + BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon, parent, false); favorite.applyFromShortcutInfo(info, mIconCache); favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx); @@ -1875,7 +1874,7 @@ public class Launcher extends Activity return mDragLayer; } - public AppsContainerView getAppsView() { + public AllAppsContainerView getAppsView() { return mAppsView; } @@ -3347,7 +3346,7 @@ public class Launcher extends Activity } } - protected void showWorkspace(boolean animated) { + public void showWorkspace(boolean animated) { showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null, true); } @@ -4813,14 +4812,6 @@ public class Launcher extends Activity } } -interface LauncherTransitionable { - View getContent(); - void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace); - void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace); - void onLauncherTransitionStep(Launcher l, float t); - void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace); -} - interface DebugIntents { static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE"; static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE"; diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index ce13da66e..f373fde2d 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -23,14 +23,13 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.content.res.Resources; -import android.os.Build; import android.util.Log; import android.view.View; import android.view.ViewAnimationUtils; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetsContainerView; import java.util.HashMap; @@ -129,7 +128,7 @@ public class LauncherStateTransitionAnimation { * Starts an animation to the apps view. */ public void startAnimationToAllApps(final boolean animated) { - final AppsContainerView toView = mLauncher.getAppsView(); + final AllAppsContainerView toView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { private int[] mAllAppsToPanelDelta; @@ -233,9 +232,9 @@ public class LauncherStateTransitionAnimation { final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); - final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); final int itemsAlphaStagger = - res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); + res.getInteger(R.integer.config_overlayItemsAlphaStagger); final View allAppsButtonView = mLauncher.getAllAppsButton(); final View fromView = mLauncher.getWorkspace(); @@ -428,7 +427,7 @@ public class LauncherStateTransitionAnimation { private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, final Workspace.State toWorkspaceState, final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { - AppsContainerView appsView = mLauncher.getAppsView(); + AllAppsContainerView appsView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { int[] mAllAppsToPanelDelta; @@ -530,9 +529,9 @@ public class LauncherStateTransitionAnimation { final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); - final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); final int itemsAlphaStagger = - res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); + res.getInteger(R.integer.config_overlayItemsAlphaStagger); final View allAppsButtonView = mLauncher.getAllAppsButton(); final View toView = mLauncher.getWorkspace(); diff --git a/src/com/android/launcher3/LauncherTransitionable.java b/src/com/android/launcher3/LauncherTransitionable.java new file mode 100644 index 000000000..9442abcde --- /dev/null +++ b/src/com/android/launcher3/LauncherTransitionable.java @@ -0,0 +1,30 @@ +/* + * 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.view.View; + +/** + * An interface to get callbacks during a launcher transition. + */ +public interface LauncherTransitionable { + View getContent(); + void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace); + void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace); + void onLauncherTransitionStep(Launcher l, float t); + void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace); +} diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 1cf3bc469..e8cc48685 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -25,7 +25,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Handler; -import android.os.Process; import android.util.Log; import android.util.LongSparseArray; import com.android.launcher3.compat.AppWidgetManagerCompat; @@ -34,7 +33,6 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetCell; -import junit.framework.Assert; import java.util.ArrayList; import java.util.Collections; diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 42ba36ef6..340066d64 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -163,9 +163,9 @@ public class WorkspaceStateTransitionAnimation { DeviceProfile grid = mLauncher.getDeviceProfile(); Resources res = launcher.getResources(); - mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime); + mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime); mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); - mOverlayTransitionTime = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime); + mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime); mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java new file mode 100644 index 000000000..60f9ab347 --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -0,0 +1,1018 @@ +/* + * 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.allapps; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.ComponentName; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; +import android.os.Build; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.FrameLayout; +import android.widget.TextView; +import com.android.launcher3.AppInfo; +import com.android.launcher3.BaseContainerView; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.CellLayout; +import com.android.launcher3.CheckLongPressHelper; +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; +import com.android.launcher3.Folder; +import com.android.launcher3.Insettable; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherTransitionable; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace; +import com.android.launcher3.util.Thunk; + +import java.util.List; +import java.util.regex.Pattern; + + +/** + * Interface for controlling the header elevation in response to RecyclerView scroll. + */ +interface HeaderElevationController { + void onScroll(int scrollY); + void updateBackgroundPadding(Drawable bg); + void disable(); +} + +/** + * Implementation of the header elevation mechanism for pre-L devices. It simulates elevation + * by drawing a gradient under the header bar. + */ +final class HeaderElevationControllerV16 implements HeaderElevationController { + + private final View mShadow; + private final float mScrollToElevation; + private final Rect mTmpRect = new Rect(); + + public HeaderElevationControllerV16(View header) { + Resources res = header.getContext().getResources(); + mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); + + mShadow = new View(header.getContext()); + mShadow.setBackground(new GradientDrawable( + GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000})); + mShadow.setAlpha(0); + + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height)); + lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height; + + ((ViewGroup) header.getParent()).addView(mShadow, lp); + } + + @Override + public void onScroll(int scrollY) { + float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / + mScrollToElevation; + mShadow.setAlpha(elevationPct); + } + + @Override + public void updateBackgroundPadding(Drawable bg) { + bg.getPadding(mTmpRect); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams(); + lp.leftMargin = mTmpRect.left; + lp.rightMargin = mTmpRect.right; + mShadow.requestLayout(); + } + + @Override + public void disable() { + ViewGroup parent = (ViewGroup) mShadow.getParent(); + if (parent != null) { + parent.removeView(mShadow); + } + } +} + +/** + * Implementation of the header elevation mechanism for L+ devices, which makes use of the native + * view elevation. + */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +final class HeaderElevationControllerVL implements HeaderElevationController { + + private final View mHeader; + private final float mMaxElevation; + private final float mScrollToElevation; + + public HeaderElevationControllerVL(View header) { + mHeader = header; + + Resources res = header.getContext().getResources(); + mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation); + mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); + } + + @Override + public void onScroll(int scrollY) { + float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / + mScrollToElevation; + float newElevation = mMaxElevation * elevationPct; + if (Float.compare(mHeader.getElevation(), newElevation) != 0) { + mHeader.setElevation(newElevation); + } + } + + @Override + public void updateBackgroundPadding(Drawable bg) { + // Do nothing, the background padding on the header view is already applied + } + + @Override + public void disable() { } +} + +/** + * The all apps view container. + */ +public class AllAppsContainerView extends BaseContainerView implements DragSource, Insettable, + TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, + AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks, + View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, + ViewTreeObserver.OnPreDrawListener { + + public static final boolean GRID_MERGE_SECTIONS = true; + + private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; + private static final boolean DYNAMIC_HEADER_ELEVATION = true; + private static final boolean DISMISS_SEARCH_ON_BACK = true; + + private static final int FADE_IN_DURATION = 175; + private static final int FADE_OUT_DURATION = 100; + private static final int SEARCH_TRANSLATION_X_DP = 18; + + private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); + + @Thunk Launcher mLauncher; + @Thunk AlphabeticalAppsList mApps; + private LayoutInflater mLayoutInflater; + private AllAppsGridAdapter mAdapter; + private RecyclerView.LayoutManager mLayoutManager; + private RecyclerView.ItemDecoration mItemDecoration; + + private FrameLayout mContentView; + @Thunk AllAppsRecyclerView mAppsRecyclerView; + private ViewGroup mPredictionBarView; + private View mHeaderView; + private View mSearchBarContainerView; + private View mSearchButtonView; + private View mDismissSearchButtonView; + private AllAppsSearchEditView mSearchBarEditView; + + private HeaderElevationController mElevationController; + + private int mNumAppsPerRow; + private int mNumPredictedAppsPerRow; + // This coordinate is relative to this container view + private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1); + // This coordinate is relative to its parent + private final Point mIconLastTouchPos = new Point(); + // This coordinate is used to proxy click and long-click events to the prediction bar icons + private final Point mPredictionIconTouchDownPos = new Point(); + private int mContentMarginStart; + // Normal container insets + private int mContainerInset; + private int mPredictionBarHeight; + private int mLastRecyclerViewScrollPos = -1; + private boolean mFocusPredictionBarOnFirstBind; + + private CheckLongPressHelper mPredictionIconCheckForLongPress; + private View mPredictionIconUnderTouch; + + public AllAppsContainerView(Context context) { + this(context, null); + } + + public AllAppsContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + LauncherAppState app = LauncherAppState.getInstance(); + Resources res = context.getResources(); + + mLauncher = (Launcher) context; + DeviceProfile grid = mLauncher.getDeviceProfile(); + + mContainerInset = context.getResources().getDimensionPixelSize( + R.dimen.all_apps_container_inset); + mPredictionBarHeight = grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx + + grid.allAppsIconTextSizePx + + 2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_top_bottom_padding); + + mLayoutInflater = LayoutInflater.from(context); + + mNumAppsPerRow = grid.allAppsNumCols; + mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols; + mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow); + mApps.setAdapterChangedCallback(this); + mAdapter = new AllAppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this); + mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message)); + mAdapter.setNumAppsPerRow(mNumAppsPerRow); + mAdapter.setPredictionRowHeight(mPredictionBarHeight); + mLayoutManager = mAdapter.getLayoutManager(); + mItemDecoration = mAdapter.getItemDecoration(); + mContentMarginStart = mAdapter.getContentMarginStart(); + + mApps.setAdapter(mAdapter); + } + + /** + * Sets the current set of predicted apps. + */ + public void setPredictedApps(List apps) { + mApps.setPredictedApps(apps); + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + mApps.setApps(apps); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + mApps.addApps(apps); + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + mApps.updateApps(apps); + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + mApps.removeApps(apps); + } + + /** + * Hides the header bar + */ + public void hideHeaderBar() { + mHeaderView.setVisibility(View.GONE); + mElevationController.disable(); + onUpdateBackgrounds(); + onUpdatePaddings(); + } + + /** + * Scrolls this list view to the top. + */ + public void scrollToTop() { + mAppsRecyclerView.scrollToTop(); + } + + /** + * Returns the content view used for the launcher transitions. + */ + public View getContentView() { + return mContentView; + } + + /** + * Returns the reveal view used for the launcher transitions. + */ + public View getRevealView() { + return findViewById(R.id.apps_view_transition_overlay); + } + + @Override + protected void onFinishInflate() { + boolean isRtl = Utilities.isRtl(getResources()); + mAdapter.setRtl(isRtl); + + // Work around the search box getting first focus and showing the cursor by + // proxying the focus from the content view to the recycler view directly + mContentView = (FrameLayout) findViewById(R.id.apps_list); + mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (v == mContentView && hasFocus) { + if (!mApps.getPredictedApps().isEmpty()) { + // If the prediction bar is going to be bound, then defer focusing until + // it is first bound + if (mPredictionBarView.getChildCount() == 0) { + mFocusPredictionBarOnFirstBind = true; + } else { + mPredictionBarView.requestFocus(); + } + } else { + mAppsRecyclerView.requestFocus(); + } + } + } + }); + + // Fix the header view elevation if not dynamically calculating it + mHeaderView = findViewById(R.id.header); + mHeaderView.setOnClickListener(this); + + mElevationController = Utilities.isLmpOrAbove() ? + new HeaderElevationControllerVL(mHeaderView) : + new HeaderElevationControllerV16(mHeaderView); + if (!DYNAMIC_HEADER_ELEVATION) { + mElevationController.onScroll(getResources() + .getDimensionPixelSize(R.dimen.all_apps_header_scroll_to_elevation)); + } + + // Fix the prediction bar size + mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); + lp.height = mPredictionBarHeight; + + mSearchButtonView = mHeaderView.findViewById(R.id.search_button); + mSearchBarContainerView = findViewById(R.id.app_search_container); + mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button); + mDismissSearchButtonView.setOnClickListener(this); + mSearchBarEditView = (AllAppsSearchEditView) findViewById(R.id.apps_search_box); + if (mSearchBarEditView != null) { + mSearchBarEditView.addTextChangedListener(this); + mSearchBarEditView.setOnEditorActionListener(this); + if (DISMISS_SEARCH_ON_BACK) { + mSearchBarEditView.setOnBackKeyListener( + new AllAppsSearchEditView.OnBackKeyListener() { + @Override + public void onBackKey() { + // Only hide the search field if there is no query, or if there + // are no filtered results + String query = Utilities.trim( + mSearchBarEditView.getEditableText().toString()); + if (query.isEmpty() || mApps.hasNoFilteredResults()) { + hideSearchField(true, true); + } + } + }); + } + } + mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view); + mAppsRecyclerView.setApps(mApps); + mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); + mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight); + mAppsRecyclerView.setLayoutManager(mLayoutManager); + mAppsRecyclerView.setAdapter(mAdapter); + mAppsRecyclerView.setHasFixedSize(true); + if (mItemDecoration != null) { + mAppsRecyclerView.addItemDecoration(mItemDecoration); + } + onUpdateBackgrounds(); + onUpdatePaddings(); + } + + @Override + public void onBindPredictionBar() { + updatePredictionBarVisibility(); + + List predictedApps = mApps.getPredictedApps(); + int childCount = mPredictionBarView.getChildCount(); + for (int i = 0; i < mNumPredictedAppsPerRow; i++) { + BubbleTextView icon; + if (i < childCount) { + // If a child at that index exists, then get that child + icon = (BubbleTextView) mPredictionBarView.getChildAt(i); + } else { + // Otherwise, inflate a new icon + icon = (BubbleTextView) mLayoutInflater.inflate( + R.layout.all_apps_prediction_bar_icon, mPredictionBarView, false); + icon.setFocusable(true); + mPredictionBarView.addView(icon); + } + + // Either apply the app info to the child, or hide the view + if (i < predictedApps.size()) { + if (icon.getVisibility() != View.VISIBLE) { + icon.setVisibility(View.VISIBLE); + } + icon.applyFromApplicationInfo(predictedApps.get(i)); + } else { + icon.setVisibility(View.INVISIBLE); + } + } + + if (mFocusPredictionBarOnFirstBind) { + mFocusPredictionBarOnFirstBind = false; + mPredictionBarView.requestFocus(); + } + } + + @Override + protected void onFixedBoundsUpdated() { + // Update the number of items in the grid + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = mLauncher.getDeviceProfile(); + if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) { + mNumAppsPerRow = grid.allAppsNumCols; + mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols; + mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); + mAdapter.setNumAppsPerRow(mNumAppsPerRow); + mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); + } + } + + /** + * Update the padding of the Apps view and children. To ensure that the RecyclerView has the + * full width to handle touches right to the edge of the screen, we only apply the top and + * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView + * itself. In particular, the left/right padding is applied to the background of the view, + * and then additionally inset by the start margin. + */ + @Override + protected void onUpdatePaddings() { + boolean isRtl = Utilities.isRtl(getResources()); + boolean hasSearchBar = (mSearchBarEditView != null) && + (mSearchBarEditView.getVisibility() == View.VISIBLE); + + // Set the background on the container, but let the recyclerView extend the full screen, + // so that the fast-scroller works on the edge as well. + mContentView.setPadding(0, 0, 0, 0); + + if (mFixedBounds.isEmpty()) { + // If there are no fixed bounds, then use the default padding and insets + setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right, + mContainerInset + mInsets.bottom); + } else { + // If there are fixed bounds, then we update the padding to reflect the fixed bounds. + setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, + mFixedBounds.bottom); + } + + // Update the apps recycler view, inset it by the container inset as well + DeviceProfile grid = mLauncher.getDeviceProfile(); + int startMargin = grid.isPhone ? mContentMarginStart : 0; + int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + if (isRtl) { + mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset, + inset + startMargin, inset); + } else { + mAppsRecyclerView.setPadding(inset + startMargin, inset, + inset + mAppsRecyclerView.getScrollbarWidth(), inset); + } + + // Update the header bar + if (hasSearchBar) { + FrameLayout.LayoutParams lp = + (FrameLayout.LayoutParams) mHeaderView.getLayoutParams(); + lp.leftMargin = lp.rightMargin = inset; + mHeaderView.requestLayout(); + } + + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); + lp.leftMargin = inset + mAppsRecyclerView.getScrollbarWidth(); + lp.rightMargin = inset + mAppsRecyclerView.getScrollbarWidth(); + mPredictionBarView.requestLayout(); + } + + /** + * Update the background of the Apps view and children. + */ + @Override + protected void onUpdateBackgrounds() { + int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + + // Update the background of the reveal view and list to be inset with the fixed bound + // insets instead of the default insets + // TODO: Use quantum_panel instead of quantum_panel_shape. + InsetDrawable background = new InsetDrawable( + getContext().getResources().getDrawable(R.drawable.quantum_panel_shape), + inset, 0, inset, 0); + mContentView.setBackground(background); + mAppsRecyclerView.updateBackgroundPadding(background); + mAdapter.updateBackgroundPadding(background); + mElevationController.updateBackgroundPadding(background); + getRevealView().setBackground(background.getConstantState().newDrawable()); + } + + @Override + public boolean onPreDraw() { + synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition()); + return true; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return handleTouchEvent(ev); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent ev) { + return handleTouchEvent(ev); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY()); + break; + } + return false; + } + + @Override + public void onClick(View v) { + if (v == mHeaderView) { + showSearchField(); + } else if (v == mDismissSearchButtonView) { + hideSearchField(true, true); + } + } + + @Override + public boolean onLongClick(View v) { + // Return early if this is not initiated from a touch + if (!v.isInTouchMode()) return false; + // When we have exited all apps or are in transition, disregard long clicks + if (!mLauncher.isAppsViewVisible() || + mLauncher.getWorkspace().isSwitchingState()) return false; + // Return if global dragging is not enabled + if (!mLauncher.isDraggingEnabled()) return false; + + // Start the drag + mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false); + // Enter spring loaded mode + mLauncher.enterSpringLoadedDragMode(); + + return false; + } + + @Override + public boolean supportsFlingToDelete() { + return true; + } + + @Override + public boolean supportsAppInfoDropTarget() { + return true; + } + + @Override + public boolean supportsDeleteDropTarget() { + return false; + } + + @Override + public float getIntrinsicIconScaleFactor() { + DeviceProfile grid = mLauncher.getDeviceProfile(); + return (float) grid.allAppsIconSizePx / grid.iconSizePx; + } + + @Override + public void onFlingToDeleteCompleted() { + // We just dismiss the drag when we fling, so cleanup here + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + mLauncher.unlockScreenOrientation(false); + } + + @Override + public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, + boolean success) { + if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && + !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { + // Exit spring loaded mode if we have not successfully dropped or have not handled the + // drop in Workspace + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + mLauncher.unlockScreenOrientation(false); + + // Display an error message if the drag failed due to there not being enough space on the + // target layout we were dropping on. + if (!success) { + boolean showOutOfSpaceMessage = false; + if (target instanceof Workspace) { + int currentScreen = mLauncher.getCurrentWorkspaceScreen(); + Workspace workspace = (Workspace) target; + CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); + ItemInfo itemInfo = (ItemInfo) d.dragInfo; + if (layout != null) { + layout.calculateSpans(itemInfo); + showOutOfSpaceMessage = + !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); + } + } + if (showOutOfSpaceMessage) { + mLauncher.showOutOfSpaceMessage(false); + } + + d.deferDragViewCleanupPostAnimation = false; + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + @Override + public void afterTextChanged(final Editable s) { + String queryText = s.toString(); + if (queryText.isEmpty()) { + mApps.setFilter(null); + } else { + String formatStr = getResources().getString(R.string.all_apps_no_search_results); + mAdapter.setEmptySearchText(String.format(formatStr, queryText)); + + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = queryText.toLowerCase(); + final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); + mApps.setFilter(new AlphabeticalAppsList.Filter() { + @Override + public boolean retainApp(AppInfo info, String sectionName) { + if (sectionName.toLowerCase().contains(queryTextLower)) { + return true; + } + String title = info.title.toString(); + String[] words = SPLIT_PATTERN.split(title.toLowerCase()); + for (int qi = 0; qi < queryWords.length; qi++) { + boolean foundMatch = false; + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(queryWords[qi])) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + // If there is a word in the query that does not match any words in this + // title, so skip it. + return false; + } + } + return true; + } + }); + } + scrollToTop(); + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { + // Skip the quick-launch if there isn't exactly one item + if (mApps.getSize() != 1) { + return false; + } + + List items = mApps.getAdapterItems(); + for (int i = 0; i < items.size(); i++) { + AlphabeticalAppsList.AdapterItem item = items.get(i); + if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { + mAppsRecyclerView.getChildAt(i).performClick(); + getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); + return true; + } + } + } + return false; + } + + @Override + public void onAdapterItemsChanged() { + updatePredictionBarVisibility(); + } + + @Override + public View getContent() { + return null; + } + + @Override + public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { + // Register for a pre-draw listener to synchronize the recycler view scroll to other views + // in this container + if (!toWorkspace) { + getViewTreeObserver().addOnPreDrawListener(this); + } + } + + @Override + public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { + // Do nothing + } + + @Override + public void onLauncherTransitionStep(Launcher l, float t) { + // Do nothing + } + + @Override + public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { + if (mSearchBarEditView != null) { + if (toWorkspace) { + hideSearchField(false, false); + } + } + if (toWorkspace) { + getViewTreeObserver().removeOnPreDrawListener(this); + mLastRecyclerViewScrollPos = -1; + } + } + + /** + * Updates the container when the recycler view is scrolled. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void synchronizeToRecyclerViewScrollPosition(int scrollY) { + if (mLastRecyclerViewScrollPos != scrollY) { + mLastRecyclerViewScrollPos = scrollY; + if (DYNAMIC_HEADER_ELEVATION) { + mElevationController.onScroll(scrollY); + } + + // Scroll the prediction bar with the contents of the recycler view + mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop()); + } + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + // If we were waiting for long-click, cancel the request once a child has started handling + // the scrolling + if (mPredictionIconCheckForLongPress != null) { + mPredictionIconCheckForLongPress.cancelLongPress(); + } + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + + /** + * Handles the touch events to dismiss all apps when clicking outside the bounds of the + * recycler view. + */ + private boolean handleTouchEvent(MotionEvent ev) { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = mLauncher.getDeviceProfile(); + int x = (int) ev.getX(); + int y = (int) ev.getY(); + + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + // We workaround the fact that the recycler view needs the touches for the scroll + // and we want to intercept it for clicks in the prediction bar by handling clicks + // and long clicks in the prediction bar ourselves. + if (mPredictionBarView != null && mPredictionBarView.getVisibility() == View.VISIBLE) { + mPredictionIconTouchDownPos.set(x, y); + mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y); + if (mPredictionIconUnderTouch != null) { + mPredictionIconCheckForLongPress = + new CheckLongPressHelper(mPredictionIconUnderTouch, this); + mPredictionIconCheckForLongPress.postCheckForLongPress(); + } + } + + if (!mFixedBounds.isEmpty()) { + // Outset the fixed bounds and check if the touch is outside all apps + Rect tmpRect = new Rect(mFixedBounds); + tmpRect.inset(-grid.allAppsIconSizePx / 2, 0); + if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) { + mBoundsCheckLastTouchDownPos.set(x, y); + return true; + } + } else { + // Check if the touch is outside all apps + if (ev.getX() < getPaddingLeft() || + ev.getX() > (getWidth() - getPaddingRight())) { + mBoundsCheckLastTouchDownPos.set(x, y); + return true; + } + } + break; + case MotionEvent.ACTION_MOVE: + if (mPredictionIconUnderTouch != null) { + float dist = (float) Math.hypot(x - mPredictionIconTouchDownPos.x, + y - mPredictionIconTouchDownPos.y); + if (dist > ViewConfiguration.get(getContext()).getScaledTouchSlop()) { + if (mPredictionIconCheckForLongPress != null) { + mPredictionIconCheckForLongPress.cancelLongPress(); + } + mPredictionIconCheckForLongPress = null; + mPredictionIconUnderTouch = null; + } + } + break; + case MotionEvent.ACTION_UP: + if (mBoundsCheckLastTouchDownPos.x > -1) { + ViewConfiguration viewConfig = ViewConfiguration.get(getContext()); + float dx = ev.getX() - mBoundsCheckLastTouchDownPos.x; + float dy = ev.getY() - mBoundsCheckLastTouchDownPos.y; + float distance = (float) Math.hypot(dx, dy); + if (distance < viewConfig.getScaledTouchSlop()) { + // The background was clicked, so just go home + Launcher launcher = (Launcher) getContext(); + launcher.showWorkspace(true); + return true; + } + } + + // Trigger the click on the prediction bar icon if that's where we touched + if (mPredictionIconUnderTouch != null && + !mPredictionIconCheckForLongPress.hasPerformedLongPress()) { + mLauncher.onClick(mPredictionIconUnderTouch); + } + + // Fall through + case MotionEvent.ACTION_CANCEL: + mBoundsCheckLastTouchDownPos.set(-1, -1); + mPredictionIconTouchDownPos.set(-1, -1); + + // On touch up/cancel, cancel the long press on the prediction bar icon if it has + // not yet been performed + if (mPredictionIconCheckForLongPress != null) { + mPredictionIconCheckForLongPress.cancelLongPress(); + mPredictionIconCheckForLongPress = null; + } + mPredictionIconUnderTouch = null; + + break; + } + return false; + } + + /** + * Returns the predicted app in the prediction bar given a set of local coordinates. + */ + private View findPredictedAppAtCoordinate(int x, int y) { + Rect hitRect = new Rect(); + + // Ensure we aren't hitting the search bar + int[] coord = {x, y}; + Utilities.mapCoordInSelfToDescendent(mHeaderView, this, coord); + mHeaderView.getHitRect(hitRect); + if (hitRect.contains(coord[0], coord[1])) { + return null; + } + + // Check against the children of the prediction bar + coord[0] = x; + coord[1] = y; + Utilities.mapCoordInSelfToDescendent(mPredictionBarView, this, coord); + for (int i = 0; i < mPredictionBarView.getChildCount(); i++) { + View child = mPredictionBarView.getChildAt(i); + if (child.getVisibility() != View.VISIBLE) { + continue; + } + child.getHitRect(hitRect); + if (hitRect.contains(coord[0], coord[1])) { + return child; + } + } + return null; + } + + /** + * Shows the search field. + */ + private void showSearchField() { + // Show the search bar and focus the search + final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, + getContext().getResources().getDisplayMetrics()); + mSearchBarContainerView.setVisibility(View.VISIBLE); + mSearchBarContainerView.setAlpha(0f); + mSearchBarContainerView.setTranslationX(translationX); + mSearchBarContainerView.animate() + .alpha(1f) + .translationX(0) + .setDuration(FADE_IN_DURATION) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mSearchBarEditView.requestFocus(); + getInputMethodManager().showSoftInput(mSearchBarEditView, + InputMethodManager.SHOW_IMPLICIT); + } + }); + mSearchButtonView.animate() + .alpha(0f) + .translationX(-translationX) + .setDuration(FADE_OUT_DURATION) + .withLayer(); + } + + /** + * Hides the search field. + */ + private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; + final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, + getContext().getResources().getDisplayMetrics()); + if (animated) { + // Hide the search bar and focus the recycler view + mSearchBarContainerView.animate() + .alpha(0f) + .translationX(0) + .setDuration(FADE_IN_DURATION) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mSearchBarContainerView.setVisibility(View.INVISIBLE); + if (resetTextField) { + mSearchBarEditView.setText(""); + } + mApps.setFilter(null); + if (returnFocusToRecyclerView) { + mAppsRecyclerView.requestFocus(); + } + } + }); + mSearchButtonView.setTranslationX(-translationX); + mSearchButtonView.animate() + .alpha(1f) + .translationX(0) + .setDuration(FADE_OUT_DURATION) + .withLayer(); + } else { + mSearchBarContainerView.setVisibility(View.INVISIBLE); + if (resetTextField) { + mSearchBarEditView.setText(""); + } + mApps.setFilter(null); + mSearchButtonView.setAlpha(1f); + mSearchButtonView.setTranslationX(0f); + if (returnFocusToRecyclerView) { + mAppsRecyclerView.requestFocus(); + } + } + getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); + } + + /** + * Updates the visibility of the prediction bar. + * @return whether the prediction bar is visible + */ + private boolean updatePredictionBarVisibility() { + boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() && (!mApps.hasFilter() || + mSearchBarEditView.getEditableText().toString().isEmpty()); + if (showPredictionBar) { + mPredictionBarView.setVisibility(View.VISIBLE); + } else if (!showPredictionBar) { + mPredictionBarView.setVisibility(View.INVISIBLE); + } + return showPredictionBar; + } + + /** + * Returns an input method manager. + */ + private InputMethodManager getInputMethodManager() { + return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + } +} diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java new file mode 100644 index 000000000..e010270ce --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -0,0 +1,471 @@ +/* + * 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.allapps; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import com.android.launcher3.AppInfo; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.Thunk; + +import java.util.HashMap; +import java.util.List; + + +/** + * The grid view adapter of all the apps. + */ +class AllAppsGridAdapter extends RecyclerView.Adapter { + + public static final String TAG = "AppsGridAdapter"; + private static final boolean DEBUG = false; + + // A section break in the grid + public static final int SECTION_BREAK_VIEW_TYPE = 0; + // A normal icon + public static final int ICON_VIEW_TYPE = 1; + // The message shown when there are no filtered results + public static final int EMPTY_SEARCH_VIEW_TYPE = 2; + // The spacer used for the prediction bar + public static final int PREDICTION_BAR_SPACER_TYPE = 3; + + /** + * Callback for when the prediction bar spacer is bound. + */ + public interface PredictionBarSpacerCallbacks { + void onBindPredictionBar(); + } + + /** + * ViewHolder for each icon. + */ + public static class ViewHolder extends RecyclerView.ViewHolder { + public View mContent; + + public ViewHolder(View v) { + super(v); + mContent = v; + } + } + + /** + * Helper class to size the grid items. + */ + public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup { + + public GridSpanSizer() { + super(); + setSpanIndexCacheEnabled(true); + } + + @Override + public int getSpanSize(int position) { + if (mApps.hasNoFilteredResults()) { + // Empty view spans full width + return mAppsPerRow; + } + + if (mApps.getAdapterItems().get(position).viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) { + // Both the section breaks and predictive bar span the full width + return mAppsPerRow; + } else { + return 1; + } + } + } + + /** + * Helper class to draw the section headers + */ + public class GridItemDecoration extends RecyclerView.ItemDecoration { + + private static final boolean FADE_OUT_SECTIONS = false; + + private HashMap mCachedSectionBounds = new HashMap<>(); + private Rect mTmpBounds = new Rect(); + private Launcher mLauncher; + + public GridItemDecoration(Context context) { + mLauncher = (Launcher) context; + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + if (mApps.hasFilter()) { + return; + } + + DeviceProfile grid = mLauncher.getDeviceProfile(); + List items = mApps.getAdapterItems(); + boolean hasDrawnPredictedAppsDivider = false; + int childCount = parent.getChildCount(); + int lastSectionTop = 0; + int lastSectionHeight = 0; + for (int i = 0; i < childCount; i++) { + View child = parent.getChildAt(i); + ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child); + if (!isValidHolderAndChild(holder, child, items)) { + continue; + } + + if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) { + // Draw the divider under the predicted apps + int top = child.getTop() + child.getHeight(); + c.drawLine(mBackgroundPadding.left, top, + parent.getWidth() - mBackgroundPadding.right, top, + mPredictedAppsDividerPaint); + hasDrawnPredictedAppsDivider = true; + + } else if (grid.isPhone && shouldDrawItemSection(holder, i, items)) { + // At this point, we only draw sections for each section break; + int viewTopOffset = (2 * child.getPaddingTop()); + int pos = holder.getPosition(); + AlphabeticalAppsList.AdapterItem item = items.get(pos); + AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo; + + // Draw all the sections for this index + String lastSectionName = item.sectionName; + for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) { + AlphabeticalAppsList.AdapterItem nextItem = items.get(pos); + String sectionName = nextItem.sectionName; + if (nextItem.sectionInfo != sectionInfo) { + break; + } + if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) { + continue; + } + + + // Find the section name bounds + PointF sectionBounds = getAndCacheSectionBounds(sectionName); + + // Calculate where to draw the section + int sectionBaseline = (int) (viewTopOffset + sectionBounds.y); + int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin : + mPaddingStart; + x += (int) ((mStartMargin - sectionBounds.x) / 2f); + int y = child.getTop() + sectionBaseline; + + // Determine whether this is the last row with apps in that section, if + // so, then fix the section to the row allowing it to scroll past the + // baseline, otherwise, bound it to the baseline so it's in the viewport + int appIndexInSection = items.get(pos).sectionAppIndex; + int nextRowPos = Math.min(items.size() - 1, + pos + mAppsPerRow - (appIndexInSection % mAppsPerRow)); + AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos); + boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName); + if (!fixedToRow) { + y = Math.max(sectionBaseline, y); + } + + // In addition, if it overlaps with the last section that was drawn, then + // offset it so that it does not overlap + if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) { + y += lastSectionTop - y + lastSectionHeight; + } + + // Draw the section header + if (FADE_OUT_SECTIONS) { + int alpha = 255; + if (fixedToRow) { + alpha = Math.min(255, + (int) (255 * (Math.max(0, y) / (float) sectionBaseline))); + } + mSectionTextPaint.setAlpha(alpha); + } + c.drawText(sectionName, x, y, mSectionTextPaint); + + lastSectionTop = y; + lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset); + lastSectionName = sectionName; + } + i += (sectionInfo.numApps - item.sectionAppIndex); + } + } + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, + RecyclerView.State state) { + // Do nothing + } + + /** + * Given a section name, return the bounds of the given section name. + */ + private PointF getAndCacheSectionBounds(String sectionName) { + PointF bounds = mCachedSectionBounds.get(sectionName); + if (bounds == null) { + mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds); + bounds = new PointF(mSectionTextPaint.measureText(sectionName), mTmpBounds.height()); + mCachedSectionBounds.put(sectionName, bounds); + } + return bounds; + } + + /** + * Returns whether we consider this a valid view holder for us to draw a divider or section for. + */ + private boolean isValidHolderAndChild(ViewHolder holder, View child, + List items) { + // Ensure item is not already removed + GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) + child.getLayoutParams(); + if (lp.isItemRemoved()) { + return false; + } + // Ensure we have a valid holder + if (holder == null) { + return false; + } + // Ensure we have a holder position + int pos = holder.getPosition(); + if (pos < 0 || pos >= items.size()) { + return false; + } + return true; + } + + /** + * Returns whether to draw the divider for a given child. + */ + private boolean shouldDrawItemDivider(ViewHolder holder, + List items) { + int pos = holder.getPosition(); + return items.get(pos).viewType == AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE; + } + + /** + * Returns whether to draw the section for the given child. + */ + private boolean shouldDrawItemSection(ViewHolder holder, int childIndex, + List items) { + int pos = holder.getPosition(); + AlphabeticalAppsList.AdapterItem item = items.get(pos); + + // Ensure it's an icon + if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) { + return false; + } + // Draw the section header for the first item in each section + return (childIndex == 0) || + (items.get(pos - 1).viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE); + } + } + + private Handler mHandler; + private LayoutInflater mLayoutInflater; + @Thunk AlphabeticalAppsList mApps; + private GridLayoutManager mGridLayoutMgr; + private GridSpanSizer mGridSizer; + private GridItemDecoration mItemDecoration; + private PredictionBarSpacerCallbacks mPredictionBarCb; + private View.OnTouchListener mTouchListener; + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + @Thunk final Rect mBackgroundPadding = new Rect(); + @Thunk int mPredictionBarHeight; + @Thunk int mAppsPerRow; + @Thunk boolean mIsRtl; + private String mEmptySearchText; + + // Section drawing + @Thunk int mPaddingStart; + @Thunk int mStartMargin; + @Thunk int mSectionHeaderOffset; + @Thunk Paint mSectionTextPaint; + @Thunk Paint mPredictedAppsDividerPaint; + + public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow, + PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener, + View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) { + Resources res = context.getResources(); + mHandler = new Handler(); + mApps = apps; + mAppsPerRow = appsPerRow; + mPredictionBarCb = pbCb; + mGridSizer = new GridSpanSizer(); + mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL, + false); + mGridLayoutMgr.setSpanSizeLookup(mGridSizer); + mItemDecoration = new GridItemDecoration(context); + mLayoutInflater = LayoutInflater.from(context); + mTouchListener = touchListener; + mIconClickListener = iconClickListener; + mIconLongClickListener = iconLongClickListener; + mStartMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); + mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset); + mPaddingStart = res.getDimensionPixelSize(R.dimen.all_apps_container_inset); + + mSectionTextPaint = new Paint(); + mSectionTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.all_apps_grid_section_text_size)); + mSectionTextPaint.setColor(res.getColor(R.color.all_apps_grid_section_text_color)); + mSectionTextPaint.setAntiAlias(true); + + mPredictedAppsDividerPaint = new Paint(); + mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics())); + mPredictedAppsDividerPaint.setColor(0x1E000000); + mPredictedAppsDividerPaint.setAntiAlias(true); + } + + /** + * Sets the number of apps per row. + */ + public void setNumAppsPerRow(int appsPerRow) { + mAppsPerRow = appsPerRow; + mGridLayoutMgr.setSpanCount(appsPerRow); + } + + /** + * Sets the prediction row height. + */ + public void setPredictionRowHeight(int height) { + mPredictionBarHeight = height; + } + + /** + * Sets whether we are in RTL mode. + */ + public void setRtl(boolean rtl) { + mIsRtl = rtl; + } + + /** + * Sets the text to show when there are no apps. + */ + public void setEmptySearchText(String query) { + mEmptySearchText = query; + } + + /** + * Notifies the adapter of the background padding so that it can draw things correctly in the + * item decorator. + */ + public void updateBackgroundPadding(Drawable background) { + background.getPadding(mBackgroundPadding); + } + + /** + * Returns the grid layout manager. + */ + public GridLayoutManager getLayoutManager() { + return mGridLayoutMgr; + } + + /** + * Returns the item decoration for the recycler view. + */ + public RecyclerView.ItemDecoration getItemDecoration() { + // We don't draw any headers when we are uncomfortably dense + return mItemDecoration; + } + + /** + * Returns the left padding for the recycler view. + */ + public int getContentMarginStart() { + return mStartMargin; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case EMPTY_SEARCH_VIEW_TYPE: + return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, parent, + false)); + case SECTION_BREAK_VIEW_TYPE: + return new ViewHolder(new View(parent.getContext())); + case PREDICTION_BAR_SPACER_TYPE: + // Create a view of a specific height to match the floating prediction bar + View v = new View(parent.getContext()); + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, mPredictionBarHeight); + v.setLayoutParams(lp); + return new ViewHolder(v); + case ICON_VIEW_TYPE: + BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( + R.layout.all_apps_icon, parent, false); + icon.setOnTouchListener(mTouchListener); + icon.setOnClickListener(mIconClickListener); + icon.setOnLongClickListener(mIconLongClickListener); + icon.setFocusable(true); + return new ViewHolder(icon); + default: + throw new RuntimeException("Unexpected view type"); + } + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + switch (holder.getItemViewType()) { + case ICON_VIEW_TYPE: + AppInfo info = mApps.getAdapterItems().get(position).appInfo; + BubbleTextView icon = (BubbleTextView) holder.mContent; + icon.applyFromApplicationInfo(info); + break; + case PREDICTION_BAR_SPACER_TYPE: + mHandler.post(new Runnable() { + @Override + public void run() { + if (mPredictionBarCb != null) { + mPredictionBarCb.onBindPredictionBar(); + } + } + }); + break; + case EMPTY_SEARCH_VIEW_TYPE: + TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text); + emptyViewText.setText(mEmptySearchText); + break; + } + } + + @Override + public int getItemCount() { + if (mApps.hasNoFilteredResults()) { + // For the empty view + return 1; + } + return mApps.getAdapterItems().size(); + } + + @Override + public int getItemViewType(int position) { + if (mApps.hasNoFilteredResults()) { + return EMPTY_SEARCH_VIEW_TYPE; + } + + AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); + return item.viewType; + } +} diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java new file mode 100644 index 000000000..e95fa325a --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -0,0 +1,528 @@ +/* + * 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.allapps; + +import android.animation.ObjectAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +import java.util.List; + +/** + * A RecyclerView with custom fastscroll support. This is the main container for the all apps + * icons. + */ +public class AllAppsRecyclerView extends BaseRecyclerView { + + /** + * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() + * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so + * that we can calculate what the scroll bar looks like, and where to jump to from the fast + * scroller. + */ + private static class ScrollPositionState { + // The index of the first visible row + int rowIndex; + // The offset of the first visible row + int rowTopOffset; + // The height of a given row (they are currently all the same height) + int rowHeight; + } + + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; + + private AlphabeticalAppsList mApps; + private int mNumAppsPerRow; + private int mNumPredictedAppsPerRow; + + private Drawable mScrollbar; + private Drawable mFastScrollerBg; + private Rect mTmpFastScrollerInvalidateRect = new Rect(); + private Rect mFastScrollerBounds = new Rect(); + private Rect mVerticalScrollbarBounds = new Rect(); + private boolean mDraggingFastScroller; + private String mFastScrollSectionName; + private Paint mFastScrollTextPaint; + private Rect mFastScrollTextBounds = new Rect(); + private float mFastScrollAlpha; + private int mPredictionBarHeight; + private int mDownX; + private int mDownY; + private int mLastX; + private int mLastY; + private int mScrollbarWidth; + private int mScrollbarMinHeight; + private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); + private ScrollPositionState mScrollPosState = new ScrollPositionState(); + + private Launcher mLauncher; + + public AllAppsRecyclerView(Context context) { + this(context, null); + } + + public AllAppsRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr); + + mLauncher = (Launcher) context; + Resources res = context.getResources(); + int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size); + mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb); + mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg); + mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); + mFastScrollTextPaint = new Paint(); + mFastScrollTextPaint.setColor(Color.WHITE); + mFastScrollTextPaint.setAntiAlias(true); + mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.all_apps_fast_scroll_text_size)); + mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); + mScrollbarMinHeight = + res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); + mScrollbarInset = + res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); + setFastScrollerAlpha(getFastScrollerAlpha()); + setOverScrollMode(View.OVER_SCROLL_NEVER); + } + + /** + * Sets the list of apps in this view, used to determine the fastscroll position. + */ + public void setApps(AlphabeticalAppsList apps) { + mApps = apps; + } + + /** + * Sets the number of apps per row in this recycler view. + */ + public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) { + mNumAppsPerRow = numAppsPerRow; + mNumPredictedAppsPerRow = numPredictedAppsPerRow; + + DeviceProfile grid = mLauncher.getDeviceProfile(); + RecyclerView.RecycledViewPool pool = getRecycledViewPool(); + int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); + pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1); + pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1); + pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow); + pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows); + } + + public void updateBackgroundPadding(Drawable background) { + background.getPadding(mBackgroundPadding); + } + + /** + * Sets the prediction bar height. + */ + public void setPredictionBarHeight(int height) { + mPredictionBarHeight = height; + } + + /** + * Sets the fast scroller alpha. + */ + public void setFastScrollerAlpha(float alpha) { + mFastScrollAlpha = alpha; + invalidateFastScroller(mFastScrollerBounds); + } + + /** + * Gets the fast scroller alpha. + */ + public float getFastScrollerAlpha() { + return mFastScrollAlpha; + } + + /** + * Returns the scroll bar width. + */ + public int getScrollbarWidth() { + return mScrollbarWidth; + } + + /** + * Scrolls this recycler view to the top. + */ + public void scrollToTop() { + scrollToPosition(0); + } + + /** + * Returns the current scroll position. + */ + public int getScrollPosition() { + List items = mApps.getAdapterItems(); + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex != -1) { + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; + return getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + + predictionBarHeight - mScrollPosState.rowTopOffset; + } + return 0; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + addOnItemTouchListener(this); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + drawVerticalScrubber(canvas); + drawFastScrollerPopup(canvas); + } + + /** + * We intercept the touch handling only to support fast scrolling when initiated from the + * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. + */ + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { + return handleTouchEvent(ev); + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent ev) { + handleTouchEvent(ev); + } + + /** + * Handles the touch event and determines whether to show the fast scroller (or updates it if + * it is already showing). + */ + private boolean handleTouchEvent(MotionEvent ev) { + ViewConfiguration config = ViewConfiguration.get(getContext()); + + int action = ev.getAction(); + int x = (int) ev.getX(); + int y = (int) ev.getY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + // Keep track of the down positions + mDownX = mLastX = x; + mDownY = mLastY = y; + if (shouldStopScroll(ev)) { + stopScroll(); + } + break; + case MotionEvent.ACTION_MOVE: + // Check if we are scrolling + if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && + Math.abs(y - mDownY) > config.getScaledTouchSlop()) { + getParent().requestDisallowInterceptTouchEvent(true); + mDraggingFastScroller = true; + animateFastScrollerVisibility(true); + } + if (mDraggingFastScroller) { + mLastX = x; + mLastY = y; + + // Scroll to the right position, and update the section name + int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); + int bottom = getHeight() - getPaddingBottom() - + (mFastScrollerBg.getBounds().height() / 2); + float boundedY = (float) Math.max(top, Math.min(bottom, y)); + mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / + (bottom - top)); + + // Combine the old and new fast scroller bounds to create the full invalidate + // rect + mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); + updateFastScrollerBounds(); + mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); + invalidateFastScroller(mTmpFastScrollerInvalidateRect); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mDraggingFastScroller = false; + animateFastScrollerVisibility(false); + break; + } + return mDraggingFastScroller; + } + + /** + * Animates the visibility of the fast scroller popup. + */ + private void animateFastScrollerVisibility(boolean visible) { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); + anim.setDuration(visible ? 200 : 150); + anim.start(); + } + + /** + * Returns whether a given point is near the scrollbar. + */ + private boolean isPointNearScrollbar(int x, int y) { + // Check if we are scrolling + updateVerticalScrollbarBounds(); + mVerticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); + return mVerticalScrollbarBounds.contains(x, y); + } + + /** + * Draws the fast scroller popup. + */ + private void drawFastScrollerPopup(Canvas canvas) { + if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { + // Draw the fast scroller popup + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); + mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollerBg.draw(canvas); + mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, + mFastScrollSectionName.length(), mFastScrollTextBounds); + float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); + canvas.drawText(mFastScrollSectionName, + (mFastScrollerBounds.width() - textWidth) / 2, + mFastScrollerBounds.height() - + (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, + mFastScrollTextPaint); + canvas.restoreToCount(restoreCount); + } + } + + /** + * Draws the vertical scrollbar. + */ + private void drawVerticalScrubber(Canvas canvas) { + updateVerticalScrollbarBounds(); + + // Draw the scroll bar + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(mVerticalScrollbarBounds.left, mVerticalScrollbarBounds.top); + mScrollbar.setBounds(0, 0, mScrollbarWidth, mVerticalScrollbarBounds.height()); + mScrollbar.draw(canvas); + canvas.restoreToCount(restoreCount); + } + + /** + * Invalidates the fast scroller popup. + */ + private void invalidateFastScroller(Rect bounds) { + invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + /** + * Maps the touch (from 0..1) to the adapter position that should be visible. + */ + private String scrollToPositionAtProgress(float touchFraction) { + // Ensure that we have any sections + List fastScrollSections = + mApps.getFastScrollerSections(); + if (fastScrollSections.isEmpty()) { + return ""; + } + + // Stop the scroller if it is scrolling + LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); + stopScroll(); + + // If there is a prediction bar, then capture the appropriate area for the prediction bar + float predictionBarFraction = 0f; + if (!mApps.getPredictedApps().isEmpty()) { + predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize(); + if (touchFraction <= predictionBarFraction) { + // Scroll to the top of the view, where the prediction bar is + layoutManager.scrollToPositionWithOffset(0, 0); + return ""; + } + } + + // Since the app ranges are from 0..1, we need to map the touch fraction back to 0..1 from + // predictionBarFraction..1 + touchFraction = (touchFraction - predictionBarFraction) * + (1f / (1f - predictionBarFraction)); + AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0); + for (int i = 1; i < fastScrollSections.size(); i++) { + AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i); + if (lastScrollSection.appRangeFraction <= touchFraction && + touchFraction < scrollSection.appRangeFraction) { + break; + } + lastScrollSection = scrollSection; + } + + // Scroll to the view at the position, anchored at the top of the screen. We call the scroll + // method on the LayoutManager directly since it is not exposed by RecyclerView. + layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); + + return lastScrollSection.sectionName; + } + + /** + * Updates the bounds for the scrollbar. + */ + private void updateVerticalScrollbarBounds() { + List items = mApps.getAdapterItems(); + + // Skip early if there are no items + if (items.isEmpty()) { + mVerticalScrollbarBounds.setEmpty(); + return; + } + + // Find the index and height of the first visible row (all rows have the same height) + int x; + int y; + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; + int rowCount = getNumRows(); + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex != -1) { + int height = getHeight() - getPaddingTop() - getPaddingBottom(); + int totalScrollHeight = rowCount * mScrollPosState.rowHeight + predictionBarHeight; + if (totalScrollHeight > height) { + int scrollbarHeight = Math.max(mScrollbarMinHeight, + (int) (height / ((float) totalScrollHeight / height))); + + // Calculate the position and size of the scroll bar + if (Utilities.isRtl(getResources())) { + x = mBackgroundPadding.left; + } else { + x = getWidth() - mBackgroundPadding.right - mScrollbarWidth; + } + + // To calculate the offset, we compute the percentage of the total scrollable height + // that the user has already scrolled and then map that to the scroll bar bounds + int availableY = totalScrollHeight - height; + int availableScrollY = height - scrollbarHeight; + y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + predictionBarHeight + - mScrollPosState.rowTopOffset; + y = getPaddingTop() + + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); + + mVerticalScrollbarBounds.set(x, y, x + mScrollbarWidth, y + scrollbarHeight); + return; + } + } + mVerticalScrollbarBounds.setEmpty(); + } + + /** + * Updates the bounds for the fast scroller. + */ + private void updateFastScrollerBounds() { + if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { + int x; + int y; + + // Calculate the position for the fast scroller popup + Rect bgBounds = mFastScrollerBg.getBounds(); + if (Utilities.isRtl(getResources())) { + x = mBackgroundPadding.left + getScrollBarSize(); + } else { + x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); + } + y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); + y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - + bgBounds.height())); + mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); + } else { + mFastScrollerBounds.setEmpty(); + } + } + + /** + * Returns the row index for a app index in the list. + */ + private int findRowForAppIndex(int index) { + List sections = mApps.getSections(); + int appIndex = 0; + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + if (appIndex + info.numApps > index) { + return rowCount + ((index - appIndex) / mNumAppsPerRow); + } + appIndex += info.numApps; + rowCount += numRowsInSection; + } + return appIndex; + } + + /** + * Returns the total number of rows in the list. + */ + private int getNumRows() { + List sections = mApps.getSections(); + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + rowCount += numRowsInSection; + } + return rowCount; + } + + /** + * Returns the current scroll state. + */ + private void getCurScrollState(ScrollPositionState stateOut, + List items) { + stateOut.rowIndex = -1; + stateOut.rowTopOffset = -1; + stateOut.rowHeight = -1; + + // Return early if there are no items + if (items.isEmpty()) { + return; + } + + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + int position = getChildPosition(child); + if (position != NO_POSITION) { + AlphabeticalAppsList.AdapterItem item = items.get(position); + if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { + stateOut.rowIndex = findRowForAppIndex(item.appIndex); + stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); + stateOut.rowHeight = child.getHeight(); + break; + } + } + } + } +} diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java new file mode 100644 index 000000000..8a8afde51 --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java @@ -0,0 +1,69 @@ +/* + * 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.allapps; + +import android.content.Context; +import android.graphics.Bitmap; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler; +import com.android.launcher3.ClickShadowView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; + +/** + * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that + * is launching. + */ +public class AllAppsRecyclerViewContainerView extends FrameLayout + implements BubbleTextShadowHandler { + + private final ClickShadowView mTouchFeedbackView; + + public AllAppsRecyclerViewContainerView(Context context) { + this(context, null); + } + + public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + Launcher launcher = (Launcher) context; + DeviceProfile grid = launcher.getDeviceProfile(); + + mTouchFeedbackView = new ClickShadowView(context); + + // Make the feedback view large enough to hold the blur bitmap. + int size = grid.allAppsIconSizePx + mTouchFeedbackView.getExtraSize(); + addView(mTouchFeedbackView, size, size); + } + + @Override + public void setPressedIcon(BubbleTextView icon, Bitmap background) { + if (icon == null || background == null) { + mTouchFeedbackView.setBitmap(null); + mTouchFeedbackView.animate().cancel(); + } else if (mTouchFeedbackView.setBitmap(background)) { + mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent()); + mTouchFeedbackView.animateShadow(); + } + } +} diff --git a/src/com/android/launcher3/allapps/AllAppsSearchEditView.java b/src/com/android/launcher3/allapps/AllAppsSearchEditView.java new file mode 100644 index 000000000..b7dcd66ed --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsSearchEditView.java @@ -0,0 +1,65 @@ +/* + * 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.allapps; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.widget.EditText; + + +/** + * The edit text for the search container + */ +public class AllAppsSearchEditView extends EditText { + + /** + * Implemented by listeners of the back key. + */ + public interface OnBackKeyListener { + public void onBackKey(); + } + + private OnBackKeyListener mBackKeyListener; + + public AllAppsSearchEditView(Context context) { + this(context, null); + } + + public AllAppsSearchEditView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AllAppsSearchEditView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setOnBackKeyListener(OnBackKeyListener listener) { + mBackKeyListener = listener; + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + // If this is a back key, propagate the key back to the listener + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + if (mBackKeyListener != null) { + mBackKeyListener.onBackKey(); + } + return false; + } + return super.onKeyPreIme(keyCode, event); + } +} diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java new file mode 100644 index 000000000..3d1503d46 --- /dev/null +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -0,0 +1,577 @@ +package com.android.launcher3.allapps; + +import android.content.ComponentName; +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import com.android.launcher3.AppInfo; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.model.AppNameComparator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +/** + * The alphabetically sorted list of applications. + */ +public class AlphabeticalAppsList { + + public static final String TAG = "AlphabeticalAppsList"; + private static final boolean DEBUG = false; + + /** + * Info about a section in the alphabetic list + */ + public static class SectionInfo { + // The number of applications in this section + public int numApps; + // The section break AdapterItem for this section + public AdapterItem sectionBreakItem; + // The first app AdapterItem for this section + public AdapterItem firstAppItem; + } + + /** + * Info about a fast scroller section, depending if sections are merged, the fast scroller + * sections will not be the same set as the section headers. + */ + public static class FastScrollSectionInfo { + // The section name + public String sectionName; + // To map the touch (from 0..1) to the index in the app list to jump to in the fast + // scroller, we use the fraction in range (0..1) of the app index / total app count. + public float appRangeFraction; + // The AdapterItem to scroll to for this section + public AdapterItem appItem; + + public FastScrollSectionInfo(String sectionName, float appRangeFraction) { + this.sectionName = sectionName; + this.appRangeFraction = appRangeFraction; + } + } + + /** + * Info about a particular adapter item (can be either section or app) + */ + public static class AdapterItem { + /** Common properties */ + // The index of this adapter item in the list + public int position; + // The type of this item + public int viewType; + + /** Section & App properties */ + // The section for this item + public SectionInfo sectionInfo; + + /** App-only properties */ + // The section name of this app. Note that there can be multiple items with different + // sectionNames in the same section + public String sectionName = null; + // The index of this app in the section + public int sectionAppIndex = -1; + // The associated AppInfo for the app + public AppInfo appInfo = null; + // The index of this app not including sections + public int appIndex = -1; + + public static AdapterItem asSectionBreak(int pos, SectionInfo section) { + AdapterItem item = new AdapterItem(); + item.viewType = AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE; + item.position = pos; + item.sectionInfo = section; + section.sectionBreakItem = item; + return item; + } + + public static AdapterItem asPredictionBarSpacer(int pos) { + AdapterItem item = new AdapterItem(); + item.viewType = AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE; + item.position = pos; + return item; + } + + public static AdapterItem asApp(int pos, SectionInfo section, String sectionName, + int sectionAppIndex, AppInfo appInfo, int appIndex) { + AdapterItem item = new AdapterItem(); + item.viewType = AllAppsGridAdapter.ICON_VIEW_TYPE; + item.position = pos; + item.sectionInfo = section; + item.sectionName = sectionName; + item.sectionAppIndex = sectionAppIndex; + item.appInfo = appInfo; + item.appIndex = appIndex; + return item; + } + } + + /** + * A filter interface to limit the set of applications in the apps list. + */ + public interface Filter { + boolean retainApp(AppInfo info, String sectionName); + } + + /** + * Callback to notify when the set of adapter items have changed. + */ + public interface AdapterChangedCallback { + void onAdapterItemsChanged(); + } + + /** + * Common interface for different merging strategies. + */ + private interface MergeAlgorithm { + boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount); + } + + /** + * The logic we use to merge sections on tablets. + */ + private static class TabletMergeAlgorithm implements MergeAlgorithm { + + @Override + public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + // Merge EVERYTHING + return true; + } + } + + /** + * The logic we use to merge sections on phones. + */ + private static class PhoneMergeAlgorithm implements MergeAlgorithm { + + private int mMinAppsPerRow; + private int mMinRowsInMergedSection; + private int mMaxAllowableMerges; + + public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { + mMinAppsPerRow = minAppsPerRow; + mMinRowsInMergedSection = minRowsInMergedSection; + mMaxAllowableMerges = maxNumMerges; + } + + @Override + public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + // Continue merging if the number of hanging apps on the final row is less than some + // fixed number (ragged), the merged rows has yet to exceed some minimum row count, + // and while the number of merged sections is less than some fixed number of merges + int rows = sectionAppCount / numAppsPerRow; + int cols = sectionAppCount % numAppsPerRow; + return (0 < cols && cols < mMinAppsPerRow) && + rows < mMinRowsInMergedSection && + mergeCount < mMaxAllowableMerges; + } + } + + private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3; + private static final int MAX_NUM_MERGES_PHONE = 2; + + private Launcher mLauncher; + + // The set of apps from the system not including predictions + private List mApps = new ArrayList<>(); + // The set of filtered apps with the current filter + private List mFilteredApps = new ArrayList<>(); + // The current set of adapter items + private List mAdapterItems = new ArrayList<>(); + // The set of sections for the apps with the current filter + private List mSections = new ArrayList<>(); + // The set of sections that we allow fast-scrolling to (includes non-merged sections) + private List mFastScrollerSections = new ArrayList<>(); + // The set of predicted app component names + private List mPredictedAppComponents = new ArrayList<>(); + // The set of predicted apps resolved from the component names and the current set of apps + private List mPredictedApps = new ArrayList<>(); + private HashMap mCachedSectionNames = new HashMap<>(); + private RecyclerView.Adapter mAdapter; + private Filter mFilter; + private AlphabeticIndexCompat mIndexer; + private AppNameComparator mAppNameComparator; + private MergeAlgorithm mMergeAlgorithm; + private AdapterChangedCallback mAdapterChangedCallback; + private int mNumAppsPerRow; + private int mNumPredictedAppsPerRow; + + public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) { + mLauncher = (Launcher) context; + mIndexer = new AlphabeticIndexCompat(context); + mAppNameComparator = new AppNameComparator(context); + setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow); + } + + /** + * Sets the apps updated callback. + */ + public void setAdapterChangedCallback(AdapterChangedCallback cb) { + mAdapterChangedCallback = cb; + } + + /** + * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. + */ + public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) { + // Update the merge algorithm + DeviceProfile grid = mLauncher.getDeviceProfile(); + if (grid.isPhone) { + mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f), + MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); + } else { + mMergeAlgorithm = new TabletMergeAlgorithm(); + } + + mNumAppsPerRow = numAppsPerRow; + mNumPredictedAppsPerRow = numPredictedAppsPerRow; + + onAppsUpdated(); + } + + /** + * Sets the adapter to notify when this dataset changes. + */ + public void setAdapter(RecyclerView.Adapter adapter) { + mAdapter = adapter; + } + + /** + * Returns sections of all the current filtered applications. + */ + public List getSections() { + return mSections; + } + + /** + * Returns fast scroller sections of all the current filtered applications. + */ + public List getFastScrollerSections() { + return mFastScrollerSections; + } + + /** + * Returns the current filtered list of applications broken down into their sections. + */ + public List getAdapterItems() { + return mAdapterItems; + } + + /** + * Returns the number of applications in this list. + */ + public int getSize() { + return mFilteredApps.size(); + } + + /** + * Returns whether there are is a filter set. + */ + public boolean hasFilter() { + return (mFilter != null); + } + + /** + * Returns whether there are no filtered results. + */ + public boolean hasNoFilteredResults() { + return (mFilter != null) && mFilteredApps.isEmpty(); + } + + /** + * Sets the current filter for this list of apps. + */ + public void setFilter(Filter f) { + if (mFilter != f) { + mFilter = f; + updateAdapterItems(); + } + } + + /** + * 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. + */ + public void setPredictedApps(List apps) { + mPredictedAppComponents.clear(); + mPredictedAppComponents.addAll(apps); + onAppsUpdated(); + } + + /** + * Returns the current set of predicted apps. + */ + public List getPredictedApps() { + return mPredictedApps; + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + mApps.clear(); + mApps.addAll(apps); + onAppsUpdated(); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + // We add it in place, in alphabetical order + for (AppInfo info : apps) { + mApps.add(info); + } + onAppsUpdated(); + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + for (AppInfo info : apps) { + int index = mApps.indexOf(info); + if (index != -1) { + mApps.set(index, info); + } else { + mApps.add(info); + } + } + onAppsUpdated(); + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + for (AppInfo info : apps) { + int removeIndex = findAppByComponent(mApps, info); + if (removeIndex != -1) { + mApps.remove(removeIndex); + } + } + onAppsUpdated(); + } + + /** + * Finds the index of an app given a target AppInfo. + */ + private int findAppByComponent(List apps, AppInfo targetInfo) { + ComponentName targetComponent = targetInfo.intent.getComponent(); + int length = apps.size(); + for (int i = 0; i < length; ++i) { + AppInfo info = apps.get(i); + if (info.user.equals(targetInfo.user) + && info.intent.getComponent().equals(targetComponent)) { + return i; + } + } + return -1; + } + + /** + * Updates internals when the set of apps are updated. + */ + private void onAppsUpdated() { + // Sort the list of apps + Collections.sort(mApps, mAppNameComparator.getAppInfoComparator()); + + // As a special case for some languages (currently only Simplified Chinese), we may need to + // coalesce sections + Locale curLocale = mLauncher.getResources().getConfiguration().locale; + TreeMap> sectionMap = null; + boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE); + if (localeRequiresSectionSorting) { + // Compute the section headers. We use a TreeMap with the section name comparator to + // ensure that the sections are ordered when we iterate over it later + sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator()); + for (AppInfo info : mApps) { + // Add the section to the cache + String sectionName = getAndUpdateCachedSectionName(info.title); + + // Add it to the mapping + ArrayList sectionApps = sectionMap.get(sectionName); + if (sectionApps == null) { + sectionApps = new ArrayList<>(); + sectionMap.put(sectionName, sectionApps); + } + sectionApps.add(info); + } + + // Add each of the section apps to the list in order + List allApps = new ArrayList<>(mApps.size()); + for (Map.Entry> entry : sectionMap.entrySet()) { + allApps.addAll(entry.getValue()); + } + mApps = allApps; + } else { + // Just compute the section headers for use below + for (AppInfo info : mApps) { + // Add the section to the cache + getAndUpdateCachedSectionName(info.title); + } + } + + // Recompose the set of adapter items from the current set of apps + updateAdapterItems(); + } + + /** + * Updates the set of filtered apps with the current filter. At this point, we expect + * mCachedSectionNames to have been calculated for the set of all apps in mApps. + */ + private void updateAdapterItems() { + SectionInfo lastSectionInfo = null; + String lastSectionName = null; + FastScrollSectionInfo lastFastScrollerSectionInfo = null; + int position = 0; + int appIndex = 0; + + // Prepare to update the list of sections, filtered apps, etc. + mFilteredApps.clear(); + mFastScrollerSections.clear(); + mAdapterItems.clear(); + mSections.clear(); + + // Process the predicted app components + mPredictedApps.clear(); + if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { + for (ComponentName cn : mPredictedAppComponents) { + for (AppInfo info : mApps) { + if (cn.equals(info.componentName)) { + mPredictedApps.add(info); + break; + } + } + // Stop at the number of predicted apps + if (mPredictedApps.size() == mNumPredictedAppsPerRow) { + break; + } + } + + if (!mPredictedApps.isEmpty()) { + // Create a new spacer for the prediction bar + AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++); + mAdapterItems.add(sectionItem); + } + } + + // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the + // ordered set of sections + int numApps = mApps.size(); + for (int i = 0; i < numApps; i++) { + AppInfo info = mApps.get(i); + String sectionName = getAndUpdateCachedSectionName(info.title); + + // Check if we want to retain this app + if (mFilter != null && !mFilter.retainApp(info, sectionName)) { + continue; + } + + // Create a new section if the section names do not match + if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { + lastSectionName = sectionName; + lastSectionInfo = new SectionInfo(); + lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, + (float) appIndex / numApps); + mSections.add(lastSectionInfo); + mFastScrollerSections.add(lastFastScrollerSectionInfo); + + // Create a new section item to break the flow of items in the list + if (!hasFilter()) { + AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo); + mAdapterItems.add(sectionItem); + } + } + + // Create an app item + AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName, + lastSectionInfo.numApps++, info, appIndex++); + if (lastSectionInfo.firstAppItem == null) { + lastSectionInfo.firstAppItem = appItem; + lastFastScrollerSectionInfo.appItem = appItem; + } + mAdapterItems.add(appItem); + mFilteredApps.add(info); + } + + // Merge multiple sections together as requested by the merge strategy for this device + mergeSections(); + + // Refresh the recycler view + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } + + if (mAdapterChangedCallback != null) { + mAdapterChangedCallback.onAdapterItemsChanged(); + } + } + + /** + * Merges multiple sections to reduce visual raggedness. + */ + private void mergeSections() { + // Go through each section and try and merge some of the sections + if (AllAppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { + int sectionAppCount = 0; + for (int i = 0; i < mSections.size(); i++) { + SectionInfo section = mSections.get(i); + sectionAppCount = section.numApps; + int mergeCount = 1; + + // Merge rows based on the current strategy + while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) && + (i + 1) < mSections.size()) { + SectionInfo nextSection = mSections.remove(i + 1); + + // Remove the next section break + mAdapterItems.remove(nextSection.sectionBreakItem); + int pos = mAdapterItems.indexOf(section.firstAppItem); + // Point the section for these new apps to the merged section + int nextPos = pos + section.numApps; + for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) { + AdapterItem item = mAdapterItems.get(j); + item.sectionInfo = section; + item.sectionAppIndex += section.numApps; + } + + // Update the following adapter items of the removed section item + pos = mAdapterItems.indexOf(nextSection.firstAppItem); + for (int j = pos; j < mAdapterItems.size(); j++) { + AdapterItem item = mAdapterItems.get(j); + item.position--; + } + section.numApps += nextSection.numApps; + sectionAppCount += nextSection.numApps; + + if (DEBUG) { + Log.d(TAG, "Merging: " + nextSection.firstAppItem.sectionName + + " to " + section.firstAppItem.sectionName + + " mergedNumRows: " + (sectionAppCount / mNumAppsPerRow)); + } + mergeCount++; + } + } + } + } + + /** + * Returns the cached section name for the given title, recomputing and updating the cache if + * the title has no cached section name. + */ + private String getAndUpdateCachedSectionName(CharSequence title) { + String sectionName = mCachedSectionNames.get(title); + if (sectionName == null) { + sectionName = mIndexer.computeSectionName(title); + mCachedSectionNames.put(title, sectionName); + } + return sectionName; + } +} diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java deleted file mode 100644 index 6f15324c1..000000000 --- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java +++ /dev/null @@ -1,40 +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.widget; - -import android.content.Context; -import android.util.AttributeSet; -import com.android.launcher3.BaseContainerRecyclerView; - -/** - * The widgets recycler view container. - */ -public class WidgetsContainerRecyclerView extends BaseContainerRecyclerView { - - public WidgetsContainerRecyclerView(Context context) { - this(context, null); - } - - public WidgetsContainerRecyclerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WidgetsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - -} \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java new file mode 100644 index 000000000..31ef5d6fc --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -0,0 +1,40 @@ +/* + * 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.widget; + +import android.content.Context; +import android.util.AttributeSet; +import com.android.launcher3.BaseRecyclerView; + +/** + * The widgets recycler view. + */ +public class WidgetsRecyclerView extends BaseRecyclerView { + + public WidgetsRecyclerView(Context context) { + this(context, null); + } + + public WidgetsRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + +} \ No newline at end of file -- cgit v1.2.3 From 9110d485fa12c03df3061278717efb342f1142cf Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 22 May 2015 14:49:23 -0700 Subject: Fix widget tray crash on screen rotation introduced by ag/694693 b/21402209 Change-Id: Idae97fca971d90f5fbba658411491147cb023c8d --- src/com/android/launcher3/LauncherModel.java | 12 ++++++--- src/com/android/launcher3/model/WidgetsModel.java | 30 ++++++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 500253826..e293c2a73 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -139,6 +139,8 @@ public class LauncherModel extends BroadcastReceiver // < only access in worker thread > AllAppsList mBgAllAppsList; + // Entire list of widgets. + WidgetsModel mBgWidgetsModel; // The lock that must be acquired before referencing any static bg data structures. Unlike // other locks, this one can generally be held long-term because we never expect any of these @@ -2779,12 +2781,14 @@ public class LauncherModel extends BroadcastReceiver @SuppressWarnings("unchecked") final ArrayList list = (ArrayList) mBgAllAppsList.data.clone(); + final WidgetsModel widgetList = mBgWidgetsModel.clone(); Runnable r = new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAllApplications(list); + callbacks.bindAllPackages(widgetList); } if (DEBUG_LOADERS) { Log.d(TAG, "bound all " + list.size() + " apps from cache in " @@ -2798,8 +2802,6 @@ public class LauncherModel extends BroadcastReceiver } else { mHandler.post(r); } - loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks), - false /* refresh */); } private void loadAllApps() { @@ -3342,6 +3344,7 @@ public class LauncherModel extends BroadcastReceiver public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks, final boolean refresh) { + runOnWorkerThread(new Runnable(){ @Override public void run() { @@ -3355,6 +3358,7 @@ public class LauncherModel extends BroadcastReceiver } } }); + mBgWidgetsModel = model; // update the Widget entries inside DB on the worker thread. LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews( model.getRawList()); @@ -3363,9 +3367,9 @@ public class LauncherModel extends BroadcastReceiver } /** - * Returns a list of ResolveInfos/AppWidgetInfos. + * Returns a list of ResolveInfos/AppWidgetInfos. * - * @see #loadAndBindWidgetsAndShortcuts + * @see #loadAndBindWidgetsAndShortcuts */ private WidgetsModel createWidgetsModel(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index b72b98126..fdb9795d8 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -3,15 +3,12 @@ package com.android.launcher3.model; import android.content.Context; import android.content.pm.ResolveInfo; -import android.os.Handler; -import android.os.Process; import android.util.Log; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.LauncherModel; import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserHandleCompat; @@ -20,7 +17,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.Map; /** * Widgets data model that is used by the adapters of the widget views and controllers. @@ -33,24 +29,30 @@ public class WidgetsModel { private static final boolean DEBUG = false; /* List of packages that is tracked by this model. */ - private List mPackageItemInfos = new ArrayList<>(); + private ArrayList mPackageItemInfos = new ArrayList<>(); /* Map of widgets and shortcuts that are tracked per package. */ - private Map> mWidgetsList = new HashMap<>(); + private HashMap> mWidgetsList = new HashMap<>(); private ArrayList mRawList; private final Comparator mWidgetAndShortcutNameComparator; private final Comparator mAppNameComparator; - private final IconCache mIconCache; - private final Handler mWorkerHandler; public WidgetsModel(Context context) { mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); mIconCache = LauncherAppState.getInstance().getIconCache(); - mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); + } + + private WidgetsModel(WidgetsModel model) { + mPackageItemInfos = (ArrayList) model.mPackageItemInfos.clone(); + mWidgetsList = (HashMap>) model.mWidgetsList.clone(); + // mRawList is not copied as should not be needed. + mWidgetAndShortcutNameComparator = model.mWidgetAndShortcutNameComparator; + mAppNameComparator = model.mAppNameComparator; + mIconCache = model.mIconCache; } // Access methods that may be deleted if the private fields are made package-private. @@ -122,4 +124,14 @@ public class WidgetsModel { Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator); } } + + /** + * Create a snapshot of the widgets model. + *

+ * Usage case: view binding without being modified from package updates. + */ + @Override + public WidgetsModel clone(){ + return new WidgetsModel(this); + } } \ No newline at end of file -- cgit v1.2.3 From af80285acccec89923a8dd7c5833962e85af2fd5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 22 May 2015 15:14:01 -0700 Subject: Decrease elevation for the reveal view. > During the end of allApps animation, the shadow suddenly changes from 15dp to 0 Change-Id: I93bcf1220396b2bda3efea7febc2195ef556938b --- res/layout/all_apps_reveal.xml | 2 +- res/layout/widgets_view.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/layout/all_apps_reveal.xml b/res/layout/all_apps_reveal.xml index 2951ea4f4..5f4665642 100644 --- a/res/layout/all_apps_reveal.xml +++ b/res/layout/all_apps_reveal.xml @@ -19,6 +19,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" - android:elevation="15dp" + android:elevation="2dp" android:visibility="invisible" android:focusable="false" /> \ No newline at end of file diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index 2615ddbdd..af2c97027 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -30,7 +30,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" - android:elevation="15dp" + android:elevation="2dp" android:focusable="false" android:visibility="invisible" /> -- cgit v1.2.3 From c3d4553d462521bc77b719109ea8cf5b5812d471 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 22 May 2015 14:36:14 -0700 Subject: [DO NOT MERGE] Use custom slide-up animation on LMP MR1 instead of system default issue 21402755 Change-Id: I87ddef08e7b3134c791f769fc279e62d520cd18c --- res/anim/no_anim.xml | 18 ++++++++++++++++++ src/com/android/launcher3/Launcher.java | 7 ++++++- src/com/android/launcher3/Utilities.java | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 res/anim/no_anim.xml diff --git a/res/anim/no_anim.xml b/res/anim/no_anim.xml new file mode 100644 index 000000000..02b162519 --- /dev/null +++ b/res/anim/no_anim.xml @@ -0,0 +1,18 @@ + + + + diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2fc3b75ea..48f390145 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2943,10 +2943,15 @@ public class Launcher extends Activity Bundle optsBundle = null; if (useLaunchAnimation && !Utilities.isLmpOrAbove()) { // On pre-L devices, we use the scale up transition. - // Otherwise we use system default. ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); optsBundle = opts.toBundle(); + } else if (useLaunchAnimation && Utilities.isLmpMr1()) { + // On L-MR1 devices, we use custom slide up animation without a delay + // On L devices, we use the system default slide up. + ActivityOptions opts = ActivityOptions.makeCustomAnimation(this, + R.anim.task_open_enter, R.anim.no_anim); + optsBundle = opts.toBundle(); } if (user == null || user.equals(UserHandleCompat.myUserHandle())) { diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 1a9b9a16c..11c63d009 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -112,6 +112,10 @@ public final class Utilities { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } + public static boolean isLmpMr1() { + return Build.VERSION.SDK_INT == 22; + } + /** * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. -- cgit v1.2.3 From f0e1fd9938b26fa2fa7484613f6160e99c157a9d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 26 May 2015 09:14:49 -0700 Subject: Removing file IO used for checking configuration change > This check was used for flushing the cache, but since the cache is backed by a DB, this has no effect as same icons are loaded again Change-Id: Ib7d8a8598c5d225d922e095c899dc11ad06f2b7a --- src/com/android/launcher3/IconCache.java | 9 --- src/com/android/launcher3/Launcher.java | 108 --------------------------- src/com/android/launcher3/LauncherFiles.java | 3 +- 3 files changed, 1 insertion(+), 119 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 0c91a7113..77d90456e 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -49,10 +49,8 @@ import com.android.launcher3.util.Thunk; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Map.Entry; import java.util.Stack; /** @@ -366,13 +364,6 @@ public class IconCache { return mIconDb.newContentValues(entry.icon, entry.title.toString()); } - /** - * Empty out the cache. - */ - public synchronized void flush() { - mCache.clear(); - } - /** * Fetches high-res icon for the provided ItemInfo and updates the caller when done. * @return a request ID that can be used to cancel the request. diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5dd64e0e2..7f63f7b9a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -111,11 +111,8 @@ import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileDescriptor; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -309,8 +306,6 @@ public class Launcher extends Activity private boolean mHasFocus = false; private boolean mAttached = false; - @Thunk static LocaleConfiguration sLocaleConfiguration = null; - private static LongArrayMap sFolders = new LongArrayMap<>(); private View.OnTouchListener mHapticFeedbackTouchListener; @@ -468,7 +463,6 @@ public class Launcher extends Activity Environment.getExternalStorageDirectory() + "/launcher"); } - checkForLocaleChange(); setContentView(R.layout.launcher); setupViews(); @@ -606,108 +600,6 @@ public class Launcher extends Activity } } - @Thunk void checkForLocaleChange() { - if (sLocaleConfiguration == null) { - new AsyncTask() { - @Override - protected LocaleConfiguration doInBackground(Void... unused) { - LocaleConfiguration localeConfiguration = new LocaleConfiguration(); - readConfiguration(Launcher.this, localeConfiguration); - return localeConfiguration; - } - - @Override - protected void onPostExecute(LocaleConfiguration result) { - sLocaleConfiguration = result; - checkForLocaleChange(); // recursive, but now with a locale configuration - } - }.execute(); - return; - } - - final Configuration configuration = getResources().getConfiguration(); - - final String previousLocale = sLocaleConfiguration.locale; - final String locale = configuration.locale.toString(); - - final int previousMcc = sLocaleConfiguration.mcc; - final int mcc = configuration.mcc; - - final int previousMnc = sLocaleConfiguration.mnc; - final int mnc = configuration.mnc; - - boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc; - - if (localeChanged) { - sLocaleConfiguration.locale = locale; - sLocaleConfiguration.mcc = mcc; - sLocaleConfiguration.mnc = mnc; - - mIconCache.flush(); - - final LocaleConfiguration localeConfiguration = sLocaleConfiguration; - new AsyncTask() { - public Void doInBackground(Void ... args) { - writeConfiguration(Launcher.this, localeConfiguration); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); - } - } - - @Thunk static class LocaleConfiguration { - public String locale; - public int mcc = -1; - public int mnc = -1; - } - - @Thunk static void readConfiguration(Context context, LocaleConfiguration configuration) { - DataInputStream in = null; - try { - in = new DataInputStream(context.openFileInput(LauncherFiles.LAUNCHER_PREFERENCES)); - configuration.locale = in.readUTF(); - configuration.mcc = in.readInt(); - configuration.mnc = in.readInt(); - } catch (FileNotFoundException e) { - // Ignore - } catch (IOException e) { - // Ignore - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - - @Thunk static void writeConfiguration(Context context, LocaleConfiguration configuration) { - DataOutputStream out = null; - try { - out = new DataOutputStream(context.openFileOutput( - LauncherFiles.LAUNCHER_PREFERENCES, MODE_PRIVATE)); - out.writeUTF(configuration.locale); - out.writeInt(configuration.mcc); - out.writeInt(configuration.mnc); - out.flush(); - } catch (FileNotFoundException e) { - // Ignore - } catch (IOException e) { - //noinspection ResultOfMethodCallIgnored - context.getFileStreamPath(LauncherFiles.LAUNCHER_PREFERENCES).delete(); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - public Stats getStats() { return mStats; } diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 03ec4bf7a..c08cd0bf5 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -17,7 +17,6 @@ public class LauncherFiles { public static final String DEFAULT_WALLPAPER_THUMBNAIL = "default_thumb2.jpg"; public static final String DEFAULT_WALLPAPER_THUMBNAIL_OLD = "default_thumb.jpg"; public static final String LAUNCHER_DB = "launcher.db"; - public static final String LAUNCHER_PREFERENCES = "launcher.preferences"; public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; public static final String WALLPAPER_CROP_PREFERENCES_KEY = "com.android.launcher3.WallpaperCropActivity"; @@ -31,7 +30,6 @@ public class LauncherFiles { DEFAULT_WALLPAPER_THUMBNAIL, DEFAULT_WALLPAPER_THUMBNAIL_OLD, LAUNCHER_DB, - LAUNCHER_PREFERENCES, SHARED_PREFERENCES_KEY + XML, WALLPAPER_CROP_PREFERENCES_KEY + XML, WALLPAPER_IMAGES_DB, @@ -43,5 +41,6 @@ public class LauncherFiles { public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( "launches.log", "stats.log", + "launcher.preferences", "com.android.launcher3.compat.PackageInstallerCompatV16.queue")); } -- cgit v1.2.3 From d70ad0d09519975927697604ba38d50883a4d3bf Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 26 May 2015 17:20:07 -0700 Subject: Fixing workspace scrim not being drawn Change-Id: Iabafe159a577909396d1602a7eda9c508e95cb71 --- src/com/android/launcher3/Launcher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5dd64e0e2..13f7343c9 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1055,8 +1055,8 @@ public class Launcher extends Activity } // Background was set to gradient in onPause(), restore to transparent if in all apps. - setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_TRANSPARENT - : WORKSPACE_BACKGROUND_GRADIENT); + setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT + : WORKSPACE_BACKGROUND_TRANSPARENT); mPaused = false; if (mRestoring || mOnResumeNeedsLoad) { -- cgit v1.2.3 From 5a1f53b306a4fcf56aad577987386ea1b2dfe6b9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 27 May 2015 10:24:24 -0700 Subject: Removing SmoothPagedView as all its methods are disabled Change-Id: I83c99bb3d3546693200d64e2233957b4c679e7e6 --- src/com/android/launcher3/Launcher.java | 7 +- src/com/android/launcher3/PagedView.java | 42 ++---- src/com/android/launcher3/SmoothPagedView.java | 185 ------------------------- src/com/android/launcher3/Workspace.java | 7 +- 4 files changed, 16 insertions(+), 225 deletions(-) delete mode 100644 src/com/android/launcher3/SmoothPagedView.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 105dde6e3..1aa446e31 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -88,6 +88,7 @@ import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import android.view.animation.OvershootInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; import android.widget.FrameLayout; @@ -163,14 +164,14 @@ public class Launcher extends Activity private static final int WORKSPACE_BACKGROUND_TRANSPARENT = 1; private static final int WORKSPACE_BACKGROUND_BLACK = 2; + private static final float BOUNCE_ANIMATION_TENSION = 1.3f; + /** * IntentStarter uses request codes starting with this. This must be greater than all activity * request codes used internally. */ protected static final int REQUEST_LAST = 100; - static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; - static final int SCREEN_COUNT = 5; // To turn on these properties, type @@ -4235,7 +4236,7 @@ public class Launcher extends Activity PropertyValuesHolder.ofFloat("scaleY", 1f)); bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); - bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator()); + bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION)); return bounceAnim; } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index dda9a166c..692c3b475 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -146,7 +146,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected OnLongClickListener mLongClickListener; protected int mTouchSlop; - private int mPagingTouchSlop; private int mMaximumVelocity; protected int mPageLayoutWidthGap; protected int mPageLayoutHeightGap; @@ -172,14 +171,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // If true, modify alpha of neighboring pages as user scrolls left/right protected boolean mFadeInAdjacentScreens = false; - // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding - // to switch to a new page - protected boolean mUsePagingTouchSlop = true; - - // If true, the subclass should directly update scrollX itself in its computeScroll method - // (SmoothPagedView does this) - protected boolean mDeferScrollUpdate = false; - protected boolean mIsPageMoving = false; private boolean mWasInOverscroll = false; @@ -264,7 +255,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledPagingTouchSlop(); - mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mDensity = getResources().getDisplayMetrics().density; @@ -1434,25 +1424,20 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return; final int xDiff = (int) Math.abs(x - mLastMotionX); - final int yDiff = (int) Math.abs(y - mLastMotionY); final int touchSlop = Math.round(touchSlopScale * mTouchSlop); - boolean xPaged = xDiff > mPagingTouchSlop; boolean xMoved = xDiff > touchSlop; - boolean yMoved = yDiff > touchSlop; - if (xMoved || xPaged || yMoved) { - if (mUsePagingTouchSlop ? xPaged : xMoved) { - // Scroll if the user moved far enough along the X axis - mTouchState = TOUCH_STATE_SCROLLING; - mTotalMotionX += Math.abs(mLastMotionX - x); - mLastMotionX = x; - mLastMotionXRemainder = 0; - mTouchX = getViewportOffsetX() + getScrollX(); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - onScrollInteractionBegin(); - pageBeginMoving(); - } + if (xMoved) { + // Scroll if the user moved far enough along the X axis + mTouchState = TOUCH_STATE_SCROLLING; + mTotalMotionX += Math.abs(mLastMotionX - x); + mLastMotionX = x; + mLastMotionXRemainder = 0; + mTouchX = getViewportOffsetX() + getScrollX(); + mSmoothingTime = System.nanoTime() / NANOTIME_DIV; + onScrollInteractionBegin(); + pageBeginMoving(); } } @@ -1697,12 +1682,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (Math.abs(deltaX) >= 1.0f) { mTouchX += deltaX; mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - if (!mDeferScrollUpdate) { - scrollBy((int) deltaX, 0); - if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); - } else { - invalidate(); - } + scrollBy((int) deltaX, 0); mLastMotionX = x; mLastMotionXRemainder = deltaX - (int) deltaX; } else { diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java deleted file mode 100644 index 0f9b23cda..000000000 --- a/src/com/android/launcher3/SmoothPagedView.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2008 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.Context; -import android.util.AttributeSet; -import android.view.animation.Interpolator; - -public abstract class SmoothPagedView extends PagedView { - private static final float SMOOTHING_SPEED = 0.75f; - private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); - - private float mBaseLineFlingVelocity; - private float mFlingVelocityInfluence; - - static final int DEFAULT_MODE = 0; - static final int X_LARGE_MODE = 1; - - int mScrollMode; - - private Interpolator mScrollInterpolator; - - public static class OvershootInterpolator implements Interpolator { - private static final float DEFAULT_TENSION = 1.3f; - private float mTension; - - public OvershootInterpolator() { - mTension = DEFAULT_TENSION; - } - - public void setDistance(int distance) { - mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; - } - - public void disableSettle() { - mTension = 0.f; - } - - public float getInterpolation(float t) { - t -= 1.0f; - return t * t * ((mTension + 1) * t + mTension) + 1.0f; - } - } - - /** - * Used to inflate the Workspace from XML. - * - * @param context The application's context. - * @param attrs The attributes set containing the Workspace's customization values. - */ - public SmoothPagedView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - /** - * Used to inflate the Workspace from XML. - * - * @param context The application's context. - * @param attrs The attributes set containing the Workspace's customization values. - * @param defStyle Unused. - */ - public SmoothPagedView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - mUsePagingTouchSlop = false; - - // This means that we'll take care of updating the scroll parameter ourselves (we do it - // in computeScroll), we only do this in the OVERSHOOT_MODE, ie. on phones - mDeferScrollUpdate = mScrollMode != X_LARGE_MODE; - } - - protected int getScrollMode() { - return X_LARGE_MODE; - } - - /** - * Initializes various states for this workspace. - */ - @Override - protected void init() { - super.init(); - - mScrollMode = getScrollMode(); - if (mScrollMode == DEFAULT_MODE) { - mBaseLineFlingVelocity = 2500.0f; - mFlingVelocityInfluence = 0.4f; - mScrollInterpolator = new OvershootInterpolator(); - setDefaultInterpolator(mScrollInterpolator); - } - } - - @Override - protected void snapToDestination() { - if (mScrollMode == X_LARGE_MODE) { - super.snapToDestination(); - } else { - snapToPageWithVelocity(getPageNearestToCenterOfScreen(), 0); - } - } - - @Override - protected void snapToPageWithVelocity(int whichPage, int velocity) { - if (mScrollMode == X_LARGE_MODE) { - super.snapToPageWithVelocity(whichPage, velocity); - } else { - snapToPageWithVelocity(whichPage, 0, true); - } - } - - private void snapToPageWithVelocity(int whichPage, int velocity, boolean settle) { - // if (!mScroller.isFinished()) return; - - whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); - - final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage)); - final int newX = getScrollForPage(whichPage); - final int delta = newX - mUnboundedScrollX; - int duration = (screenDelta + 1) * 100; - - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - } - - if (settle) { - ((OvershootInterpolator) mScrollInterpolator).setDistance(screenDelta); - } else { - ((OvershootInterpolator) mScrollInterpolator).disableSettle(); - } - - velocity = Math.abs(velocity); - if (velocity > 0) { - duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence; - } else { - duration += 100; - } - - snapToPage(whichPage, delta, duration); - } - - @Override - public void snapToPage(int whichPage) { - if (mScrollMode == X_LARGE_MODE) { - super.snapToPage(whichPage); - } else { - snapToPageWithVelocity(whichPage, 0, false); - } - } - - @Override - public void computeScroll() { - if (mScrollMode == X_LARGE_MODE) { - super.computeScroll(); - } else { - boolean scrollComputed = computeScrollHelper(); - - if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) { - final float now = System.nanoTime() / NANOTIME_DIV; - final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT); - - final float dx = mTouchX - mUnboundedScrollX; - scrollTo(Math.round(mUnboundedScrollX + dx * e), getScrollY()); - mSmoothingTime = now; - - // Keep generating points as long as we're more than 1px away from the target - if (dx > 1.f || dx < -1.f) { - invalidate(); - } - } - } - } -} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d2c37d209..c0a1cfc1e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -83,7 +83,7 @@ import java.util.concurrent.atomic.AtomicInteger; * Each page contains a number of icons, folders or widgets the user can * interact with. A workspace is meant to be used with a fixed width only. */ -public class Workspace extends SmoothPagedView +public class Workspace extends PagedView implements DropTarget, DragSource, DragScroller, View.OnTouchListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, Insettable, UninstallSource, AccessibilityDragSource { @@ -457,11 +457,6 @@ public class Workspace extends SmoothPagedView setLayoutTransition(null); } - @Override - protected int getScrollMode() { - return SmoothPagedView.X_LARGE_MODE; - } - @Override public void onChildViewAdded(View parent, View child) { if (!(child instanceof CellLayout)) { -- cgit v1.2.3 From 1740d901ff2bbde7590588cdaae57805b24a74d8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 27 May 2015 11:14:01 -0700 Subject: Fixing build breakage Change-Id: I2d8ac9a92d2da3d6b0f52ff8a6e3683b05663aec --- src/com/android/launcher3/PagedView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 692c3b475..f0990444d 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -2078,7 +2078,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc snapToPage(whichPage, delta, duration); } - protected void snapToPage(int whichPage) { + public void snapToPage(int whichPage) { snapToPage(whichPage, getPageSnapDuration()); } -- cgit v1.2.3 From 1558893b873cd55b2df779f594f1de3c370d3328 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Tue, 26 May 2015 23:03:31 -0700 Subject: Make sure all transition components run on the same thread -> The framework circular reveal transition runs on the render thread which can cause problems when mixed in an AnimatorSet with transitions that don't run on the render thread -> See issue 17556455 issue 21445293 Change-Id: Ie19c184c55060651e817d426ec83049b06af56ba --- src/com/android/launcher3/LauncherAnimUtils.java | 11 +++-- .../LauncherStateTransitionAnimation.java | 23 ++++----- src/com/android/launcher3/Utilities.java | 6 ++- .../launcher3/util/RevealOutlineProvider.java | 49 +++++++++++++++++++ .../launcher3/util/UiThreadCircularReveal.java | 55 ++++++++++++++++++++++ 5 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 src/com/android/launcher3/util/RevealOutlineProvider.java create mode 100644 src/com/android/launcher3/util/UiThreadCircularReveal.java diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 6ff76665c..6a248a332 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -26,6 +26,9 @@ import android.os.Build; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewTreeObserver; + +import com.android.launcher3.util.UiThreadCircularReveal; + import java.util.HashSet; import java.util.WeakHashMap; @@ -130,13 +133,11 @@ public class LauncherAnimUtils { } @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public static Animator createCircularReveal(View view, int centerX, + public static ValueAnimator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) { - Animator anim = ViewAnimationUtils.createCircularReveal(view, centerX, + ValueAnimator anim = UiThreadCircularReveal.createCircularReveal(view, centerX, centerY, startRadius, endRadius); - if (anim instanceof ValueAnimator) { - new FirstFrameAnimatorHelper((ValueAnimator) anim, view); - } + new FirstFrameAnimatorHelper(anim, view); return anim; } } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index f373fde2d..a006d141b 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -26,12 +26,14 @@ import android.annotation.SuppressLint; import android.content.res.Resources; import android.util.Log; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; + import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.util.UiThreadCircularReveal; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetsContainerView; + import java.util.HashMap; /** @@ -320,7 +322,7 @@ public class LauncherStateTransitionAnimation { float startRadius = pCb.getMaterialRevealViewStartFinalRadius(); AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener( revealView, allAppsButtonView); - Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2, + Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2, height / 2, startRadius, revealRadius); reveal.setDuration(revealDuration); reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); @@ -587,14 +589,14 @@ public class LauncherStateTransitionAnimation { TimeInterpolator decelerateInterpolator = material ? new LogDecelerateInterpolator(100, 0) : new DecelerateInterpolator(1f); - ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY", + ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY", 0, revealViewToYDrift); panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); panelDriftY.setInterpolator(decelerateInterpolator); mStateAnimation.play(panelDriftY); - ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX", + ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX", 0, revealViewToXDrift); panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); @@ -605,7 +607,7 @@ public class LauncherStateTransitionAnimation { final float revealViewToAlpha = !material ? 0f : pCb.getMaterialRevealViewFinalAlpha(revealView); if (revealViewToAlpha != 1f) { - ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", + ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha", 1f, revealViewToAlpha); panelAlpha.setDuration(material ? revealDuration : 150); panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); @@ -617,7 +619,7 @@ public class LauncherStateTransitionAnimation { layerViews.put(contentView, BUILD_AND_SET_LAYER); // Create the individual animators - ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY", + ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY", 0, revealViewToYDrift); contentView.setTranslationY(0); pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); @@ -626,7 +628,7 @@ public class LauncherStateTransitionAnimation { mStateAnimation.play(pageDrift); contentView.setAlpha(1f); - ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f); + ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f); itemsAlpha.setDuration(100); itemsAlpha.setInterpolator(decelerateInterpolator); mStateAnimation.play(itemsAlpha); @@ -636,9 +638,8 @@ public class LauncherStateTransitionAnimation { float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView); - Animator reveal = - LauncherAnimUtils.createCircularReveal(revealView, width / 2, - height / 2, revealRadius, finalRadius); + Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2, + height / 2, revealRadius, finalRadius); reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); reveal.setDuration(revealDuration); reveal.setStartDelay(itemsAlphaStagger); @@ -782,4 +783,4 @@ public class LauncherStateTransitionAnimation { mStateAnimation = null; } } -} \ No newline at end of file +} diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 6c4b7207b..256eba020 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -127,6 +127,11 @@ public final class Utilities { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } + public static boolean isLmpMR1OrAbove() { + // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22; + return Build.VERSION.SDK_INT >= 22; + } + static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { byte[] data = c.getBlob(iconIndex); try { @@ -588,7 +593,6 @@ public final class Utilities { } public static final Comparator RANK_COMPARATOR = new Comparator() { - @Override public int compare(ItemInfo lhs, ItemInfo rhs) { return lhs.rank - rhs.rank; diff --git a/src/com/android/launcher3/util/RevealOutlineProvider.java b/src/com/android/launcher3/util/RevealOutlineProvider.java new file mode 100644 index 000000000..0db3984f8 --- /dev/null +++ b/src/com/android/launcher3/util/RevealOutlineProvider.java @@ -0,0 +1,49 @@ +package com.android.launcher3.util; + +import android.annotation.TargetApi; +import android.graphics.Outline; +import android.graphics.Rect; +import android.os.Build; +import android.view.View; +import android.view.ViewOutlineProvider; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class RevealOutlineProvider extends ViewOutlineProvider { + + private int mCenterX; + private int mCenterY; + private float mRadius0; + private float mRadius1; + private int mCurrentRadius; + + private final Rect mOval; + + /** + * @param x reveal center x + * @param y reveal center y + * @param r0 initial radius + * @param r1 final radius + */ + public RevealOutlineProvider(int x, int y, float r0, float r1) { + mCenterX = x; + mCenterY = y; + mRadius0 = r0; + mRadius1 = r1; + + mOval = new Rect(); + } + + public void setProgress(float progress) { + mCurrentRadius = (int) ((1 - progress) * mRadius0 + progress * mRadius1); + + mOval.left = mCenterX - mCurrentRadius; + mOval.top = mCenterY - mCurrentRadius; + mOval.right = mCenterX + mCurrentRadius; + mOval.bottom = mCenterY + mCurrentRadius; + } + + @Override + public void getOutline(View v, Outline outline) { + outline.setRoundRect(mOval, mCurrentRadius); + } +} diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java new file mode 100644 index 000000000..c7324fb1b --- /dev/null +++ b/src/com/android/launcher3/util/UiThreadCircularReveal.java @@ -0,0 +1,55 @@ +package com.android.launcher3.util; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.TargetApi; +import android.os.Build; +import android.view.View; +import android.view.ViewOutlineProvider; + +import com.android.launcher3.Utilities; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class UiThreadCircularReveal { + + public static ValueAnimator createCircularReveal(View v, int x, int y, float r0, float r1) { + ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); + + final View revealView = v; + final RevealOutlineProvider outlineProvider = new RevealOutlineProvider(x, y, r0, r1); + final ViewOutlineProvider originalProvider = revealView.getOutlineProvider(); + final float elevation = v.getElevation(); + + va.addListener(new AnimatorListenerAdapter() { + public void onAnimationStart(Animator animation) { + revealView.setOutlineProvider(outlineProvider); + revealView.setClipToOutline(true); + revealView.setTranslationZ(-elevation); + } + + public void onAnimationEnd(Animator animation) { + revealView.setOutlineProvider(originalProvider); + revealView.setClipToOutline(false); + revealView.setTranslationZ(0); + } + + }); + + va.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator arg0) { + float progress = arg0.getAnimatedFraction(); + outlineProvider.setProgress(progress); + if (Utilities.isLmpMR1OrAbove()) { + revealView.invalidateOutline(); + } else { + // On L, a bug requires calling a full view invalidate. + revealView.invalidate(); + } + } + }); + return va; + } +} -- cgit v1.2.3 From 58376925f9596c23f5c9ffa6c99630dfddcfce9c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 26 May 2015 18:56:52 -0700 Subject: Fixing page background not displayed when dragging from all apps > Adding empty page synchronously, instead of waiting for a frame > Changing launcher state from widgets screen in the same frame, similar to all apps > Removing DragEnforcer, and moving that logic in side the workspace, disabled by a flag > Using first page to get page bounds in drag layer, as last page may not have been measured Change-Id: I172ba4e5ce44648ac55402d49994542c6e10f101 --- res/values/config.xml | 1 + src/com/android/launcher3/CellLayout.java | 4 -- src/com/android/launcher3/DragLayer.java | 2 +- src/com/android/launcher3/DropTarget.java | 39 -------------- src/com/android/launcher3/Workspace.java | 61 ++++++++++++++++------ .../launcher3/allapps/AllAppsContainerView.java | 1 - .../launcher3/widget/WidgetsContainerView.java | 21 +++----- 7 files changed, 53 insertions(+), 76 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index fbce3a41f..73de79417 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -20,6 +20,7 @@ -1500 + diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 2b1cfe0e4..61567ac00 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -168,7 +168,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private int[] mDirectionVector = new int[2]; int[] mPreviousReorderDirection = new int[2]; private static final int INVALID_DIRECTION = -100; - private DropTarget.DragEnforcer mDragEnforcer; private final Rect mTempRect = new Rect(); @@ -188,7 +187,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { public CellLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mDragEnforcer = new DropTarget.DragEnforcer(context); // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show // the user where a dragged item will land when dropped. @@ -2637,7 +2635,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { * or it may have begun on another layout. */ void onDragEnter() { - mDragEnforcer.onDragEnter(); mDragging = true; } @@ -2645,7 +2642,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { * Called when drag has left this CellLayout or has been completed (successfully or not) */ void onDragExit() { - mDragEnforcer.onDragExit(); // This can actually be called when we aren't in a drag, e.g. when adding a new // item to this layout via the customize drawer. // Guard against that case. diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index 423a9a3d5..41e053eed 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -918,7 +918,7 @@ public class DragLayer extends InsettableFrameLayout { void showPageHints() { mShowPageHints = true; Workspace workspace = mLauncher.getWorkspace(); - getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1), + getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()), mScrollChildPosition); invalidate(); } diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index a3828c1d0..c8fac5466 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -16,10 +16,8 @@ package com.android.launcher3; -import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; -import android.util.Log; /** * Interface defining an object that can receive a drag. @@ -93,43 +91,6 @@ public interface DropTarget { } } - public static class DragEnforcer implements DragController.DragListener { - int dragParity = 0; - - public DragEnforcer(Context context) { - Launcher launcher = (Launcher) context; - launcher.getDragController().addDragListener(this); - } - - void onDragEnter() { - dragParity++; - if (dragParity != 1) { - Log.e(TAG, "onDragEnter: Drag contract violated: " + dragParity); - } - } - - void onDragExit() { - dragParity--; - if (dragParity != 0) { - Log.e(TAG, "onDragExit: Drag contract violated: " + dragParity); - } - } - - @Override - public void onDragStart(DragSource source, Object info, int dragAction) { - if (dragParity != 0) { - Log.e(TAG, "onDragEnter: Drag contract violated: " + dragParity); - } - } - - @Override - public void onDragEnd() { - if (dragParity != 0) { - Log.e(TAG, "onDragExit: Drag contract violated: " + dragParity); - } - } - } - /** * Used to temporarily disable certain drop targets * diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index c0a1cfc1e..2eb1879d0 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -64,8 +64,8 @@ import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; -import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; +import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; @@ -89,6 +89,8 @@ public class Workspace extends PagedView Insettable, UninstallSource, AccessibilityDragSource { private static final String TAG = "Launcher.Workspace"; + private static boolean ENFORCE_DRAG_EVENT_ORDER = false; + protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; protected static final int FADE_EMPTY_SCREEN_DURATION = 150; @@ -215,7 +217,6 @@ public class Workspace extends PagedView private FolderIcon mDragOverFolderIcon = null; private boolean mCreateUserFolderOnDrop = false; private boolean mAddToExistingFolderOnDrop = false; - private DropTarget.DragEnforcer mDragEnforcer; private float mMaxDistanceForFolderCreation; private final Canvas mCanvas = new Canvas(); @@ -301,9 +302,6 @@ public class Workspace extends PagedView mOutlineHelper = HolographicOutlineHelper.obtain(context); - mDragEnforcer = new DropTarget.DragEnforcer(context); - // With workspace, data is available straight from the get-go - mLauncher = (Launcher) context; mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); final Resources res = getResources(); @@ -372,22 +370,23 @@ public class Workspace extends PagedView return r; } + @Override public void onDragStart(final DragSource source, Object info, int dragAction) { + if (ENFORCE_DRAG_EVENT_ORDER) { + enfoceDragParity("onDragStart", 0, 0); + } + mIsDragOccuring = true; updateChildrenLayersEnabled(false); mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging InstallShortcutReceiver.enableInstallQueue(); - post(new Runnable() { - @Override - public void run() { - if (mIsDragOccuring && mAddNewPageOnDrag) { - mDeferRemoveExtraEmptyScreen = false; - addExtraEmptyScreenOnDrag(); - } - } - }); + + if (mAddNewPageOnDrag) { + mDeferRemoveExtraEmptyScreen = false; + addExtraEmptyScreenOnDrag(); + } } public void setAddNewPageOnDrag(boolean addPage) { @@ -398,7 +397,12 @@ public class Workspace extends PagedView mDeferRemoveExtraEmptyScreen = true; } + @Override public void onDragEnd() { + if (ENFORCE_DRAG_EVENT_ORDER) { + enfoceDragParity("onDragEnd", 0, 0); + } + if (!mDeferRemoveExtraEmptyScreen) { removeExtraEmptyScreen(true, mDragSourceInternal != null); } @@ -2822,8 +2826,12 @@ public class Workspace extends PagedView location[1] = vY - y; } + @Override public void onDragEnter(DragObject d) { - mDragEnforcer.onDragEnter(); + if (ENFORCE_DRAG_EVENT_ORDER) { + enfoceDragParity("onDragEnter", 1, 1); + } + mCreateUserFolderOnDrop = false; mAddToExistingFolderOnDrop = false; @@ -2876,8 +2884,11 @@ public class Workspace extends PagedView return null; } + @Override public void onDragExit(DragObject d) { - mDragEnforcer.onDragExit(); + if (ENFORCE_DRAG_EVENT_ORDER) { + enfoceDragParity("onDragExit", -1, 0); + } // Here we store the final page that will be dropped to, if the workspace in fact // receives the drop @@ -2909,6 +2920,24 @@ public class Workspace extends PagedView mLauncher.getDragLayer().hidePageHints(); } + private void enfoceDragParity(String event, int update, int expectedValue) { + enfoceDragParity(this, event, update, expectedValue); + for (int i = 0; i < getChildCount(); i++) { + enfoceDragParity(getChildAt(i), event, update, expectedValue); + } + } + + private void enfoceDragParity(View v, String event, int update, int expectedValue) { + Object tag = v.getTag(R.id.drag_event_parity); + int value = tag == null ? 0 : (Integer) tag; + value += update; + v.setTag(R.id.drag_event_parity, value); + + if (value != expectedValue) { + Log.e(TAG, event + ": Drag contract violated: " + value); + } + } + void setCurrentDropLayout(CellLayout layout) { if (mDragTargetLayout != null) { mDragTargetLayout.revertTempState(); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 60f9ab347..d81f97f24 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -447,7 +447,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Override protected void onFixedBoundsUpdated() { // Update the number of items in the grid - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) { mNumAppsPerRow = grid.allAppsNumCols; diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 05e842e71..778cf9ef1 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -35,7 +35,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragController; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.Folder; import com.android.launcher3.IconCache; import com.android.launcher3.ItemInfo; @@ -46,6 +45,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.Workspace; +import com.android.launcher3.model.WidgetsModel; /** * The widgets list view container. @@ -56,8 +56,6 @@ public class WidgetsContainerView extends BaseContainerView private static final String TAG = "WidgetsContainerView"; private static final boolean DEBUG = false; - private static final int SPRING_MODE_DELAY_MS = 150; - /* Coefficient multiplied to the screen height for preloading widgets. */ private static final int PRELOAD_SCREEN_HEIGHT_MULTIPLE = 1; @@ -186,18 +184,11 @@ public class WidgetsContainerView extends BaseContainerView Log.e(TAG, "Unexpected dragging view: " + v); } - // We delay entering spring-loaded mode slightly to make sure the UI - // thread is free of any work. - postDelayed(new Runnable() { - @Override - public void run() { - // We don't enter spring-loaded mode if the drag has been cancelled - if (mLauncher.getDragController().isDragging()) { - // Go into spring loaded mode (must happen before we startDrag()) - mLauncher.enterSpringLoadedDragMode(); - } - } - }, SPRING_MODE_DELAY_MS); + // We don't enter spring-loaded mode if the drag has been cancelled + if (mLauncher.getDragController().isDragging()) { + // Go into spring loaded mode (must happen before we startDrag()) + mLauncher.enterSpringLoadedDragMode(); + } return true; } -- cgit v1.2.3 From 7af0d4474fe811b66db67f358ee0b5ef84b97a18 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Fri, 15 May 2015 08:48:11 -0700 Subject: Fixing non-scrolling of workspace view after opening/closing AllApps vew. The reason for non-scrolling was excluding the pages view from the accessibility hierarchy by marking it as non-important. So, I just removed the code manipulating [non]importance of the PagedView. However, this would make the PagesView accessibility-focusable, which is undesirable. It becomes focusable because it supports long clicks in "normal" mode. Since it doesn't support accessibility long clicks (i.e. Overview mode is fetched NOT via accessibility long-click), I just disabled accessibility long-clickability, which made PageView non-focusable, and it started to behave correctly. Bug: 21281859 Change-Id: I7ab01e5f39cb37c456c961199c27458c9bda1c3d --- src/com/android/launcher3/PagedView.java | 11 ++++++++++- src/com/android/launcher3/Workspace.java | 10 ++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index dda9a166c..502caaf32 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -853,7 +853,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int offsetY = getViewportOffsetY(); // Update the viewport offsets - mViewport.offset(offsetX, offsetY); + mViewport.offset(offsetX, offsetY); final int startIndex = mIsRtl ? childCount - 1 : 0; final int endIndex = mIsRtl ? -1 : childCount; @@ -2347,6 +2347,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (getCurrentPage() > 0) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); } + info.setClassName(getClass().getName()); + + // Accessibility-wise, PagedView doesn't support long click, so disabling it. + // Besides disabling the accessibility long-click, this also prevents this view from getting + // accessibility focus. + info.setLongClickable(false); + if (Utilities.isLmpOrAbove()) { + info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK); + } } @Override diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d2c37d209..8b783abc2 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -327,7 +327,6 @@ public class Workspace extends SmoothPagedView // Disable multitouch across the workspace/all apps/customize tray setMotionEventSplittingEnabled(true); - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } @Override @@ -2019,14 +2018,9 @@ public class Workspace extends SmoothPagedView for (int i = numCustomPages(); i < total; i++) { updateAccessibilityFlags((CellLayout) getPageAt(i), i); } - if (mState == State.NORMAL) { - setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - } else { - setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - } } else { int accessible = mState == State.NORMAL ? - IMPORTANT_FOR_ACCESSIBILITY_NO : + IMPORTANT_FOR_ACCESSIBILITY_AUTO : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; setImportantForAccessibility(accessible); } @@ -2045,7 +2039,7 @@ public class Workspace extends SmoothPagedView page.setAccessibilityDelegate(mPagesAccessibilityDelegate); } else { int accessible = mState == State.NORMAL ? - IMPORTANT_FOR_ACCESSIBILITY_NO : + IMPORTANT_FOR_ACCESSIBILITY_AUTO : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); page.getShortcutsAndWidgets().setImportantForAccessibility(accessible); -- cgit v1.2.3 From 89aeda1b78907fefe259df770a01f490d4901128 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 28 May 2015 14:45:59 -0700 Subject: Guard against monkey actor test NPE inside FocusHelper b/19626762 Change-Id: Icaff5931f4076323855f23e7165b0e78a11241b6 --- src/com/android/launcher3/FocusHelper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index e60704718..0dca07843 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -284,6 +284,12 @@ public class FocusHelper { if (workspace != null) { int pageIndex = workspace.getCurrentPage(); CellLayout topLayout = (CellLayout) workspace.getChildAt(pageIndex); + if (topLayout == null) { + // This is to guard against monkey actor test where the cell layout + // of the new pageIndex is null monkey issuing commands while + // animations happen. + return wasHandled; + } ShortcutAndWidgetContainer children = topLayout.getShortcutsAndWidgets(); final View newIcon = getIconInDirection(layout, children, -1, 1); // Select the first bubble text view in the current page of the workspace -- cgit v1.2.3 From 7066c1235f2531d90c44c2db279a407b44cfc263 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Thu, 21 May 2015 14:06:35 -0700 Subject: Setting scroll X, Y and related attributes for TYPE_VIEW_SCROLLED event. Change-Id: Idec0e333a0d3c3a0c232d3e2240f18174e0ea88c --- src/com/android/launcher3/PagedView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index dda9a166c..8648ae537 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -633,6 +633,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (mCurrentPage != getNextPage()) { AccessibilityEvent ev = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); + ev.setScrollable(true); + ev.setScrollX(getScrollX()); + ev.setScrollY(getScrollY()); + ev.setMaxScrollX(mMaxScrollX); + ev.setMaxScrollY(0); sendAccessibilityEventUnchecked(ev); } @@ -2360,7 +2365,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); - event.setScrollable(true); + event.setScrollable(getPageCount() > 1); } @Override -- cgit v1.2.3 From cdefc631f8b4b3e56ea3bd238d46d0bc9e28eabe Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 28 May 2015 12:53:44 -0700 Subject: Ensuring that each CacheEntry has a default title and content description. - Also defer adding entry to cache until we know that it has correctly loaded Bug: 21446070 Change-Id: Ia7aae65ecdc5d9d7741f75d3fb6e7b85daeafeff --- src/com/android/launcher3/IconCache.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 0c91a7113..a3376c42f 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -49,10 +49,8 @@ import com.android.launcher3.util.Thunk; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Map.Entry; import java.util.Stack; /** @@ -75,8 +73,8 @@ public class IconCache { @Thunk static class CacheEntry { public Bitmap icon; - public CharSequence title; - public CharSequence contentDescription; + public CharSequence title = ""; + public CharSequence contentDescription = ""; public boolean isLowResIcon; } @@ -584,7 +582,7 @@ public class IconCache { CacheEntry entry = mCache.get(cacheKey); if (entry == null || (entry.isLowResIcon && !useLowResIcon)) { entry = new CacheEntry(); - mCache.put(cacheKey, entry); + boolean entryUpdated = true; // Check the DB first. if (!getEntryFromDB(cn, user, entry, useLowResIcon)) { @@ -609,8 +607,14 @@ public class IconCache { } catch (NameNotFoundException e) { if (DEBUG) Log.d(TAG, "Application not installed " + packageName); + entryUpdated = false; } } + + // Only add a filled-out entry to the cache + if (entryUpdated) { + mCache.put(cacheKey, entry); + } } return entry; } -- cgit v1.2.3 From 35c3c7f517d77ba6740fc2a08c51b8682bd593b2 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 28 May 2015 15:33:40 -0700 Subject: Start adding unit tests for the invariant device profile / Refactor - removed redundant code to sort the device profiles - removed DeviceProfileQuery class - Added a helper method inside the test to easily generate interpolation graph looks like: https://docs.google.com/a/google.com/spreadsheets/d/1a1fdemrOqIDixiql77h0anWzUD3GlYfGsbP2FfIhyPM/edit?usp=sharing Change-Id: Ia4c54a8d59a049c418c08d1b766f07ac6e1d0944 --- .../android/launcher3/InvariantDeviceProfile.java | 248 ++++++++++----------- .../launcher3/InvariantDeviceProfileTest.java | 123 ++++++++++ 2 files changed, 242 insertions(+), 129 deletions(-) create mode 100644 tests/src/com/android/launcher3/InvariantDeviceProfileTest.java diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 92fdbde85..7f34593a9 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -19,12 +19,13 @@ package com.android.launcher3; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Point; -import android.graphics.PointF; import android.os.Build; import android.util.DisplayMetrics; import android.view.Display; import android.view.WindowManager; + import com.android.launcher3.util.Thunk; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -34,61 +35,36 @@ public class InvariantDeviceProfile { // This is a static that we use for the default icon size on a 4/5-inch phone private static float DEFAULT_ICON_SIZE_DP = 60; - private static final ArrayList sDeviceProfiles = new ArrayList<>(); - static { - sDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby", - 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Stubby", - 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4", - 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5", - 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); - sDeviceProfiles.add(new InvariantDeviceProfile("Large Phone", - 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); - // The tablet profile is odd in that the landscape orientation - // also includes the nav bar on the side - sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7", - 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); - // Larger tablet profiles always have system bars on the top & bottom - sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10", - 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); - sDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet", - 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); - } + // Constants that affects the interpolation curve between statically defined device profile + // buckets. + private static float KNEARESTNEIGHBOR = 3; + private static float WEIGHT_POWER = 5; - private class DeviceProfileQuery { - InvariantDeviceProfile profile; - float widthDps; - float heightDps; - float value; - PointF dimens; - - DeviceProfileQuery(InvariantDeviceProfile p, float v) { - widthDps = p.minWidthDps; - heightDps = p.minHeightDps; - value = v; - dimens = new PointF(widthDps, heightDps); - profile = p; - } - } + // used to offset float not being able to express extremely small weights in extreme cases. + private static float WEIGHT_EFFICIENT = 100000f; // Profile-defining invariant properties String name; float minWidthDps; float minHeightDps; + + /** + * Number of icons per row and column in the workspace. + */ public int numRows; public int numColumns; + + /** + * Number of icons per row and column in the folder. + */ public int numFolderRows; public int numFolderColumns; float iconSize; float iconTextSize; + + /** + * Number of icons inside the hotseat area. + */ float numHotseatIcons; float hotseatIconSize; int defaultLayoutId; @@ -102,6 +78,12 @@ public class InvariantDeviceProfile { InvariantDeviceProfile() { } + public InvariantDeviceProfile(InvariantDeviceProfile p) { + this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns, + p.numFolderRows, p.numFolderColumns, p.iconSize, p.iconTextSize, p.numHotseatIcons, + p.hotseatIconSize, p.defaultLayoutId); + } + InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, float is, float its, float hs, float his, int dlId) { // Ensure that we have an odd number of hotseat items (since we need to place all apps) @@ -134,21 +116,16 @@ public class InvariantDeviceProfile { Point largestSize = new Point(); display.getCurrentSizeRange(smallestSize, largestSize); + // This guarantees that width < height minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm); - ArrayList points = - new ArrayList(); + ArrayList closestProfiles = + findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles()); + InvariantDeviceProfile interpolatedDeviceProfileOut = + invDistWeightedInterpolate(minWidthDps, minHeightDps, closestProfiles); - // Find the closes profile given the width/height - for (InvariantDeviceProfile p : sDeviceProfiles) { - points.add(new DeviceProfileQuery(p, 0f)); - } - - InvariantDeviceProfile closestProfile = - findClosestDeviceProfile(minWidthDps, minHeightDps, points); - - // The following properties are inherited directly from the nearest archetypal profile + InvariantDeviceProfile closestProfile = closestProfiles.get(0); numRows = closestProfile.numRows; numColumns = closestProfile.numColumns; numHotseatIcons = closestProfile.numHotseatIcons; @@ -157,24 +134,9 @@ public class InvariantDeviceProfile { numFolderRows = closestProfile.numFolderRows; numFolderColumns = closestProfile.numFolderColumns; - - // The following properties are interpolated based on proximity to nearby archetypal - // profiles - points.clear(); - for (InvariantDeviceProfile p : sDeviceProfiles) { - points.add(new DeviceProfileQuery(p, p.iconSize)); - } - iconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points); - points.clear(); - for (InvariantDeviceProfile p : sDeviceProfiles) { - points.add(new DeviceProfileQuery(p, p.iconTextSize)); - } - iconTextSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points); - points.clear(); - for (InvariantDeviceProfile p : sDeviceProfiles) { - points.add(new DeviceProfileQuery(p, p.hotseatIconSize)); - } - hotseatIconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points); + iconSize = interpolatedDeviceProfileOut.iconSize; + iconTextSize = interpolatedDeviceProfileOut.iconTextSize; + hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize; // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize @@ -182,7 +144,7 @@ public class InvariantDeviceProfile { Point realSize = new Point(); display.getRealSize(realSize); - // The real size never changes. smallSide and largeSize will remain the + // The real size never changes. smallSide and largeSide will remain the // same in any orientation. int smallSide = Math.min(realSize.x, realSize.y); int largeSide = Math.max(realSize.x, realSize.y); @@ -193,84 +155,112 @@ public class InvariantDeviceProfile { smallSide, largeSide, false /* isLandscape */); } + ArrayList getPredefinedDeviceProfiles() { + ArrayList predefinedDeviceProfiles = new ArrayList<>(); + // width, height, #rows, #columns, #folder rows, #folder columns, + // iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId. + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby", + 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby", + 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby", + 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby", + 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S", + 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4", + 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5", + 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone", + 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); + // The tablet profile is odd in that the landscape orientation + // also includes the nav bar on the side + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7", + 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); + // Larger tablet profiles always have system bars on the top & bottom + predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10", + 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); + predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet", + 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); + return predefinedDeviceProfiles; + } + + /** * Apply any Partner customization grid overrides. * * Currently we support: all apps row / column count. */ - private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) { - Partner p = Partner.get(ctx.getPackageManager()); + private void applyPartnerDeviceProfileOverrides(Context context, DisplayMetrics dm) { + Partner p = Partner.get(context.getPackageManager()); if (p != null) { p.applyInvariantDeviceProfileOverrides(this, dm); } } - @Thunk float dist(PointF p0, PointF p1) { - return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) + - (p1.y-p0.y)*(p1.y-p0.y)); - } - - private float weight(PointF a, PointF b, - float pow) { - float d = dist(a, b); - if (d == 0f) { - return Float.POSITIVE_INFINITY; - } - return (float) (1f / Math.pow(d, pow)); - } - - /** Returns the closest device profile given the width and height and a list of profiles */ - private InvariantDeviceProfile findClosestDeviceProfile(float width, float height, - ArrayList points) { - return findClosestDeviceProfiles(width, height, points).get(0).profile; + @Thunk float dist(float x0, float y0, float x1, float y1) { + return (float) Math.hypot(x1 - x0, y1 - y0); } - /** Returns the closest device profiles ordered by closeness to the specified width and height */ - private ArrayList findClosestDeviceProfiles(float width, float height, - ArrayList points) { - final PointF xy = new PointF(width, height); + /** + * Returns the closest device profiles ordered by closeness to the specified width and height + */ + // Package private visibility for testing. + ArrayList findClosestDeviceProfiles( + final float width, final float height, ArrayList points) { // Sort the profiles by their closeness to the dimensions - ArrayList pointsByNearness = points; - Collections.sort(pointsByNearness, new Comparator() { - public int compare(DeviceProfileQuery a, DeviceProfileQuery b) { - return (int) (dist(xy, a.dimens) - dist(xy, b.dimens)); + ArrayList pointsByNearness = points; + Collections.sort(pointsByNearness, new Comparator() { + public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) { + return (int) (dist(width, height, a.minWidthDps, a.minHeightDps) + - dist(width, height, b.minWidthDps, b.minHeightDps)); } }); return pointsByNearness; } - private float invDistWeightedInterpolate(float width, float height, - ArrayList points) { - float sum = 0; + // Package private visibility for testing. + InvariantDeviceProfile invDistWeightedInterpolate(float width, float height, + ArrayList points) { float weights = 0; - float pow = 5; - float kNearestNeighbors = 3; - final PointF xy = new PointF(width, height); - - ArrayList pointsByNearness = findClosestDeviceProfiles(width, height, - points); - - for (int i = 0; i < pointsByNearness.size(); ++i) { - DeviceProfileQuery p = pointsByNearness.get(i); - if (i < kNearestNeighbors) { - float w = weight(xy, p.dimens, pow); - if (w == Float.POSITIVE_INFINITY) { - return p.value; - } - weights += w; - } + + InvariantDeviceProfile p = points.get(0); + if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) { + return p; } - for (int i = 0; i < pointsByNearness.size(); ++i) { - DeviceProfileQuery p = pointsByNearness.get(i); - if (i < kNearestNeighbors) { - float w = weight(xy, p.dimens, pow); - sum += w * p.value / weights; - } + InvariantDeviceProfile out = new InvariantDeviceProfile(); + for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) { + p = new InvariantDeviceProfile(points.get(i)); + float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER); + weights += w; + out.add(p.multiply(w)); } + return out.multiply(1.0f/weights); + } - return sum; + private void add(InvariantDeviceProfile p) { + iconSize += p.iconSize; + iconTextSize += p.iconTextSize; + hotseatIconSize += p.hotseatIconSize; + } + + private InvariantDeviceProfile multiply(float w) { + iconSize *= w; + iconTextSize *= w; + hotseatIconSize *= w; + return this; + } + + private float weight(float x0, float y0, float x1, float y1, float pow) { + float d = dist(x0, y0, x1, y1); + if (Float.compare(d, 0f) == 0) { + return Float.POSITIVE_INFINITY; + } + return (float) (WEIGHT_EFFICIENT / Math.pow(d, pow)); } -} +} \ No newline at end of file diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java new file mode 100644 index 000000000..1bc7c1190 --- /dev/null +++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java @@ -0,0 +1,123 @@ +/* + * 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.graphics.PointF; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.util.FocusLogic; + +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(); + } + + @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 closestProfiles = + mInvariantProfile.findClosestDeviceProfiles( + idf.minWidthDps, idf.minHeightDps, mPredefinedDeviceProfiles); + 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(mInvariantProfile.numRows, 4); + assertEquals(mInvariantProfile.numColumns, 4); + assertEquals((int) mInvariantProfile.numHotseatIcons, 5); + + DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile; + DeviceProfile portraitProfile = mInvariantProfile.portraitProfile; + + assertEquals(portraitProfile.allAppsNumCols, 3); + assertEquals(landscapeProfile.allAppsNumCols, 5); // not used + } + + // 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. +} -- cgit v1.2.3 From ac5f6af1488ec1cf0b73aa0848a675764c2f652b Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 29 May 2015 12:00:44 -0700 Subject: Move fast scrolling logic to BaseRecyclerView - This change has no effect on actual functionality but to make it easier for widget tray to inherit the goodness of the 1) fast scroller functionality 2) unify the scroll look and feel with the all apps view b/21375339 Change-Id: Ib859b1c3352c0b69f16549ded8f20eb82cf58ba8 --- src/com/android/launcher3/BaseRecyclerView.java | 272 +++++++++++++++- .../launcher3/allapps/AllAppsRecyclerView.java | 352 ++++----------------- .../launcher3/widget/WidgetsContainerView.java | 5 +- .../launcher3/widget/WidgetsListAdapter.java | 1 - .../launcher3/widget/WidgetsRecyclerView.java | 72 +++++ 5 files changed, 395 insertions(+), 307 deletions(-) diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index b63ef788a..8d418f984 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -16,15 +16,28 @@ package com.android.launcher3; +import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + import com.android.launcher3.util.Thunk; /** - * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling - * velocity is below a predefined threshold. + * A base {@link RecyclerView}, which does the following: + *

    + *
  • NOT intercept a touch unless the scrolling velocity is below a predefined threshold. + *
  • Enable fast scroller. + *
*/ public class BaseRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { @@ -35,6 +48,53 @@ public class BaseRecyclerView extends RecyclerView @Thunk int mDy = 0; private float mDeltaThreshold; + // + // Keeps track of variables required for the second function of this class: fast scroller. + // + + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; + + /** + * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() + * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so + * that we can calculate what the scroll bar looks like, and where to jump to from the fast + * scroller. + */ + public static class ScrollPositionState { + // The index of the first visible row + public int rowIndex; + // The offset of the first visible row + public int rowTopOffset; + // The height of a given row (they are currently all the same height) + public int rowHeight; + } + // Should be maintained inside overriden method #updateVerticalScrollbarBounds + public ScrollPositionState scrollPosState = new ScrollPositionState(); + public Rect verticalScrollbarBounds = new Rect(); + + private boolean mDraggingFastScroller; + + private Drawable mScrollbar; + private Drawable mFastScrollerBg; + private Rect mTmpFastScrollerInvalidateRect = new Rect(); + private Rect mFastScrollerBounds = new Rect(); + + private String mFastScrollSectionName; + private Paint mFastScrollTextPaint; + private Rect mFastScrollTextBounds = new Rect(); + private float mFastScrollAlpha; + + private int mDownX; + private int mDownY; + private int mLastX; + private int mLastY; + private int mScrollbarWidth; + private int mScrollbarMinHeight; + private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); + + + public BaseRecyclerView(Context context) { this(context, null); } @@ -49,6 +109,24 @@ public class BaseRecyclerView extends RecyclerView ScrollListener listener = new ScrollListener(); setOnScrollListener(listener); + + Resources res = context.getResources(); + int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size); + mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb); + mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg); + mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); + mFastScrollTextPaint = new Paint(); + mFastScrollTextPaint.setColor(Color.WHITE); + mFastScrollTextPaint.setAntiAlias(true); + mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.all_apps_fast_scroll_text_size)); + mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); + mScrollbarMinHeight = + res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); + mScrollbarInset = + res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); + setFastScrollerAlpha(mFastScrollAlpha); + setOverScrollMode(View.OVER_SCROLL_NEVER); } private class ScrollListener extends OnScrollListener { @@ -68,17 +146,74 @@ public class BaseRecyclerView extends RecyclerView addOnItemTouchListener(this); } + /** + * We intercept the touch handling only to support fast scrolling when initiated from the + * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. + */ @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - if (shouldStopScroll(ev)) { - stopScroll(); - } - return false; + return handleTouchEvent(ev); } @Override public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - // Do nothing. + handleTouchEvent(ev); + } + + /** + * Handles the touch event and determines whether to show the fast scroller (or updates it if + * it is already showing). + */ + private boolean handleTouchEvent(MotionEvent ev) { + ViewConfiguration config = ViewConfiguration.get(getContext()); + + int action = ev.getAction(); + int x = (int) ev.getX(); + int y = (int) ev.getY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + // Keep track of the down positions + mDownX = mLastX = x; + mDownY = mLastY = y; + if (shouldStopScroll(ev)) { + stopScroll(); + } + break; + case MotionEvent.ACTION_MOVE: + // Check if we are scrolling + if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && + Math.abs(y - mDownY) > config.getScaledTouchSlop()) { + getParent().requestDisallowInterceptTouchEvent(true); + mDraggingFastScroller = true; + animateFastScrollerVisibility(true); + } + if (mDraggingFastScroller) { + mLastX = x; + mLastY = y; + + // Scroll to the right position, and update the section name + int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); + int bottom = getHeight() - getPaddingBottom() - + (mFastScrollerBg.getBounds().height() / 2); + float boundedY = (float) Math.max(top, Math.min(bottom, y)); + mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / + (bottom - top)); + + // Combine the old and new fast scroller bounds to create the full invalidate + // rect + mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); + updateFastScrollerBounds(); + mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); + invalidateFastScroller(mTmpFastScrollerInvalidateRect); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mDraggingFastScroller = false; + animateFastScrollerVisibility(false); + break; + } + return mDraggingFastScroller; } public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { @@ -99,4 +234,127 @@ public class BaseRecyclerView extends RecyclerView } return false; } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + drawVerticalScrubber(canvas); + drawFastScrollerPopup(canvas); + } + + /** + * Draws the vertical scrollbar. + */ + private void drawVerticalScrubber(Canvas canvas) { + updateVerticalScrollbarBounds(); + + // Draw the scroll bar + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(verticalScrollbarBounds.left, verticalScrollbarBounds.top); + mScrollbar.setBounds(0, 0, mScrollbarWidth, verticalScrollbarBounds.height()); + mScrollbar.draw(canvas); + canvas.restoreToCount(restoreCount); + } + + /** + * Draws the fast scroller popup. + */ + private void drawFastScrollerPopup(Canvas canvas) { + if (mFastScrollAlpha > 0f && mFastScrollSectionName != null && !mFastScrollSectionName.isEmpty()) { + // Draw the fast scroller popup + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); + mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollerBg.draw(canvas); + mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, + mFastScrollSectionName.length(), mFastScrollTextBounds); + float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); + canvas.drawText(mFastScrollSectionName, + (mFastScrollerBounds.width() - textWidth) / 2, + mFastScrollerBounds.height() - + (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, + mFastScrollTextPaint); + canvas.restoreToCount(restoreCount); + } + } + + /** + * Returns the scroll bar width. + */ + public int getScrollbarWidth() { + return mScrollbarWidth; + } + + /** + * Sets the fast scroller alpha. + */ + public void setFastScrollerAlpha(float alpha) { + mFastScrollAlpha = alpha; + invalidateFastScroller(mFastScrollerBounds); + } + + /** + * Maps the touch (from 0..1) to the adapter position that should be visible. + *

Override in each subclass of this base class. + */ + public String scrollToPositionAtProgress(float touchFraction) { + return null; + } + + /** + * Updates the bounds for the scrollbar. + *

Override in each subclass of this base class. + */ + public void updateVerticalScrollbarBounds() {}; + + /** + * Animates the visibility of the fast scroller popup. + */ + private void animateFastScrollerVisibility(boolean visible) { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); + anim.setDuration(visible ? 200 : 150); + anim.start(); + } + + /** + * Invalidates the fast scroller popup. + */ + protected void invalidateFastScroller(Rect bounds) { + invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + /** + * Returns whether a given point is near the scrollbar. + */ + private boolean isPointNearScrollbar(int x, int y) { + // Check if we are scrolling + updateVerticalScrollbarBounds(); + verticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); + return verticalScrollbarBounds.contains(x, y); + } + + /** + * Updates the bounds for the fast scroller. + */ + private void updateFastScrollerBounds() { + if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { + int x; + int y; + + // Calculate the position for the fast scroller popup + Rect bgBounds = mFastScrollerBg.getBounds(); + if (Utilities.isRtl(getResources())) { + x = mBackgroundPadding.left + getScrollBarSize(); + } else { + x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); + } + y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); + y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - + bgBounds.height())); + mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); + } else { + mFastScrollerBounds.setEmpty(); + } + } } \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index e95fa325a..cc5add3b2 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -15,75 +15,34 @@ */ package com.android.launcher3.allapps; -import android.animation.ObjectAnimator; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; + import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.R; import com.android.launcher3.Utilities; import java.util.List; /** - * A RecyclerView with custom fastscroll support. This is the main container for the all apps - * icons. + * A RecyclerView with custom fast scroll support for the all apps view. */ public class AllAppsRecyclerView extends BaseRecyclerView { - /** - * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() - * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so - * that we can calculate what the scroll bar looks like, and where to jump to from the fast - * scroller. - */ - private static class ScrollPositionState { - // The index of the first visible row - int rowIndex; - // The offset of the first visible row - int rowTopOffset; - // The height of a given row (they are currently all the same height) - int rowHeight; - } - - private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - private AlphabeticalAppsList mApps; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; - private Drawable mScrollbar; - private Drawable mFastScrollerBg; - private Rect mTmpFastScrollerInvalidateRect = new Rect(); - private Rect mFastScrollerBounds = new Rect(); - private Rect mVerticalScrollbarBounds = new Rect(); - private boolean mDraggingFastScroller; - private String mFastScrollSectionName; - private Paint mFastScrollTextPaint; - private Rect mFastScrollTextBounds = new Rect(); - private float mFastScrollAlpha; private int mPredictionBarHeight; - private int mDownX; - private int mDownY; - private int mLastX; - private int mLastY; - private int mScrollbarWidth; private int mScrollbarMinHeight; - private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); - private ScrollPositionState mScrollPosState = new ScrollPositionState(); private Launcher mLauncher; @@ -102,25 +61,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr); - mLauncher = (Launcher) context; - Resources res = context.getResources(); - int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size); - mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb); - mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg); - mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); - mFastScrollTextPaint = new Paint(); - mFastScrollTextPaint.setColor(Color.WHITE); - mFastScrollTextPaint.setAntiAlias(true); - mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( - R.dimen.all_apps_fast_scroll_text_size)); - mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); - mScrollbarMinHeight = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); - mScrollbarInset = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); - setFastScrollerAlpha(getFastScrollerAlpha()); - setOverScrollMode(View.OVER_SCROLL_NEVER); } /** @@ -157,28 +98,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView { mPredictionBarHeight = height; } - /** - * Sets the fast scroller alpha. - */ - public void setFastScrollerAlpha(float alpha) { - mFastScrollAlpha = alpha; - invalidateFastScroller(mFastScrollerBounds); - } - - /** - * Gets the fast scroller alpha. - */ - public float getFastScrollerAlpha() { - return mFastScrollAlpha; - } - - /** - * Returns the scroll bar width. - */ - public int getScrollbarWidth() { - return mScrollbarWidth; - } - /** * Scrolls this recycler view to the top. */ @@ -191,11 +110,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView { */ public int getScrollPosition() { List items = mApps.getAdapterItems(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { + getCurScrollState(scrollPosState, items); + if (scrollPosState.rowIndex != -1) { int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - return getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + - predictionBarHeight - mScrollPosState.rowTopOffset; + return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) + + predictionBarHeight - scrollPosState.rowTopOffset; } return 0; } @@ -206,150 +125,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView { addOnItemTouchListener(this); } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - drawVerticalScrubber(canvas); - drawFastScrollerPopup(canvas); - } - - /** - * We intercept the touch handling only to support fast scrolling when initiated from the - * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. - */ - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - return handleTouchEvent(ev); - } - - @Override - public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - handleTouchEvent(ev); - } - - /** - * Handles the touch event and determines whether to show the fast scroller (or updates it if - * it is already showing). - */ - private boolean handleTouchEvent(MotionEvent ev) { - ViewConfiguration config = ViewConfiguration.get(getContext()); - - int action = ev.getAction(); - int x = (int) ev.getX(); - int y = (int) ev.getY(); - switch (action) { - case MotionEvent.ACTION_DOWN: - // Keep track of the down positions - mDownX = mLastX = x; - mDownY = mLastY = y; - if (shouldStopScroll(ev)) { - stopScroll(); - } - break; - case MotionEvent.ACTION_MOVE: - // Check if we are scrolling - if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && - Math.abs(y - mDownY) > config.getScaledTouchSlop()) { - getParent().requestDisallowInterceptTouchEvent(true); - mDraggingFastScroller = true; - animateFastScrollerVisibility(true); - } - if (mDraggingFastScroller) { - mLastX = x; - mLastY = y; - - // Scroll to the right position, and update the section name - int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); - int bottom = getHeight() - getPaddingBottom() - - (mFastScrollerBg.getBounds().height() / 2); - float boundedY = (float) Math.max(top, Math.min(bottom, y)); - mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / - (bottom - top)); - - // Combine the old and new fast scroller bounds to create the full invalidate - // rect - mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); - updateFastScrollerBounds(); - mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); - invalidateFastScroller(mTmpFastScrollerInvalidateRect); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mDraggingFastScroller = false; - animateFastScrollerVisibility(false); - break; - } - return mDraggingFastScroller; - } - - /** - * Animates the visibility of the fast scroller popup. - */ - private void animateFastScrollerVisibility(boolean visible) { - ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); - anim.setDuration(visible ? 200 : 150); - anim.start(); - } - - /** - * Returns whether a given point is near the scrollbar. - */ - private boolean isPointNearScrollbar(int x, int y) { - // Check if we are scrolling - updateVerticalScrollbarBounds(); - mVerticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); - return mVerticalScrollbarBounds.contains(x, y); - } - - /** - * Draws the fast scroller popup. - */ - private void drawFastScrollerPopup(Canvas canvas) { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - // Draw the fast scroller popup - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); - mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollerBg.draw(canvas); - mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, - mFastScrollSectionName.length(), mFastScrollTextBounds); - float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); - canvas.drawText(mFastScrollSectionName, - (mFastScrollerBounds.width() - textWidth) / 2, - mFastScrollerBounds.height() - - (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, - mFastScrollTextPaint); - canvas.restoreToCount(restoreCount); - } - } - - /** - * Draws the vertical scrollbar. - */ - private void drawVerticalScrubber(Canvas canvas) { - updateVerticalScrollbarBounds(); - - // Draw the scroll bar - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mVerticalScrollbarBounds.left, mVerticalScrollbarBounds.top); - mScrollbar.setBounds(0, 0, mScrollbarWidth, mVerticalScrollbarBounds.height()); - mScrollbar.draw(canvas); - canvas.restoreToCount(restoreCount); - } - - /** - * Invalidates the fast scroller popup. - */ - private void invalidateFastScroller(Rect bounds) { - invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); - } - /** * Maps the touch (from 0..1) to the adapter position that should be visible. */ - private String scrollToPositionAtProgress(float touchFraction) { + @Override + public String scrollToPositionAtProgress(float touchFraction) { // Ensure that we have any sections List fastScrollSections = mApps.getFastScrollerSections(); @@ -393,27 +173,60 @@ public class AllAppsRecyclerView extends BaseRecyclerView { return lastScrollSection.sectionName; } + + /** + * Returns the row index for a app index in the list. + */ + private int findRowForAppIndex(int index) { + List sections = mApps.getSections(); + int appIndex = 0; + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + if (appIndex + info.numApps > index) { + return rowCount + ((index - appIndex) / mNumAppsPerRow); + } + appIndex += info.numApps; + rowCount += numRowsInSection; + } + return appIndex; + } + + /** + * Returns the total number of rows in the list. + */ + private int getNumRows() { + List sections = mApps.getSections(); + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + rowCount += numRowsInSection; + } + return rowCount; + } + + /** * Updates the bounds for the scrollbar. */ - private void updateVerticalScrollbarBounds() { + @Override + public void updateVerticalScrollbarBounds() { List items = mApps.getAdapterItems(); - // Skip early if there are no items + // Skip early if there are no items. if (items.isEmpty()) { - mVerticalScrollbarBounds.setEmpty(); + verticalScrollbarBounds.setEmpty(); return; } // Find the index and height of the first visible row (all rows have the same height) - int x; - int y; + int x, y; int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; int rowCount = getNumRows(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { + getCurScrollState(scrollPosState, items); + if (scrollPosState.rowIndex != -1) { int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * mScrollPosState.rowHeight + predictionBarHeight; + int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight; if (totalScrollHeight > height) { int scrollbarHeight = Math.max(mScrollbarMinHeight, (int) (height / ((float) totalScrollHeight / height))); @@ -422,78 +235,23 @@ public class AllAppsRecyclerView extends BaseRecyclerView { if (Utilities.isRtl(getResources())) { x = mBackgroundPadding.left; } else { - x = getWidth() - mBackgroundPadding.right - mScrollbarWidth; + x = getWidth() - mBackgroundPadding.right - getScrollbarWidth(); } // To calculate the offset, we compute the percentage of the total scrollable height // that the user has already scrolled and then map that to the scroll bar bounds int availableY = totalScrollHeight - height; int availableScrollY = height - scrollbarHeight; - y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + predictionBarHeight - - mScrollPosState.rowTopOffset; + y = (scrollPosState.rowIndex * scrollPosState.rowHeight) + predictionBarHeight + - scrollPosState.rowTopOffset; y = getPaddingTop() + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); - mVerticalScrollbarBounds.set(x, y, x + mScrollbarWidth, y + scrollbarHeight); + verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight); return; } } - mVerticalScrollbarBounds.setEmpty(); - } - - /** - * Updates the bounds for the fast scroller. - */ - private void updateFastScrollerBounds() { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - int x; - int y; - - // Calculate the position for the fast scroller popup - Rect bgBounds = mFastScrollerBg.getBounds(); - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left + getScrollBarSize(); - } else { - x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); - } - y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); - y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - - bgBounds.height())); - mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); - } else { - mFastScrollerBounds.setEmpty(); - } - } - - /** - * Returns the row index for a app index in the list. - */ - private int findRowForAppIndex(int index) { - List sections = mApps.getSections(); - int appIndex = 0; - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - if (appIndex + info.numApps > index) { - return rowCount + ((index - appIndex) / mNumAppsPerRow); - } - appIndex += info.numApps; - rowCount += numRowsInSection; - } - return appIndex; - } - - /** - * Returns the total number of rows in the list. - */ - private int getNumRows() { - List sections = mApps.getSections(); - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - rowCount += numRowsInSection; - } - return rowCount; + verticalScrollbarBounds.setEmpty(); } /** diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 778cf9ef1..11c2107f2 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -65,7 +65,7 @@ public class WidgetsContainerView extends BaseContainerView private IconCache mIconCache; /* Recycler view related member variables */ - private RecyclerView mView; + private WidgetsRecyclerView mView; private WidgetsListAdapter mAdapter; /* Touch handling related member variables. */ @@ -100,7 +100,7 @@ public class WidgetsContainerView extends BaseContainerView @Override protected void onFinishInflate() { - mView = (RecyclerView) findViewById(R.id.widgets_list_view); + mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view); mView.setAdapter(mAdapter); // This extends the layout space so that preloading happen for the {@link RecyclerView} @@ -351,6 +351,7 @@ public class WidgetsContainerView extends BaseContainerView * Initialize the widget data model. */ public void addWidgets(WidgetsModel model) { + mView.setWidgets(model); mAdapter.setWidgetsModel(model); mAdapter.notifyDataSetChanged(); } diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 7439a44f8..e82c0a631 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -32,7 +32,6 @@ import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 31ef5d6fc..bef255908 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -17,14 +17,23 @@ package com.android.launcher3.widget; import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; +import android.view.MotionEvent; + import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.model.WidgetsModel; /** * The widgets recycler view. */ public class WidgetsRecyclerView extends BaseRecyclerView { + private WidgetsModel mWidgets; + private Rect mBackgroundPadding = new Rect(); + public WidgetsRecyclerView(Context context) { this(context, null); } @@ -37,4 +46,67 @@ public class WidgetsRecyclerView extends BaseRecyclerView { super(context, attrs, defStyleAttr); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + addOnItemTouchListener(this); + } + + public void updateBackgroundPadding(Drawable background) { + background.getPadding(mBackgroundPadding); + } + + /** + * Sets the widget model in this view, used to determine the fast scroll position. + */ + public void setWidgets(WidgetsModel widgets) { + mWidgets = widgets; + } + + /** + * Maps the touch (from 0..1) to the adapter position that should be visible. + */ + @Override + public String scrollToPositionAtProgress(float touchFraction) { + // Ensure that we have any sections + return ""; + } + + /** + * Updates the bounds for the scrollbar. + */ + @Override + public void updateVerticalScrollbarBounds() { + int rowCount = mWidgets.getPackageSize(); + + // Skip early if there are no items. + if (rowCount == 0) { + verticalScrollbarBounds.setEmpty(); + return; + } + + int x, y; + getCurScrollState(scrollPosState); + if (scrollPosState.rowIndex < 0) { + verticalScrollbarBounds.setEmpty(); + } + // TODO + } + + /** + * Returns the current scroll state. + */ + private void getCurScrollState(ScrollPositionState stateOut) { + stateOut.rowIndex = -1; + stateOut.rowTopOffset = -1; + stateOut.rowHeight = -1; + + int rowCount = mWidgets.getPackageSize(); + + // Return early if there are no items + if (rowCount == 0) { + return; + } + // TODO + } } \ No newline at end of file -- cgit v1.2.3 From 2b7bf87bba2a17374958b73ef07397282d7e655e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 28 May 2015 10:06:43 -0700 Subject: Fixing screen jumps when performing DnD in rtl mode Bug: 21445825 Change-Id: Ie43d24ce9c8c08dc2c4b3e24692d497760d8cd2d --- src/com/android/launcher3/Workspace.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index c0a1cfc1e..e8f9c5810 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -731,6 +731,7 @@ public class Workspace extends PagedView fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION, onComplete, stripEmptyScreens); } else { + snapToPage(getNextPage(), 0); fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION, onComplete, stripEmptyScreens); } -- cgit v1.2.3 From d3d8c958a28aee7815fa2bc81b5c0b79903d51ad Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 Jun 2015 10:09:06 -0700 Subject: Null check in accessibility delegate bug: 21338696 Change-Id: I00d67e53e03e33b26a8eadb669b60fec47553f26 --- .../launcher3/accessibility/LauncherAccessibilityDelegate.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index eeec8c580..93cf8d050 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -219,9 +219,13 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { } private ArrayList getSupportedResizeActions(View host, LauncherAppWidgetInfo info) { - AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo(); ArrayList actions = new ArrayList<>(); + AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo(); + if (providerInfo == null) { + return actions; + } + CellLayout layout = (CellLayout) host.getParent().getParent(); if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) { if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) || -- cgit v1.2.3 From 595395d84f0507a1c5c163ad4722fa4345376079 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 1 Jun 2015 11:18:12 -0700 Subject: Avoid merging sections across scripts. Bug: 20222023 Change-Id: I6f6577112845fec14ae2e9c6e88ff3569bd2f1af --- .../launcher3/allapps/AlphabeticalAppsList.java | 27 +++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 3d1503d46..4ab016f76 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -10,6 +10,8 @@ import com.android.launcher3.Launcher; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.AppNameComparator; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -130,7 +132,8 @@ public class AlphabeticalAppsList { * Common interface for different merging strategies. */ private interface MergeAlgorithm { - boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount); + boolean continueMerging(SectionInfo section, SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount); } /** @@ -139,7 +142,8 @@ public class AlphabeticalAppsList { private static class TabletMergeAlgorithm implements MergeAlgorithm { @Override - public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + public boolean continueMerging(SectionInfo section, SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount) { // Merge EVERYTHING return true; } @@ -153,23 +157,34 @@ public class AlphabeticalAppsList { private int mMinAppsPerRow; private int mMinRowsInMergedSection; private int mMaxAllowableMerges; + private CharsetEncoder mAsciiEncoder; public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { mMinAppsPerRow = minAppsPerRow; mMinRowsInMergedSection = minRowsInMergedSection; mMaxAllowableMerges = maxNumMerges; + mAsciiEncoder = StandardCharsets.US_ASCII.newEncoder(); } @Override - public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + public boolean continueMerging(SectionInfo section, SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount) { // Continue merging if the number of hanging apps on the final row is less than some // fixed number (ragged), the merged rows has yet to exceed some minimum row count, // and while the number of merged sections is less than some fixed number of merges int rows = sectionAppCount / numAppsPerRow; int cols = sectionAppCount % numAppsPerRow; + // Ensure that we do not merge across scripts, currently we only allow for english and + // native scripts so we can test if both can just be ascii encoded + boolean isCrossScript = false; + if (section.firstAppItem != null && withSection.firstAppItem != null) { + isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) != + mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName); + } return (0 < cols && cols < mMinAppsPerRow) && rows < mMinRowsInMergedSection && - mergeCount < mMaxAllowableMerges; + mergeCount < mMaxAllowableMerges && + !isCrossScript; } } @@ -527,8 +542,8 @@ public class AlphabeticalAppsList { int mergeCount = 1; // Merge rows based on the current strategy - while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) && - (i + 1) < mSections.size()) { + while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount, + section, mSections.get(i + 1)) && (i + 1) < mSections.size()) { SectionInfo nextSection = mSections.remove(i + 1); // Remove the next section break -- cgit v1.2.3 From 1612d110b6e7759d592e0294bd969c581b0108e8 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 1 Jun 2015 19:20:23 +0000 Subject: Revert "Avoid merging sections across scripts." This reverts commit 595395d84f0507a1c5c163ad4722fa4345376079. Change-Id: I44913141f6cb3af29b4509d6b7c317247d1f24c9 --- .../launcher3/allapps/AlphabeticalAppsList.java | 27 +++++----------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 4ab016f76..3d1503d46 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -10,8 +10,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.AppNameComparator; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -132,8 +130,7 @@ public class AlphabeticalAppsList { * Common interface for different merging strategies. */ private interface MergeAlgorithm { - boolean continueMerging(SectionInfo section, SectionInfo withSection, - int sectionAppCount, int numAppsPerRow, int mergeCount); + boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount); } /** @@ -142,8 +139,7 @@ public class AlphabeticalAppsList { private static class TabletMergeAlgorithm implements MergeAlgorithm { @Override - public boolean continueMerging(SectionInfo section, SectionInfo withSection, - int sectionAppCount, int numAppsPerRow, int mergeCount) { + public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { // Merge EVERYTHING return true; } @@ -157,34 +153,23 @@ public class AlphabeticalAppsList { private int mMinAppsPerRow; private int mMinRowsInMergedSection; private int mMaxAllowableMerges; - private CharsetEncoder mAsciiEncoder; public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { mMinAppsPerRow = minAppsPerRow; mMinRowsInMergedSection = minRowsInMergedSection; mMaxAllowableMerges = maxNumMerges; - mAsciiEncoder = StandardCharsets.US_ASCII.newEncoder(); } @Override - public boolean continueMerging(SectionInfo section, SectionInfo withSection, - int sectionAppCount, int numAppsPerRow, int mergeCount) { + public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { // Continue merging if the number of hanging apps on the final row is less than some // fixed number (ragged), the merged rows has yet to exceed some minimum row count, // and while the number of merged sections is less than some fixed number of merges int rows = sectionAppCount / numAppsPerRow; int cols = sectionAppCount % numAppsPerRow; - // Ensure that we do not merge across scripts, currently we only allow for english and - // native scripts so we can test if both can just be ascii encoded - boolean isCrossScript = false; - if (section.firstAppItem != null && withSection.firstAppItem != null) { - isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) != - mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName); - } return (0 < cols && cols < mMinAppsPerRow) && rows < mMinRowsInMergedSection && - mergeCount < mMaxAllowableMerges && - !isCrossScript; + mergeCount < mMaxAllowableMerges; } } @@ -542,8 +527,8 @@ public class AlphabeticalAppsList { int mergeCount = 1; // Merge rows based on the current strategy - while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount, - section, mSections.get(i + 1)) && (i + 1) < mSections.size()) { + while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) && + (i + 1) < mSections.size()) { SectionInfo nextSection = mSections.remove(i + 1); // Remove the next section break -- cgit v1.2.3 From 51b1c83946b390eb0861a899e26f77319c3f45ad Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 1 Jun 2015 12:35:17 -0700 Subject: Avoid merging app sections across scripts. Bug: 20222023 Change-Id: I171d75a33694fc2cb347a32cff209f64fe3bf7c3 --- .../launcher3/allapps/AlphabeticalAppsList.java | 40 ++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 3d1503d46..7a9dfa16e 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -10,6 +10,8 @@ import com.android.launcher3.Launcher; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.AppNameComparator; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -130,46 +132,63 @@ public class AlphabeticalAppsList { * Common interface for different merging strategies. */ private interface MergeAlgorithm { - boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount); + boolean continueMerging(SectionInfo section, SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount); } /** - * The logic we use to merge sections on tablets. + * The logic we use to merge sections on tablets. Currently, we don't show section names on + * tablet layouts, so just merge all the sections indiscriminately. */ private static class TabletMergeAlgorithm implements MergeAlgorithm { @Override - public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + public boolean continueMerging(SectionInfo section, SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount) { // Merge EVERYTHING return true; } } /** - * The logic we use to merge sections on phones. + * The logic we use to merge sections on phones. We only merge sections when their final row + * contains less than a certain number of icons, and stop at a specified max number of merges. + * In addition, we will try and not merge sections that identify apps from different scripts. */ private static class PhoneMergeAlgorithm implements MergeAlgorithm { private int mMinAppsPerRow; private int mMinRowsInMergedSection; private int mMaxAllowableMerges; + private CharsetEncoder mAsciiEncoder; public PhoneMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { mMinAppsPerRow = minAppsPerRow; mMinRowsInMergedSection = minRowsInMergedSection; mMaxAllowableMerges = maxNumMerges; + mAsciiEncoder = StandardCharsets.US_ASCII.newEncoder(); } @Override - public boolean continueMerging(int sectionAppCount, int numAppsPerRow, int mergeCount) { + public boolean continueMerging(SectionInfo section, SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount) { // Continue merging if the number of hanging apps on the final row is less than some // fixed number (ragged), the merged rows has yet to exceed some minimum row count, // and while the number of merged sections is less than some fixed number of merges int rows = sectionAppCount / numAppsPerRow; int cols = sectionAppCount % numAppsPerRow; + + // Ensure that we do not merge across scripts, currently we only allow for english and + // native scripts so we can test if both can just be ascii encoded + boolean isCrossScript = false; + if (section.firstAppItem != null && withSection.firstAppItem != null) { + isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) != + mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName); + } return (0 < cols && cols < mMinAppsPerRow) && rows < mMinRowsInMergedSection && - mergeCount < mMaxAllowableMerges; + mergeCount < mMaxAllowableMerges && + !isCrossScript; } } @@ -521,15 +540,16 @@ public class AlphabeticalAppsList { // Go through each section and try and merge some of the sections if (AllAppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { int sectionAppCount = 0; - for (int i = 0; i < mSections.size(); i++) { + for (int i = 0; i < mSections.size() - 1; i++) { SectionInfo section = mSections.get(i); + SectionInfo nextSection = mSections.get(i + 1); sectionAppCount = section.numApps; int mergeCount = 1; // Merge rows based on the current strategy - while (mMergeAlgorithm.continueMerging(sectionAppCount, mNumAppsPerRow, mergeCount) && - (i + 1) < mSections.size()) { - SectionInfo nextSection = mSections.remove(i + 1); + while (mMergeAlgorithm.continueMerging(section, nextSection, sectionAppCount, + mNumAppsPerRow, mergeCount)) { + nextSection = mSections.remove(i + 1); // Remove the next section break mAdapterItems.remove(nextSection.sectionBreakItem); -- cgit v1.2.3 From 7fc77cad3d06bd3647e550f7419e89116471240a Mon Sep 17 00:00:00 2001 From: Rahul Chaturvedi Date: Tue, 19 May 2015 18:02:16 -0700 Subject: Add the Allow Rotation setting to Launcher3. This CL adds a Settings activity along with the code needed to provide a "Allow Rotation" setting to all phones and tablets. This setting is set to false for phones and true for tablets. On changing the setting from unlocked to locked, the launcher (and the Settings activity) will get locked to the orientation the user was in when he disabled "Allow Rotation". This is consistent with how the natural rotation feature of Android works. Change-Id: I8a1c59d1fa0bb9262530cad96e0a9bdbab0d9344 --- AndroidManifest.xml | 6 +++ .../android/launcher3/WallpaperPickerActivity.java | 7 ++- res/values/strings.xml | 6 +++ res/xml/launcher_preferences.xml | 27 ++++++++++ src/com/android/launcher3/Launcher.java | 58 ++++++++++++++++++++-- src/com/android/launcher3/LauncherFiles.java | 5 +- src/com/android/launcher3/SettingsActivity.java | 53 ++++++++++++++++++++ src/com/android/launcher3/Utilities.java | 17 +++++-- 8 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 res/xml/launcher_preferences.xml create mode 100644 src/com/android/launcher3/SettingsActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b61b90c5c..9d8a76a5c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -148,6 +148,12 @@ + + + Settings + + + Allow rotation + + Allow rotation of the home screen + Unknown diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml new file mode 100644 index 000000000..f283575f0 --- /dev/null +++ b/res/xml/launcher_preferences.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1aa446e31..da6430a73 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -221,7 +221,8 @@ public class Launcher extends Activity public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data"; /** The different states that Launcher can be in. */ - enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }; + enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED } + @Thunk State mState = State.WORKSPACE; @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; @@ -405,6 +406,24 @@ public class Launcher extends Activity FocusIndicatorView mFocusHandler; + private boolean mRotationEnabled = false; + private boolean mPreferenceObserverRegistered = false; + + final private SharedPreferences.OnSharedPreferenceChangeListener mSettingsObserver = + new SharedPreferences.OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) { + if (mRotationEnabled = sharedPreferences.getBoolean( + Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false)) { + unlockScreenOrientation(true); + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); + } + } + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { if (DEBUG_STRICT_MODE) { @@ -505,7 +524,19 @@ public class Launcher extends Activity IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); registerReceiver(mCloseSystemDialogsReceiver, filter); - // On large interfaces, we want the screen to auto-rotate based on the current orientation + mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext()); + // In case we are on a device with locked rotation, we should look at preferences to check + // if the user has specifically allowed rotation. + if (!mRotationEnabled) { + getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, + Context.MODE_MULTI_PROCESS).registerOnSharedPreferenceChangeListener( + mSettingsObserver); + mPreferenceObserverRegistered = true; + mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); + } + + // On large interfaces, or on devices that a user has specifically enabled screen rotation, + // we want the screen to auto-rotate based on the current orientation unlockScreenOrientation(true); if (mLauncherCallbacks != null) { @@ -1300,8 +1331,11 @@ public class Launcher extends Activity protected boolean hasSettings() { if (mLauncherCallbacks != null) { return mLauncherCallbacks.hasSettings(); + } else { + // On devices with a locked orientation, we will at least have the allow rotation + // setting. + return !Utilities.isRotationAllowedForDevice(this); } - return false; } public void addToCustomContentPage(View customContent, @@ -2083,6 +2117,13 @@ public class Launcher extends Activity public void onDestroy() { super.onDestroy(); + if (mPreferenceObserverRegistered) { + getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, + Context.MODE_MULTI_PROCESS).unregisterOnSharedPreferenceChangeListener( + mSettingsObserver); + mPreferenceObserverRegistered = false; + } + // Remove all pending runnables mHandler.removeMessages(ADVANCE_MSG); mHandler.removeMessages(0); @@ -2865,6 +2906,8 @@ public class Launcher extends Activity if (LOGD) Log.d(TAG, "onClickSettingsButton"); if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickSettingsButton(v); + } else { + showSettingsActivity(); } } @@ -4472,7 +4515,7 @@ public class Launcher extends Activity } public void lockScreenOrientation() { - if (Utilities.isRotationEnabled(this)) { + if (mRotationEnabled) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() .getConfiguration().orientation)); @@ -4481,8 +4524,9 @@ public class Launcher extends Activity } } } + public void unlockScreenOrientation(boolean immediate) { - if (Utilities.isRotationEnabled(this)) { + if (mRotationEnabled) { if (immediate) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } else { @@ -4587,6 +4631,10 @@ public class Launcher extends Activity editor.apply(); } + private void showSettingsActivity() { + startActivity(new Intent(this, SettingsActivity.class)); + } + /** * To be overridden by subclasses to indicate that there is an in-activity full-screen intro * screen that must be displayed and dismissed. diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 03ec4bf7a..4aeaef0ad 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -27,6 +27,8 @@ public class LauncherFiles { public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; public static final String APP_ICONS_DB = "app_icons.db"; + public static final String ROTATION_PREF_FILE = "com.android.launcher3.rotation.prefs"; + public static final List ALL_FILES = Collections.unmodifiableList(Arrays.asList( DEFAULT_WALLPAPER_THUMBNAIL, DEFAULT_WALLPAPER_THUMBNAIL_OLD, @@ -37,7 +39,8 @@ public class LauncherFiles { WALLPAPER_IMAGES_DB, WIDGET_PREVIEWS_DB, MANAGED_USER_PREFERENCES_KEY, - APP_ICONS_DB)); + APP_ICONS_DB, + ROTATION_PREF_FILE)); // TODO: Delete these files on upgrade public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java new file mode 100644 index 000000000..a1da1b6fd --- /dev/null +++ b/src/com/android/launcher3/SettingsActivity.java @@ -0,0 +1,53 @@ +/* + * 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.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.preference.PreferenceFragment; + +/** + * Settings activity for Launcher. Currently implements the following setting: + * LockToPortrait + */ +public class SettingsActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new LauncherSettingsFragment()) + .commit(); + } + + /** + * This fragment shows the launcher preferences. + */ + @SuppressWarnings("WeakerAccess") + public static class LauncherSettingsFragment extends PreferenceFragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + getPreferenceManager().setSharedPreferencesName(LauncherFiles.ROTATION_PREF_FILE); + addPreferencesFromResource(R.xml.launcher_preferences); + } + } +} diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 256eba020..a9cbf6970 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -25,6 +25,7 @@ import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -67,6 +68,7 @@ import java.util.regex.Pattern; * Various utilities shared amongst the Launcher's classes. */ public final class Utilities { + private static final String TAG = "Launcher.Utilities"; private static int sIconWidth = -1; @@ -93,6 +95,8 @@ public final class Utilities { static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); + public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; + /** * Returns a FastBitmapDrawable with the icon, accurately sized. */ @@ -114,10 +118,15 @@ public final class Utilities { return Log.isLoggable(propertyName, Log.VERBOSE); } - public static boolean isRotationEnabled(Context c) { - boolean enableRotation = sForceEnableRotation || - c.getResources().getBoolean(R.bool.allow_rotation); - return enableRotation; + public static boolean isAllowRotationPrefEnabled(Context context) { + SharedPreferences sharedPrefs = context.getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, + Context.MODE_MULTI_PROCESS); + boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); + return sForceEnableRotation || allowRotationPref; + } + + public static boolean isRotationAllowedForDevice(Context context) { + return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation); } /** -- cgit v1.2.3 From dd5814c471c316091f59febcffedb8d58d693492 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 1 Jun 2015 15:42:37 -0700 Subject: Fixing crash on tablets from merging scheme changes. --- src/com/android/launcher3/allapps/AlphabeticalAppsList.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 7a9dfa16e..13e18289a 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -542,14 +542,14 @@ public class AlphabeticalAppsList { int sectionAppCount = 0; for (int i = 0; i < mSections.size() - 1; i++) { SectionInfo section = mSections.get(i); - SectionInfo nextSection = mSections.get(i + 1); sectionAppCount = section.numApps; int mergeCount = 1; // Merge rows based on the current strategy - while (mMergeAlgorithm.continueMerging(section, nextSection, sectionAppCount, - mNumAppsPerRow, mergeCount)) { - nextSection = mSections.remove(i + 1); + while (i < (mSections.size() - 1) && + mMergeAlgorithm.continueMerging(section, mSections.get(i + 1), + sectionAppCount, mNumAppsPerRow, mergeCount)) { + SectionInfo nextSection = mSections.remove(i + 1); // Remove the next section break mAdapterItems.remove(nextSection.sectionBreakItem); -- cgit v1.2.3 From 5683f871722254e4e357cf3fb77cd28156278e51 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 29 May 2015 14:54:40 -0700 Subject: Adding an asynchronous search interface for apps search Change-Id: Ib09df0a3d587dc60ed888ddbd0edf058e4a1cc3e --- src/com/android/launcher3/Launcher.java | 11 +++ .../launcher3/allapps/AllAppsContainerView.java | 68 +++++++-------- .../launcher3/allapps/AlphabeticalAppsList.java | 95 ++++++++++++++++----- .../launcher3/allapps/AppSearchManager.java | 59 +++++++++++++ .../allapps/SimpleAppSearchManagerImpl.java | 98 ++++++++++++++++++++++ .../launcher3/model/AbstractUserComparator.java | 67 +++++++++++++++ .../android/launcher3/model/AppNameComparator.java | 49 +++++------ 7 files changed, 357 insertions(+), 90 deletions(-) create mode 100644 src/com/android/launcher3/allapps/AppSearchManager.java create mode 100644 src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java create mode 100644 src/com/android/launcher3/model/AbstractUserComparator.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 51f091613..ef34660df 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -100,6 +100,7 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.AppSearchManager; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; @@ -583,6 +584,11 @@ public class Launcher extends Activity } } } + + @Override + public void setSearchManager(AppSearchManager manager) { + mAppsView.setSearchManager(manager); + } }); mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() { private boolean mImportanceStored = false; @@ -1158,6 +1164,11 @@ public class Launcher extends Activity * Called to dismiss all apps if it is showing. */ public void dismissAllApps(); + + /** + * Sets the search manager to be used for app search. + */ + public void setSearchManager(AppSearchManager manager); } public interface LauncherSearchCallbacks { diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index d81f97f24..c05f7c0b9 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -41,6 +41,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import android.widget.TextView; + import com.android.launcher3.AppInfo; import com.android.launcher3.BaseContainerView; import com.android.launcher3.BubbleTextView; @@ -54,15 +55,15 @@ import com.android.launcher3.Folder; import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherTransitionable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.allapps.AppSearchManager.AppSearchResultCallback; import com.android.launcher3.util.Thunk; +import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; /** @@ -171,7 +172,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, - ViewTreeObserver.OnPreDrawListener { + ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback { public static final boolean GRID_MERGE_SECTIONS = true; @@ -183,8 +184,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private static final int FADE_OUT_DURATION = 100; private static final int SEARCH_TRANSLATION_X_DP = 18; - private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); - @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; private LayoutInflater mLayoutInflater; @@ -221,6 +220,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; + private AppSearchManager mSearchManager; + public AllAppsContainerView(Context context) { this(context, null); } @@ -231,7 +232,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - LauncherAppState app = LauncherAppState.getInstance(); Resources res = context.getResources(); mLauncher = (Launcher) context; @@ -258,6 +258,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mContentMarginStart = mAdapter.getContentMarginStart(); mApps.setAdapter(mAdapter); + mSearchManager = mApps.newSimpleAppSearchManager(); } /** @@ -281,6 +282,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mApps.addApps(apps); } + public void setSearchManager(AppSearchManager searchManager) { + mSearchManager.cancel(true); + mSearchManager = searchManager; + } + /** * Updates existing apps in the list */ @@ -664,44 +670,25 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc public void afterTextChanged(final Editable s) { String queryText = s.toString(); if (queryText.isEmpty()) { - mApps.setFilter(null); + mSearchManager.cancel(true); + mApps.setOrderedFilter(null); } else { String formatStr = getResources().getString(R.string.all_apps_no_search_results); mAdapter.setEmptySearchText(String.format(formatStr, queryText)); - // Do an intersection of the words in the query and each title, and filter out all the - // apps that don't match all of the words in the query. - final String queryTextLower = queryText.toLowerCase(); - final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); - mApps.setFilter(new AlphabeticalAppsList.Filter() { - @Override - public boolean retainApp(AppInfo info, String sectionName) { - if (sectionName.toLowerCase().contains(queryTextLower)) { - return true; - } - String title = info.title.toString(); - String[] words = SPLIT_PATTERN.split(title.toLowerCase()); - for (int qi = 0; qi < queryWords.length; qi++) { - boolean foundMatch = false; - for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(queryWords[qi])) { - foundMatch = true; - break; - } - } - if (!foundMatch) { - // If there is a word in the query that does not match any words in this - // title, so skip it. - return false; - } - } - return true; - } - }); + mSearchManager.cancel(false); + mSearchManager.doSearch(queryText, this); } scrollToTop(); } + @Override + public void onSearchResult(ArrayList apps) { + if (apps != null) { + mApps.setOrderedFilter(apps); + } + } + @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { @@ -796,7 +783,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * recycler view. */ private boolean handleTouchEvent(MotionEvent ev) { - LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); int x = (int) ev.getX(); int y = (int) ev.getY(); @@ -919,6 +905,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Shows the search field. */ private void showSearchField() { + mSearchManager.connect(); + // Show the search bar and focus the search final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, getContext().getResources().getDisplayMetrics()); @@ -949,6 +937,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Hides the search field. */ private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + mSearchManager.cancel(true); + final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, getContext().getResources().getDisplayMetrics()); @@ -966,7 +956,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc if (resetTextField) { mSearchBarEditView.setText(""); } - mApps.setFilter(null); + mApps.setOrderedFilter(null); if (returnFocusToRecyclerView) { mAppsRecyclerView.requestFocus(); } @@ -983,7 +973,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc if (resetTextField) { mSearchBarEditView.setText(""); } - mApps.setFilter(null); + mApps.setOrderedFilter(null); mSearchButtonView.setAlpha(1f); mSearchButtonView.setTranslationX(0f); if (returnFocusToRecyclerView) { diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 13e18289a..0dc2d1e63 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -1,13 +1,31 @@ +/* + * 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.allapps; import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; + import com.android.launcher3.AppInfo; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.model.AbstractUserComparator; import com.android.launcher3.model.AppNameComparator; import java.nio.charset.CharsetEncoder; @@ -114,13 +132,6 @@ public class AlphabeticalAppsList { } } - /** - * A filter interface to limit the set of applications in the apps list. - */ - public interface Filter { - boolean retainApp(AppInfo info, String sectionName); - } - /** * Callback to notify when the set of adapter items have changed. */ @@ -198,7 +209,7 @@ public class AlphabeticalAppsList { private Launcher mLauncher; // The set of apps from the system not including predictions - private List mApps = new ArrayList<>(); + private final List mApps = new ArrayList<>(); // The set of filtered apps with the current filter private List mFilteredApps = new ArrayList<>(); // The current set of adapter items @@ -211,9 +222,10 @@ public class AlphabeticalAppsList { private List mPredictedAppComponents = new ArrayList<>(); // The set of predicted apps resolved from the component names and the current set of apps private List mPredictedApps = new ArrayList<>(); + // The of ordered component names as a result of a search query + private ArrayList mSearchResults; private HashMap mCachedSectionNames = new HashMap<>(); private RecyclerView.Adapter mAdapter; - private Filter mFilter; private AlphabeticIndexCompat mIndexer; private AppNameComparator mAppNameComparator; private MergeAlgorithm mMergeAlgorithm; @@ -235,6 +247,10 @@ public class AlphabeticalAppsList { mAdapterChangedCallback = cb; } + public SimpleAppSearchManagerImpl newSimpleAppSearchManager() { + return new SimpleAppSearchManagerImpl(mApps); + } + /** * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. */ @@ -293,22 +309,22 @@ public class AlphabeticalAppsList { * Returns whether there are is a filter set. */ public boolean hasFilter() { - return (mFilter != null); + return (mSearchResults != null); } /** * Returns whether there are no filtered results. */ public boolean hasNoFilteredResults() { - return (mFilter != null) && mFilteredApps.isEmpty(); + return (mSearchResults != null) && mFilteredApps.isEmpty(); } /** - * Sets the current filter for this list of apps. + * Sets the sorted list of filtered components. */ - public void setFilter(Filter f) { - if (mFilter != f) { - mFilter = f; + public void setOrderedFilter(ArrayList f) { + if (mSearchResults != f) { + mSearchResults = f; updateAdapterItems(); } } @@ -428,7 +444,9 @@ public class AlphabeticalAppsList { for (Map.Entry> entry : sectionMap.entrySet()) { allApps.addAll(entry.getValue()); } - mApps = allApps; + + mApps.clear(); + mApps.addAll(allApps); } else { // Just compute the section headers for use below for (AppInfo info : mApps) { @@ -483,16 +501,12 @@ public class AlphabeticalAppsList { // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections - int numApps = mApps.size(); + List apps = getFiltersAppInfos(); + int numApps = apps.size(); for (int i = 0; i < numApps; i++) { - AppInfo info = mApps.get(i); + AppInfo info = apps.get(i); String sectionName = getAndUpdateCachedSectionName(info.title); - // Check if we want to retain this app - if (mFilter != null && !mFilter.retainApp(info, sectionName)) { - continue; - } - // Create a new section if the section names do not match if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { lastSectionName = sectionName; @@ -533,6 +547,41 @@ public class AlphabeticalAppsList { } } + private List getFiltersAppInfos() { + if (mSearchResults == null) { + return mApps; + } + + int total = mSearchResults.size(); + final HashMap sortOrder = new HashMap<>(total); + for (int i = 0; i < total; i++) { + sortOrder.put(mSearchResults.get(i), i); + } + + ArrayList result = new ArrayList<>(); + for (AppInfo info : mApps) { + if (sortOrder.containsKey(info.componentName)) { + result.add(info); + } + } + + Collections.sort(result, new AbstractUserComparator( + LauncherAppState.getInstance().getContext()) { + + @Override + public int compare(AppInfo lhs, AppInfo rhs) { + Integer indexA = sortOrder.get(lhs.componentName); + int result = indexA.compareTo(sortOrder.get(rhs.componentName)); + if (result == 0) { + return super.compare(lhs, rhs); + } else { + return result; + } + } + }); + return result; + } + /** * Merges multiple sections to reduce visual raggedness. */ diff --git a/src/com/android/launcher3/allapps/AppSearchManager.java b/src/com/android/launcher3/allapps/AppSearchManager.java new file mode 100644 index 000000000..b6aa22341 --- /dev/null +++ b/src/com/android/launcher3/allapps/AppSearchManager.java @@ -0,0 +1,59 @@ +/* + * 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.allapps; + +import android.content.ComponentName; + +import java.util.ArrayList; + +/** + * Interface for handling app search. + */ +public interface AppSearchManager { + + /** + * Called when the search is about to be used. This method is optional for making a query but + * calling this appropriately can improve the initial response time. + */ + void connect(); + + /** + * Cancels all pending search requests. + * + * @param interruptActiveRequests if true, any active requests which are already executing will + * be invalidated, and the corresponding results will not be sent. The client should usually + * set this to true, before beginning a new search session. + */ + void cancel(boolean interruptActiveRequests); + + /** + * Performs a search + */ + void doSearch(String query, AppSearchResultCallback callback); + + /** + * Callback for getting search results. + */ + public interface AppSearchResultCallback { + + /** + * Called when the search is complete. + * + * @param apps sorted list of matching components or null if in case of failure. + */ + void onSearchResult(ArrayList apps); + } +} diff --git a/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java b/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java new file mode 100644 index 000000000..e8a31b546 --- /dev/null +++ b/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java @@ -0,0 +1,98 @@ +/* + * 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.allapps; + +import android.content.ComponentName; +import android.os.Handler; + +import com.android.launcher3.AppInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * An {@link AppSearchManager} which does label matching on the UI thread. + */ +public class SimpleAppSearchManagerImpl implements AppSearchManager { + + private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); + + private final List mApps; + private final Handler mResultHandler; + + public SimpleAppSearchManagerImpl(List apps) { + mApps = apps; + mResultHandler = new Handler(); + } + + @Override + public void connect() { + // No op + } + + @Override + public void cancel(boolean interruptActiveRequests) { + if (interruptActiveRequests) { + mResultHandler.removeCallbacksAndMessages(null); + } + } + + @Override + public void doSearch(String query, final AppSearchResultCallback callback) { + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = query.toLowerCase(); + final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); + final ArrayList result = new ArrayList(); + int total = mApps.size(); + + for (int i = 0; i < total; i++) { + AppInfo info = mApps.get(i); + if (!result.contains(info.componentName) && matches(info, queryWords)) { + result.add(info.componentName); + } + } + mResultHandler.post(new Runnable() { + + @Override + public void run() { + callback.onSearchResult(result); + } + }); + } + + private boolean matches(AppInfo info, String[] queryWords) { + String title = info.title.toString(); + String[] words = SPLIT_PATTERN.split(title.toLowerCase()); + for (int qi = 0; qi < queryWords.length; qi++) { + boolean foundMatch = false; + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(queryWords[qi])) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + // If there is a word in the query that does not match any words in this + // title, so skip it. + return false; + } + } + return true; + } + +} diff --git a/src/com/android/launcher3/model/AbstractUserComparator.java b/src/com/android/launcher3/model/AbstractUserComparator.java new file mode 100644 index 000000000..cf47ce648 --- /dev/null +++ b/src/com/android/launcher3/model/AbstractUserComparator.java @@ -0,0 +1,67 @@ +/* + * 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.model; + +import android.content.Context; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; + +import java.util.Comparator; +import java.util.HashMap; + +/** + * A comparator to arrange items based on user profiles. + */ +public abstract class AbstractUserComparator implements Comparator { + + private HashMap mUserSerialCache = new HashMap<>(); + private final UserManagerCompat mUserManager; + private final UserHandleCompat mMyUser; + + public AbstractUserComparator(Context context) { + mUserManager = UserManagerCompat.getInstance(context); + mMyUser = UserHandleCompat.myUserHandle(); + } + + @Override + public int compare(T lhs, T rhs) { + if (mMyUser.equals(lhs.user)) { + return -1; + } else { + Long aUserSerial = getAndCacheUserSerial(lhs.user); + Long bUserSerial = getAndCacheUserSerial(rhs.user); + return aUserSerial.compareTo(bUserSerial); + } + } + + /** + * Returns the user serial for this user, using a cached serial if possible. + */ + private Long getAndCacheUserSerial(UserHandleCompat user) { + Long userSerial = mUserSerialCache.get(user); + if (userSerial == null) { + userSerial = mUserManager.getSerialNumberForUser(user); + mUserSerialCache.put(user, userSerial); + } + return userSerial; + } + + public void clearUserCache() { + mUserSerialCache.clear(); + } +} diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java index 706f7515d..cdac40ac0 100644 --- a/src/com/android/launcher3/model/AppNameComparator.java +++ b/src/com/android/launcher3/model/AppNameComparator.java @@ -1,14 +1,27 @@ +/* + * 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.model; import android.content.Context; import com.android.launcher3.AppInfo; import com.android.launcher3.ItemInfo; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.compat.UserManagerCompat; + import java.text.Collator; import java.util.Comparator; -import java.util.HashMap; /** * Class to manage access to an app name comparator. @@ -16,17 +29,15 @@ import java.util.HashMap; * Used to sort application name in all apps view and widget tray view. */ public class AppNameComparator { - private final UserManagerCompat mUserManager; private final Collator mCollator; - private final Comparator mAppInfoComparator; + private final AbstractUserComparator mAppInfoComparator; private final Comparator mSectionNameComparator; - private HashMap mUserSerialCache = new HashMap<>(); public AppNameComparator(Context context) { mCollator = Collator.getInstance(); - mUserManager = UserManagerCompat.getInstance(context); - mAppInfoComparator = new Comparator() { + mAppInfoComparator = new AbstractUserComparator(context) { + @Override public final int compare(ItemInfo a, ItemInfo b) { // Order by the title in the current locale int result = compareTitles(a.title.toString(), b.title.toString()); @@ -38,13 +49,7 @@ public class AppNameComparator { if (result == 0) { // If the two apps are the same component, then prioritize by the order that // the app user was created (prioritizing the main user's apps) - if (UserHandleCompat.myUserHandle().equals(a.user)) { - return -1; - } else { - Long aUserSerial = getAndCacheUserSerial(a.user); - Long bUserSerial = getAndCacheUserSerial(b.user); - return aUserSerial.compareTo(bUserSerial); - } + return super.compare(a, b); } } return result; @@ -63,7 +68,7 @@ public class AppNameComparator { */ public Comparator getAppInfoComparator() { // Clear the user serial cache so that we get serials as needed in the comparator - mUserSerialCache.clear(); + mAppInfoComparator.clearUserCache(); return mAppInfoComparator; } @@ -90,16 +95,4 @@ public class AppNameComparator { // Order by the title in the current locale return mCollator.compare(titleA, titleB); } - - /** - * Returns the user serial for this user, using a cached serial if possible. - */ - private Long getAndCacheUserSerial(UserHandleCompat user) { - Long userSerial = mUserSerialCache.get(user); - if (userSerial == null) { - userSerial = mUserManager.getSerialNumberForUser(user); - mUserSerialCache.put(user, userSerial); - } - return userSerial; - } } -- cgit v1.2.3 From 761e820ae2b5482d1bece87e25525fcd17e23a8a Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 1 Jun 2015 12:38:30 -0700 Subject: Enabling resumeWhilePausing for the Launcher activity. Bug: 21342101 --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9d8a76a5c..fce469114 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -83,6 +83,7 @@ android:theme="@style/Theme" android:windowSoftInputMode="adjustPan" android:screenOrientation="nosensor" + android:resumeWhilePausing="true" android:enabled="true"> -- cgit v1.2.3 From a07c2f5c80908e8a612f4681314a7068b92baa9f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 Jun 2015 11:00:38 -0700 Subject: Folder UI fixes > Fixing top padding (b/21304523) > Alinging the label and page indicators to the icons > Fixing folder label alingment in RTL (b/21445821) > Updating footer animation (b/21336853) Bug: 21304523, 21445821, 21336853 Change-Id: I2cf3a48c76653b19437b31252d6ee559ce798094 --- res/layout/user_folder.xml | 8 +++++--- res/values/dimens.xml | 1 - src/com/android/launcher3/Folder.java | 21 +++++++++++++++++---- src/com/android/launcher3/FolderPagedView.java | 20 +++++++++++++++----- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index 67b69cabf..ecf7def48 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -40,7 +40,7 @@ android:layout_height="match_parent" android:paddingLeft="4dp" android:paddingRight="4dp" - android:paddingTop="4dp" + android:paddingTop="8dp" launcher:pageIndicator="@+id/folder_page_indicator" /> @@ -48,6 +48,7 @@ android:id="@+id/folder_footer" android:layout_width="match_parent" android:layout_height="wrap_content" + android:clipChildren="false" android:orientation="horizontal" android:paddingLeft="8dp" android:paddingRight="8dp" > @@ -63,8 +64,8 @@ android:gravity="center_horizontal" android:hint="@string/folder_hint_text" android:imeOptions="flagNoExtractUi" - android:paddingBottom="@dimen/folder_name_padding" - android:paddingTop="@dimen/folder_name_padding" + android:paddingBottom="8dp" + android:paddingTop="4dp" android:singleLine="true" android:textColor="#ff777777" android:textColorHighlight="#ffCCCCCC" @@ -78,6 +79,7 @@ android:layout_height="12dp" android:layout_gravity="center_vertical" layout="@layout/page_indicator" /> + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 246adcdad..7950862db 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -114,7 +114,6 @@ 4dp - 10dp 24dp diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index ec4ea044c..a5b7a6003 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -29,10 +29,12 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; +import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.text.InputType; import android.text.Selection; import android.text.Spannable; import android.util.AttributeSet; +import android.util.LayoutDirection; import android.util.Log; import android.view.ActionMode; import android.view.KeyEvent; @@ -89,7 +91,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList */ private static final float ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f; - public static final int FOOTER_ANIMATION_DURATION = 200; + private static final int FOLDER_NAME_ANIMATION_DURATION = 633; private static final int REORDER_DELAY = 250; private static final int ON_EXIT_CLOSE_DELAY = 400; @@ -502,7 +504,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList textAlpha.setStartDelay(mMaterialExpandStagger); textAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - anim.play(drift); anim.play(iconsAlpha); anim.play(textAlpha); @@ -545,7 +546,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList - mFooter.getPaddingLeft() - mFooter.getPaddingRight(); float textWidth = mFolderName.getPaint().measureText(mFolderName.getText().toString()); - mFolderName.setTranslationX((footerWidth - textWidth) / 2); + float translation = (footerWidth - textWidth) / 2; + mFolderName.setTranslationX(mContent.mIsRtl ? -translation : translation); mContent.setMarkerScale(0); // Do not update the flag if we are in drag mode. The flag will be updated, when we @@ -555,7 +557,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public void onAnimationEnd(Animator animation) { - mFolderName.animate().setDuration(FOOTER_ANIMATION_DURATION).translationX(0); + mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION) + .translationX(0) + .setInterpolator(new FastOutSlowInInterpolator()); mContent.animateMarkers(); if (updateAnimationFlag) { @@ -1032,6 +1036,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setFixedSize(contentWidth, contentHeight); mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec); + + if (mContent.getChildCount() > 0) { + int cellIconGap = (mContent.getPageAt(0).getCellWidth() + - mLauncher.getDeviceProfile().iconSizePx) / 2; + mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap, + mFooter.getPaddingTop(), + mContent.getPaddingRight() + cellIconGap, + mFooter.getPaddingBottom()); + } mFooter.measure(contentAreaWidthSpec, MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY)); diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 0bd6501ed..7d90ba2f7 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -24,6 +24,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; @@ -46,7 +47,12 @@ public class FolderPagedView extends PagedView { private static final int START_VIEW_REORDER_DELAY = 30; private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; - private static final int PAGE_INDICATOR_ANIMATION_DELAY = 150; + private static final int PAGE_INDICATOR_ANIMATION_START_DELAY = 300; + private static final int PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY = 150; + private static final int PAGE_INDICATOR_ANIMATION_DURATION = 400; + + // This value approximately overshoots to 1.5 times the original size. + private static final float PAGE_INDICATOR_OVERSHOOT_TENSION = 4.9f; /** * Fraction of the width to scroll when showing the next page hint. @@ -274,6 +280,7 @@ public class FolderPagedView extends PagedView { arrangeChildren(list, itemCount, true); } + @SuppressLint("RtlHardcoded") private void arrangeChildren(ArrayList list, int itemCount, boolean saveChanges) { ArrayList pages = new ArrayList(); for (int i = 0; i < getChildCount(); i++) { @@ -340,7 +347,9 @@ public class FolderPagedView extends PagedView { // Update footer mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE); - mFolder.mFolderName.setGravity(getPageCount() > 1 ? Gravity.START : Gravity.CENTER_HORIZONTAL); + // Set the gravity as LEFT or RIGHT instead of START, as START depends on the actual text. + mFolder.mFolderName.setGravity(getPageCount() > 1 ? + (mIsRtl ? Gravity.RIGHT : Gravity.LEFT) : Gravity.CENTER_HORIZONTAL); } public int getDesiredWidth() { @@ -645,12 +654,13 @@ public class FolderPagedView extends PagedView { public void animateMarkers() { int count = mPageIndicator.getChildCount(); - OvershootInterpolator interpolator = new OvershootInterpolator(4); + Interpolator interpolator = new OvershootInterpolator(PAGE_INDICATOR_OVERSHOOT_TENSION); for (int i = 0; i < count; i++) { mPageIndicator.getChildAt(i).animate().scaleX(1).scaleY(1) .setInterpolator(interpolator) - .setDuration(Folder.FOOTER_ANIMATION_DURATION) - .setStartDelay(PAGE_INDICATOR_ANIMATION_DELAY * i); + .setDuration(PAGE_INDICATOR_ANIMATION_DURATION) + .setStartDelay(PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY * i + + PAGE_INDICATOR_ANIMATION_START_DELAY); } } -- cgit v1.2.3 From c1cd23b4a7744105c124c4d9f642134b5f49694f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 Jun 2015 17:15:34 -0700 Subject: Build fix: Removing FastOutSlowIn interpolator Change-Id: Ia84445285d013fe8da3c895f5264f329f2403890 --- src/com/android/launcher3/Folder.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index a5b7a6003..fcfb717ab 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -29,12 +29,10 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; -import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.text.InputType; import android.text.Selection; import android.text.Spannable; import android.util.AttributeSet; -import android.util.LayoutDirection; import android.util.Log; import android.view.ActionMode; import android.view.KeyEvent; @@ -46,6 +44,7 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; +import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; @@ -559,7 +558,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void onAnimationEnd(Animator animation) { mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION) .translationX(0) - .setInterpolator(new FastOutSlowInInterpolator()); + .setInterpolator(Utilities.isLmpOrAbove() ? + AnimationUtils.loadInterpolator(mLauncher, + android.R.interpolator.fast_out_slow_in) + : new LogDecelerateInterpolator(100, 0)); mContent.animateMarkers(); if (updateAnimationFlag) { -- cgit v1.2.3 From 6ff6f6d649f02e605bc1dcd120308ff78186ee61 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 Jun 2015 22:02:11 -0700 Subject: Removing unused animation style Change-Id: Ib8031eb9604cb2c10d4ac07e22b94772f7967a89 --- src/com/android/launcher3/DragLayer.java | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index 41e053eed..aaa14e6a6 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -50,7 +50,6 @@ import java.util.ArrayList; public class DragLayer extends InsettableFrameLayout { public static final int ANIMATION_END_DISAPPEAR = 0; - public static final int ANIMATION_END_FADE_OUT = 1; public static final int ANIMATION_END_REMAIN_VISIBLE = 2; // Scrim color without any alpha component. @@ -70,7 +69,6 @@ public class DragLayer extends InsettableFrameLayout { // Variables relating to animation of views after drop private ValueAnimator mDropAnim = null; - private ValueAnimator mFadeOutAnim = null; private final TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); @Thunk DragView mDropView = null; @Thunk int mAnchorViewInitialScrollX = 0; @@ -762,7 +760,6 @@ public class DragLayer extends InsettableFrameLayout { final int animationEndStyle, View anchorView) { // Clean up the previous animations if (mDropAnim != null) mDropAnim.cancel(); - if (mFadeOutAnim != null) mFadeOutAnim.cancel(); // Show the drop view if it was previously hidden mDropView = view; @@ -790,9 +787,6 @@ public class DragLayer extends InsettableFrameLayout { case ANIMATION_END_DISAPPEAR: clearAnimatedView(); break; - case ANIMATION_END_FADE_OUT: - fadeOutDragView(); - break; case ANIMATION_END_REMAIN_VISIBLE: break; } @@ -816,31 +810,6 @@ public class DragLayer extends InsettableFrameLayout { return mDropView; } - @Thunk void fadeOutDragView() { - mFadeOutAnim = new ValueAnimator(); - mFadeOutAnim.setDuration(150); - mFadeOutAnim.setFloatValues(0f, 1f); - mFadeOutAnim.removeAllUpdateListeners(); - mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() { - public void onAnimationUpdate(ValueAnimator animation) { - final float percent = (Float) animation.getAnimatedValue(); - - float alpha = 1 - percent; - mDropView.setAlpha(alpha); - } - }); - mFadeOutAnim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - if (mDropView != null) { - mDragController.onDeferredEndDrag(mDropView); - } - mDropView = null; - invalidate(); - } - }); - mFadeOutAnim.start(); - } - @Override public void onChildViewAdded(View parent, View child) { super.onChildViewAdded(parent, child); -- cgit v1.2.3 From 316490e636aad788fcfbfc2e04dd4f0e145bdd00 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 2 Jun 2015 09:38:28 -0700 Subject: Removing some synthetic method creation > Make package-private and @Thunk all private methods and constructors accessed from inner classes. Change-Id: Ie5913860a0c33e48e9bf68f9b5b1699f64c2f174 --- src/com/android/launcher3/ButtonDropTarget.java | 2 +- src/com/android/launcher3/CellLayout.java | 6 ++---- src/com/android/launcher3/CommonAppTypeParser.java | 5 +++-- src/com/android/launcher3/DragView.java | 4 ++-- src/com/android/launcher3/FocusHelper.java | 2 +- src/com/android/launcher3/IconCache.java | 18 +++++++++--------- src/com/android/launcher3/Launcher.java | 4 ++-- src/com/android/launcher3/LauncherBackupHelper.java | 10 +++++----- src/com/android/launcher3/LauncherModel.java | 4 ++-- src/com/android/launcher3/LauncherProvider.java | 4 ++-- src/com/android/launcher3/PagedView.java | 9 +++++---- src/com/android/launcher3/WidgetPreviewLoader.java | 19 +++++++++---------- .../accessibility/LauncherAccessibilityDelegate.java | 2 +- .../launcher3/allapps/AllAppsContainerView.java | 14 +++++++------- .../android/launcher3/allapps/AllAppsGridAdapter.java | 2 +- .../launcher3/allapps/AlphabeticalAppsList.java | 3 ++- .../android/launcher3/model/AppNameComparator.java | 3 ++- src/com/android/launcher3/util/LongArrayMap.java | 2 +- .../launcher3/widget/WidgetHostViewLoader.java | 5 +++-- .../launcher3/widget/WidgetsContainerView.java | 3 ++- 20 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 4cd28c034..09a71b0cc 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -64,7 +64,7 @@ public abstract class ButtonDropTarget extends TextView protected Drawable mDrawable; private AnimatorSet mCurrentColorAnim; - private ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter; + @Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter; public ButtonDropTarget(Context context, AttributeSet attrs) { diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 61567ac00..ff7bcc43b 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -130,10 +130,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private final ClickShadowView mTouchFeedbackView; - @Thunk HashMap mReorderAnimators = new - HashMap(); - private HashMap - mShakeAnimators = new HashMap(); + @Thunk HashMap mReorderAnimators = new HashMap<>(); + @Thunk HashMap mShakeAnimators = new HashMap<>(); private boolean mItemPlacementDirty = false; diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java index 31641799d..5314ecff1 100644 --- a/src/com/android/launcher3/CommonAppTypeParser.java +++ b/src/com/android/launcher3/CommonAppTypeParser.java @@ -26,6 +26,7 @@ import android.util.Log; import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.backup.BackupProtos.Favorite; +import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParserException; @@ -44,8 +45,8 @@ public class CommonAppTypeParser implements LayoutParserCallback { private final long mItemId; - private final int mResId; - private final Context mContext; + @Thunk final int mResId; + @Thunk final Context mContext; ContentValues parsedValues; Intent parsedIntent; diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index b3323384d..dfa8202a7 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -44,7 +44,7 @@ public class DragView extends View { private Bitmap mBitmap; private Bitmap mCrossFadeBitmap; - private Paint mPaint; + @Thunk Paint mPaint; private int mRegistrationX; private int mRegistrationY; @@ -62,7 +62,7 @@ public class DragView extends View { // size. This is ignored for non-icons. private float mIntrinsicIconScale = 1f; - private float[] mCurrentFilter; + @Thunk float[] mCurrentFilter; private ValueAnimator mFilterAnimator; /** diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 46e4902f9..70bb01af0 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -426,7 +426,7 @@ public class FocusHelper { /** * Private helper method to get the CellLayoutChildren given a CellLayout index. */ - private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( + @Thunk static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( ViewGroup container, int i) { CellLayout parent = (CellLayout) container.getChildAt(i); return parent.getShortcutsAndWidgets(); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 6dfca9ef3..a16067d16 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -69,7 +69,7 @@ public class IconCache { private static final int LOW_RES_SCALE_FACTOR = 8; - private static final Object ICON_UPDATE_TOKEN = new Object(); + @Thunk static final Object ICON_UPDATE_TOKEN = new Object(); @Thunk static class CacheEntry { public Bitmap icon; @@ -79,18 +79,18 @@ public class IconCache { } private final HashMap mDefaultIcons = new HashMap<>(); - private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + @Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); private final Context mContext; private final PackageManager mPackageManager; - private final UserManagerCompat mUserManager; + @Thunk final UserManagerCompat mUserManager; private final LauncherAppsCompat mLauncherApps; private final HashMap mCache = new HashMap(INITIAL_ICON_CACHE_CAPACITY); private final int mIconDpi; - private final IconDB mIconDb; + @Thunk final IconDB mIconDb; - private final Handler mWorkerHandler; + @Thunk final Handler mWorkerHandler; public IconCache(Context context, InvariantDeviceProfile inv) { ActivityManager activityManager = @@ -320,7 +320,7 @@ public class IconCache { } } - private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, + @Thunk void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, long userSerial) { // Reuse the existing entry if it already exists in the DB. This ensures that we do not // create bitmap if it was already created during loader. @@ -342,7 +342,7 @@ public class IconCache { SQLiteDatabase.CONFLICT_REPLACE); } - private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app, + @Thunk ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app, boolean replaceExisting) { final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser()); CacheEntry entry = null; @@ -688,14 +688,14 @@ public class IconCache { * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the * worker thread doesn't get blocked. */ - private class SerializedIconUpdateTask implements Runnable { + @Thunk class SerializedIconUpdateTask implements Runnable { private final long mUserSerial; private final HashMap mPkgInfoMap; private final Stack mAppsToAdd; private final Stack mAppsToUpdate; private final HashSet mUpdatedPackages = new HashSet(); - private SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap, + @Thunk SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap, Stack appsToAdd, Stack appsToUpdate) { mUserSerial = userSerial; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ef34660df..2ff6adc99 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -265,7 +265,7 @@ public class Launcher extends Activity private int[] mTmpAddItemCellCoordinates = new int[2]; - private Hotseat mHotseat; + @Thunk Hotseat mHotseat; private ViewGroup mOverviewPanel; private View mAllAppsButton; @@ -402,7 +402,7 @@ public class Launcher extends Activity FocusIndicatorView mFocusHandler; - private boolean mRotationEnabled = false; + @Thunk boolean mRotationEnabled = false; private boolean mPreferenceObserverRegistered = false; final private SharedPreferences.OnSharedPreferenceChangeListener mSettingsObserver = diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index af4101221..b40ace3fb 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -51,13 +51,13 @@ import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.Thunk; import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import com.google.protobuf.nano.MessageNano; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -135,7 +135,7 @@ public class LauncherBackupHelper implements BackupHelper { private static final int SCREEN_RANK_INDEX = 2; - private final Context mContext; + @Thunk final Context mContext; private final HashSet mExistingKeys; private final ArrayList mKeys; private final ItemTypeMatcher[] mItemTypeMatchers; @@ -1157,15 +1157,15 @@ public class LauncherBackupHelper implements BackupHelper { .getSerialNumberForUser(UserHandleCompat.myUserHandle()); } - private class InvalidBackupException extends IOException { + @Thunk class InvalidBackupException extends IOException { private static final long serialVersionUID = 8931456637211665082L; - private InvalidBackupException(Throwable cause) { + @Thunk InvalidBackupException(Throwable cause) { super(cause); } - public InvalidBackupException(String reason) { + @Thunk InvalidBackupException(String reason) { super(reason); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 224ebbf89..776c2bd63 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -275,7 +275,7 @@ public class LauncherModel extends BroadcastReceiver /** * Runs the specified runnable after the loader is complete */ - private void runAfterBindCompletes(Runnable r) { + @Thunk void runAfterBindCompletes(Runnable r) { if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) { synchronized (mBindCompleteRunnables) { mBindCompleteRunnables.add(r); @@ -3350,7 +3350,7 @@ public class LauncherModel extends BroadcastReceiver * * @see #loadAndBindWidgetsAndShortcuts */ - private WidgetsModel createWidgetsModel(Context context, boolean refresh) { + @Thunk WidgetsModel createWidgetsModel(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 27511527d..45070d190 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -88,7 +88,7 @@ public class LauncherProvider extends ContentProvider { static final Uri CONTENT_APPWIDGET_RESET_URI = Uri.parse("content://" + AUTHORITY + "/appWidgetReset"); - private DatabaseHelper mOpenHelper; + @Thunk DatabaseHelper mOpenHelper; @Override public boolean onCreate() { @@ -665,7 +665,7 @@ public class LauncherProvider extends ContentProvider { * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}. */ - private void convertShortcutsToLauncherActivities(SQLiteDatabase db) { + @Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) { db.beginTransaction(); Cursor c = null; SQLiteStatement updateStmt = null; diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 9271e8b15..18832c680 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -22,11 +22,13 @@ import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -45,9 +47,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; - import com.android.launcher3.util.Thunk; - import java.util.ArrayList; /** @@ -186,7 +186,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // We use the min scale to determine how much to expand the actually PagedView measured // dimensions such that when we are zoomed out, the view is not clipped private static int REORDERING_DROP_REPOSITION_DURATION = 200; - private static int REORDERING_REORDER_REPOSITION_DURATION = 300; + @Thunk static int REORDERING_REORDER_REPOSITION_DURATION = 300; private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; private float mMinScale = 1f; @@ -956,7 +956,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return 0; } - private void updateMaxScrollX() { + @Thunk void updateMaxScrollX() { int childCount = getChildCount(); if (childCount > 0) { final int index = mIsRtl ? 0 : childCount - 1; @@ -2322,6 +2322,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private static final int ANIM_TAG_KEY = 100; /* Accessibility */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index e8cc48685..a62177142 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -56,7 +56,7 @@ public class WidgetPreviewLoader { * Weak reference objects, do not prevent their referents from being made finalizable, * finalized, and then reclaimed. */ - private Set mUnusedBitmaps = + @Thunk Set mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap()); private final Context mContext; @@ -67,7 +67,7 @@ public class WidgetPreviewLoader { private final InvariantDeviceProfile mDeviceProfile; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - private final Handler mWorkerHandler; + @Thunk final Handler mWorkerHandler; public WidgetPreviewLoader(Context context, InvariantDeviceProfile inv, IconCache iconCache) { mContext = context; @@ -290,7 +290,7 @@ public class WidgetPreviewLoader { /** * Reads the preview bitmap from the DB or null if the preview is not in the DB. */ - private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) { + @Thunk Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) { Cursor cursor = null; try { cursor = mDb.getReadableDatabase().query( @@ -329,7 +329,7 @@ public class WidgetPreviewLoader { return null; } - private Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle, + @Thunk Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle, int previewWidth, int previewHeight) { if (info instanceof LauncherAppWidgetProviderInfo) { return generateWidgetPreview(launcher, (LauncherAppWidgetProviderInfo) info, @@ -512,7 +512,7 @@ public class WidgetPreviewLoader { /** * @return an array of containing versionCode and lastUpdatedTime for the package. */ - private long[] getPackageVersion(String packageName) { + @Thunk long[] getPackageVersion(String packageName) { synchronized (mPackageVersions) { long[] versions = mPackageVersions.get(packageName); if (versions == null) { @@ -561,14 +561,13 @@ public class WidgetPreviewLoader { } public class PreviewLoadTask extends AsyncTask { - - private final WidgetCacheKey mKey; + @Thunk final WidgetCacheKey mKey; private final Object mInfo; private final int mPreviewHeight; private final int mPreviewWidth; private final WidgetCell mCaller; - private long[] mVersions; - private Bitmap mBitmapToRecycle; + @Thunk long[] mVersions; + @Thunk Bitmap mBitmapToRecycle; PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, int previewHeight, WidgetCell caller) { @@ -674,7 +673,7 @@ public class WidgetPreviewLoader { private static final class WidgetCacheKey extends ComponentKey { // TODO: remove dependency on size - private final String size; + @Thunk final String size; public WidgetCacheKey(ComponentName componentName, UserHandleCompat user, String size) { super(componentName, user); diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 93cf8d050..3c49ccc41 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -251,7 +251,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { return actions; } - private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { + @Thunk void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams(); CellLayout layout = (CellLayout) host.getParent().getParent(); layout.markCellsAsUnoccupiedForView(host); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index c05f7c0b9..84a6462e5 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -191,14 +191,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.ItemDecoration mItemDecoration; - private FrameLayout mContentView; + @Thunk FrameLayout mContentView; @Thunk AllAppsRecyclerView mAppsRecyclerView; - private ViewGroup mPredictionBarView; + @Thunk ViewGroup mPredictionBarView; private View mHeaderView; - private View mSearchBarContainerView; + @Thunk View mSearchBarContainerView; private View mSearchButtonView; private View mDismissSearchButtonView; - private AllAppsSearchEditView mSearchBarEditView; + @Thunk AllAppsSearchEditView mSearchBarEditView; private HeaderElevationController mElevationController; @@ -215,7 +215,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private int mContainerInset; private int mPredictionBarHeight; private int mLastRecyclerViewScrollPos = -1; - private boolean mFocusPredictionBarOnFirstBind; + @Thunk boolean mFocusPredictionBarOnFirstBind; private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; @@ -936,7 +936,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc /** * Hides the search field. */ - private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + @Thunk void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { mSearchManager.cancel(true); final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; @@ -1001,7 +1001,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc /** * Returns an input method manager. */ - private InputMethodManager getInputMethodManager() { + @Thunk InputMethodManager getInputMethodManager() { return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index e010270ce..307d9403d 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -288,7 +288,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter extends LongSparseArray implements Iterable { return new ValueIterator(); } - private class ValueIterator implements Iterator { + @Thunk class ValueIterator implements Iterator { private int mNextIndex = 0; diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index d65455053..887587905 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -15,6 +15,7 @@ import com.android.launcher3.DragLayer; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.util.Thunk; public class WidgetHostViewLoader { @@ -38,7 +39,7 @@ public class WidgetHostViewLoader { PendingAddWidgetInfo mCreateWidgetInfo = null; // TODO: technically, this class should not have to know the existence of the launcher. - private Launcher mLauncher; + @Thunk Launcher mLauncher; private Handler mHandler; public WidgetHostViewLoader(Launcher launcher) { @@ -188,7 +189,7 @@ public class WidgetHostViewLoader { return options; } - private void setState(int state) { + @Thunk void setState(int state) { if (DEBUG) { Log.d(TAG, String.format(" state [%d -> %d]", mState, state)); } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 11c2107f2..8d04be5e3 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -46,6 +46,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.Workspace; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.util.Thunk; /** * The widgets list view container. @@ -60,7 +61,7 @@ public class WidgetsContainerView extends BaseContainerView private static final int PRELOAD_SCREEN_HEIGHT_MULTIPLE = 1; /* Global instances that are used inside this container. */ - private Launcher mLauncher; + @Thunk Launcher mLauncher; private DragController mDragController; private IconCache mIconCache; -- cgit v1.2.3 From 59caa60222e55212c13110ca0890023b47356fa5 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 2 Jun 2015 10:29:30 -0700 Subject: Remove use of API level 19 classes. Bug: 21583308 --- src/com/android/launcher3/allapps/AlphabeticalAppsList.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 725616fb3..aa5e907e1 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -29,8 +29,8 @@ import com.android.launcher3.model.AbstractUserComparator; import com.android.launcher3.model.AppNameComparator; import com.android.launcher3.util.Thunk; +import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -178,7 +178,7 @@ public class AlphabeticalAppsList { mMinAppsPerRow = minAppsPerRow; mMinRowsInMergedSection = minRowsInMergedSection; mMaxAllowableMerges = maxNumMerges; - mAsciiEncoder = StandardCharsets.US_ASCII.newEncoder(); + mAsciiEncoder = Charset.forName("US-ASCII").newEncoder(); } @Override -- cgit v1.2.3 From 770f7c590753219d38f957278488b5b3f05e70ec Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Tue, 2 Jun 2015 15:02:14 -0700 Subject: Stylus support: handling drag n drop for widgets This alters the widget host view code to consider stylus button presses allowing the user to pick up widgets with a stylus button press, and drop them by releasing the button. Bug: 20430722 Change-Id: I359f72bd80016f4b313aab1325cc92d7c6fd2a8c --- src/com/android/launcher3/LauncherAppWidgetHostView.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 71fb2d295..cf461a5b8 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -36,6 +36,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc LayoutInflater mInflater; private CheckLongPressHelper mLongPressHelper; + private StylusEventHelper mStylusEventHelper; private Context mContext; private int mPreviousOrientation; private DragLayer mDragLayer; @@ -46,6 +47,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc super(context); mContext = context; mLongPressHelper = new CheckLongPressHelper(this); + mStylusEventHelper = new StylusEventHelper(this); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mDragLayer = ((Launcher) context).getDragLayer(); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); @@ -89,11 +91,17 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc return true; } - // Watch for longpress events at this level to make sure - // users can always pick up this widget + // Watch for longpress or stylus button press events at this level to + // make sure users can always pick up this widget + if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + mLongPressHelper.cancelLongPress(); + return true; + } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { - mLongPressHelper.postCheckForLongPress(); + if (!mStylusEventHelper.inStylusButtonPressed()) { + mLongPressHelper.postCheckForLongPress(); + } mDragLayer.setTouchCompleteListener(this); break; } -- cgit v1.2.3 From 5b9ebcab3c6c55dcb7575fdbcb77020a5ad274d4 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 Jun 2015 18:37:32 -0700 Subject: Fixing preloaded widget not getting used for animation > The preloaded widget was being set in a different instance of PendingAddWidgetInfo and was never getting used for animation. bug: 20699153 Change-Id: Iaec13640e49c66993b4695e4a52dc3a3a2133fb2 --- src/com/android/launcher3/Launcher.java | 3 + .../launcher3/widget/PendingAddWidgetInfo.java | 19 --- .../launcher3/widget/WidgetHostViewLoader.java | 157 ++++++++------------- .../launcher3/widget/WidgetsContainerView.java | 16 +-- 4 files changed, 67 insertions(+), 128 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2ff6adc99..975672764 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2370,6 +2370,9 @@ public class Launcher extends Activity if (hostView != null) { appWidgetId = hostView.getAppWidgetId(); addAppWidgetImpl(appWidgetId, info, hostView, info.info); + + // Clear the boundWidget so that it doesn't get destroyed. + info.boundWidget = null; } else { // In this case, we either need to start an activity to get permission to bind // the widget, or we need to start an activity to configure the widget, or both. diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index 36cc2b111..88a6ca420 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -65,25 +65,6 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } - // Copy constructor - public PendingAddWidgetInfo(PendingAddWidgetInfo copy) { - minWidth = copy.minWidth; - minHeight = copy.minHeight; - minResizeWidth = copy.minResizeWidth; - minResizeHeight = copy.minResizeHeight; - previewImage = copy.previewImage; - icon = copy.icon; - info = copy.info; - boundWidget = copy.boundWidget; - componentName = copy.componentName; - itemType = copy.itemType; - spanX = copy.spanX; - spanY = copy.spanY; - minSpanX = copy.minSpanX; - minSpanY = copy.minSpanY; - bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone(); - } - @Override public String toString() { return String.format("PendingAddWidgetInfo package=%s, name=%s", diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 887587905..30b3d581a 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -7,148 +7,111 @@ import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.util.Log; import android.view.View; import com.android.launcher3.AppWidgetResizeFrame; +import com.android.launcher3.DragController.DragListener; import com.android.launcher3.DragLayer; +import com.android.launcher3.DragSource; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.util.Thunk; -public class WidgetHostViewLoader { - - private static final boolean DEBUG = false; - private static final String TAG = "WidgetHostViewLoader"; - - /* constants used for widget loading state. */ - private static final int WIDGET_NO_CLEANUP_REQUIRED = -1; - private static final int WIDGET_PRELOAD_PENDING = 0; - private static final int WIDGET_BOUND = 1; - private static final int WIDGET_INFLATED = 2; - - int mState = WIDGET_NO_CLEANUP_REQUIRED; +public class WidgetHostViewLoader implements DragListener { /* Runnables to handle inflation and binding. */ - private Runnable mInflateWidgetRunnable = null; + @Thunk Runnable mInflateWidgetRunnable = null; private Runnable mBindWidgetRunnable = null; - /* Id of the widget being handled. */ - int mWidgetLoadingId = -1; - PendingAddWidgetInfo mCreateWidgetInfo = null; - // TODO: technically, this class should not have to know the existence of the launcher. @Thunk Launcher mLauncher; - private Handler mHandler; + @Thunk Handler mHandler; + @Thunk final View mView; + @Thunk final PendingAddWidgetInfo mInfo; + + // Widget id generated for binding a widget host view or -1 for invalid id. The id is + // not is use as long as it is stored here and can be deleted safely. Once its used, this value + // to be set back to -1. + @Thunk int mWidgetLoadingId = -1; - public WidgetHostViewLoader(Launcher launcher) { + public WidgetHostViewLoader(Launcher launcher, View view) { mLauncher = launcher; mHandler = new Handler(); + mView = view; + mInfo = (PendingAddWidgetInfo) view.getTag(); } - /** - * Start loading the widget. - */ - public void load(View v) { - if (mCreateWidgetInfo != null) { - // Just in case the cleanup process wasn't properly executed. - finish(false); - } - boolean status = false; - if (v.getTag() instanceof PendingAddWidgetInfo) { - mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag()); - status = preloadWidget(v, mCreateWidgetInfo); - } - if (DEBUG) { - Log.d(TAG, String.format("load started on [state=%d, status=%s]", mState, status)); - } - } - + @Override + public void onDragStart(DragSource source, Object info, int dragAction) { } - /** - * Clean up according to what the last known state was. - * @param widgetIdUsed {@code true} if the widgetId was consumed which can happen only - * when view is fully inflated - */ - public void finish(boolean widgetIdUsed) { - if (DEBUG) { - Log.d(TAG, String.format("cancel on state [%d] widgetId=[%d]", - mState, mWidgetLoadingId)); - } + @Override + public void onDragEnd() { + // Cleanup up preloading state. + mLauncher.getDragController().removeDragListener(this); - // If the widget was not added, we may need to do further cleanup. - PendingAddWidgetInfo info = mCreateWidgetInfo; - mCreateWidgetInfo = null; - - if (mState == WIDGET_PRELOAD_PENDING) { - // We never did any preloading, so just remove pending callbacks to do so - mHandler.removeCallbacks(mBindWidgetRunnable); - mHandler.removeCallbacks(mInflateWidgetRunnable); - } else if (mState == WIDGET_BOUND) { - // Delete the widget id which was allocated - if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); - } + mHandler.removeCallbacks(mBindWidgetRunnable); + mHandler.removeCallbacks(mInflateWidgetRunnable); - // We never got around to inflating the widget, so remove the callback to do so. - mHandler.removeCallbacks(mInflateWidgetRunnable); - } else if (mState == WIDGET_INFLATED && !widgetIdUsed) { - // Delete the widget id which was allocated - if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); - } + // Cleanup widget id + if (mWidgetLoadingId != -1) { + mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); + mWidgetLoadingId = -1; + } - // The widget was inflated and added to the DragLayer -- remove it. - AppWidgetHostView widget = info.boundWidget; - mLauncher.getDragLayer().removeView(widget); + // The widget was inflated and added to the DragLayer -- remove it. + if (mInfo.boundWidget != null) { + mLauncher.getDragLayer().removeView(mInfo.boundWidget); + mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId()); + mInfo.boundWidget = null; } - setState(WIDGET_NO_CLEANUP_REQUIRED); - mWidgetLoadingId = -1; } - private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) { - final LauncherAppWidgetProviderInfo pInfo = info.info; + /** + * Start preloading the widget. + */ + public boolean preloadWidget() { + final LauncherAppWidgetProviderInfo pInfo = mInfo.info; - final Bundle options = pInfo.isCustomWidget ? null : - getDefaultOptionsForWidget(mLauncher, info); + if (pInfo.isCustomWidget) { + return false; + } + final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo); // If there is a configuration activity, do not follow thru bound and inflate. if (pInfo.configure != null) { - info.bindOptions = options; + mInfo.bindOptions = options; return false; } - setState(WIDGET_PRELOAD_PENDING); + mBindWidgetRunnable = new Runnable() { @Override public void run() { - if (pInfo.isCustomWidget) { - setState(WIDGET_BOUND); - return; - } - mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( mWidgetLoadingId, pInfo, options)) { - setState(WIDGET_BOUND); + + // Widget id bound. Inflate the widget. + mHandler.post(mInflateWidgetRunnable); } } }; - mHandler.post(mBindWidgetRunnable); mInflateWidgetRunnable = new Runnable() { @Override public void run() { - if (mState != WIDGET_BOUND) { + if (mWidgetLoadingId == -1) { return; } AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( (Context) mLauncher, mWidgetLoadingId, pInfo); - info.boundWidget = hostView; - setState(WIDGET_INFLATED); - hostView.setVisibility(View.INVISIBLE); - int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false); + mInfo.boundWidget = hostView; + + // We used up the widget Id in binding the above view. + mWidgetLoadingId = -1; + hostView.setVisibility(View.INVISIBLE); + int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, false); // We want the first widget layout to be the correct size. This will be important // for width size reporting to the AppWidgetManager. DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], @@ -157,10 +120,11 @@ public class WidgetHostViewLoader { lp.customPosition = true; hostView.setLayoutParams(lp); mLauncher.getDragLayer().addView(hostView); - v.setTag(info); + mView.setTag(mInfo); } }; - mHandler.post(mInflateWidgetRunnable); + + mHandler.post(mBindWidgetRunnable); return true; } @@ -188,11 +152,4 @@ public class WidgetHostViewLoader { } return options; } - - @Thunk void setState(int state) { - if (DEBUG) { - Log.d(TAG, String.format(" state [%d -> %d]", mState, state)); - } - mState = state; - } } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 8d04be5e3..aa139cb48 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -21,7 +21,6 @@ import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; import android.util.Log; @@ -74,7 +73,6 @@ public class WidgetsContainerView extends BaseContainerView /* Rendering related. */ private WidgetPreviewLoader mWidgetPreviewLoader; - private WidgetHostViewLoader mWidgetHostViewLoader; private Rect mPadding = new Rect(); @@ -90,7 +88,6 @@ public class WidgetsContainerView extends BaseContainerView super(context, attrs, defStyleAttr); mLauncher = (Launcher) context; mDragController = mLauncher.getDragController(); - mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher); mAdapter = new WidgetsListAdapter(context, this, this, mLauncher); mIconCache = (LauncherAppState.getInstance()).getIconCache(); @@ -170,8 +167,13 @@ public class WidgetsContainerView extends BaseContainerView if (!mLauncher.isDraggingEnabled()) return false; boolean status = beginDragging(v); - if (status) { - mWidgetHostViewLoader.load(v); + if (status && v.getTag() instanceof PendingAddWidgetInfo) { + WidgetHostViewLoader hostLoader = new WidgetHostViewLoader(mLauncher, v); + boolean preloadStatus = hostLoader.preloadWidget(); + if (DEBUG) { + Log.d(TAG, String.format("preloading widget [status=%s]", preloadStatus)); + } + mLauncher.getDragController().addDragListener(hostLoader); } return status; } @@ -325,10 +327,6 @@ public class WidgetsContainerView extends BaseContainerView } d.deferDragViewCleanupPostAnimation = false; } - //TODO(hyunyoungs): if drop fails, this call cleans up correctly. - // However, in rare corner case where drop succeeds but doesn't end up using the widget - // id created by the loader, this finish will leave dangling widget id. - mWidgetHostViewLoader.finish(success); } // -- cgit v1.2.3 From ef044dd380ac3abf354027750efdc16d5d48ac70 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Tue, 2 Jun 2015 15:35:07 -0700 Subject: Stylus support: creating and setting listeners for stylus button press This updates almost(*) all locations that use a long press listener to also set a custom touch listener that recognizes the stylus button press action. The stylus button press action is: when a stylus touches a view while the primary stylus button is pressed which may occur on a DOWN or MOVE event. *The location this is *not* enabled for is: Longpress to enter "overview" mode -- this isn't really a selection or drag n drop action; it is also easy to accidentally do this while using the stylus gesture to drag n drop items which is not an ideal interaction. Also not set for the "cling" that demonstrates this. Bug: 20430722 Change-Id: I9343f143261a7b4fada9afca28b8a11a60dbecca --- .../android/launcher3/WallpaperPickerActivity.java | 10 +++ src/com/android/launcher3/BubbleTextView.java | 13 +++- src/com/android/launcher3/CellLayout.java | 17 +++++ src/com/android/launcher3/FolderIcon.java | 8 +++ src/com/android/launcher3/StylusEventHelper.java | 84 ++++++++++++++++++++++ src/com/android/launcher3/widget/WidgetCell.java | 13 ++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/com/android/launcher3/StylusEventHelper.java diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 3f90203ed..96238717e 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -49,6 +49,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; @@ -891,6 +892,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { private void addLongPressHandler(View v) { v.setOnLongClickListener(mLongClickListener); + + // Enable stylus button to also trigger long click. + final StylusEventHelper stylusEventHelper = new StylusEventHelper(v); + v.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent event) { + return stylusEventHelper.checkAndPerformStylusEvent(event); + } + }); } private ArrayList findBundledWallpapers() { diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index edf502112..798eec8e7 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -63,6 +63,7 @@ public class BubbleTextView extends TextView { private final Drawable mBackground; private final CheckLongPressHelper mLongPressHelper; private final HolographicOutlineHelper mOutlineHelper; + private final StylusEventHelper mStylusEventHelper; private boolean mBackgroundSizeChanged; @@ -125,6 +126,7 @@ public class BubbleTextView extends TextView { } mLongPressHelper = new CheckLongPressHelper(this); + mStylusEventHelper = new StylusEventHelper(this); mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); if (mCustomShadowsEnabled) { @@ -236,6 +238,12 @@ public class BubbleTextView extends TextView { // isPressed() on an ACTION_UP boolean result = super.onTouchEvent(event); + // Check for a stylus button press, if it occurs cancel any long press checks. + if (mStylusEventHelper.checkAndPerformStylusEvent(event)) { + mLongPressHelper.cancelLongPress(); + result = true; + } + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // So that the pressed outline is visible immediately on setStayPressed(), @@ -245,7 +253,10 @@ public class BubbleTextView extends TextView { mPressedBackground = mOutlineHelper.createMediumDropShadow(this); } - mLongPressHelper.postCheckForLongPress(); + // If we're in a stylus button press, don't check for long press. + if (!mStylusEventHelper.inStylusButtonPressed()) { + mLongPressHelper.postCheckForLongPress(); + } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 61567ac00..a57c1fe98 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -95,6 +95,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { boolean[][] mTmpOccupied; private OnTouchListener mInterceptTouchListener; + private StylusEventHelper mStylusEventHelper; private ArrayList mFolderOuterRings = new ArrayList(); private int[] mFolderLeaveBehindCell = {-1, -1}; @@ -286,6 +287,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap, mCountX, mCountY); + mStylusEventHelper = new StylusEventHelper(this); + mTouchFeedbackView = new ClickShadowView(context); addView(mTouchFeedbackView); addView(mShortcutsAndWidgets); @@ -338,6 +341,20 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return false; } + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean handled = super.onTouchEvent(ev); + // Stylus button press on a home screen should not switch between overview mode and + // the home screen mode, however, once in overview mode stylus button press should be + // enabled to allow rearranging the different home screens. So check what mode + // the workspace is in, and only perform stylus button presses while in overview mode. + if (mLauncher.mWorkspace.isInOverviewMode() + && mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + return true; + } + return handled; + } + public void enableHardwareLayer(boolean hasLayer) { mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint); } diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 8652eef40..a81e6517e 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -57,6 +57,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Thunk static boolean sStaticValuesDirty = true; private CheckLongPressHelper mLongPressHelper; + private StylusEventHelper mStylusEventHelper; // The number of icons to display in the public static final int NUM_ITEMS_IN_PREVIEW = 3; @@ -128,6 +129,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void init() { mLongPressHelper = new CheckLongPressHelper(this); + mStylusEventHelper = new StylusEventHelper(this); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @@ -719,6 +721,12 @@ public class FolderIcon extends FrameLayout implements FolderListener { // isPressed() on an ACTION_UP boolean result = super.onTouchEvent(event); + // Check for a stylus button press, if it occurs cancel any long press checks. + if (mStylusEventHelper.checkAndPerformStylusEvent(event)) { + mLongPressHelper.cancelLongPress(); + return true; + } + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLongPressHelper.postCheckForLongPress(); diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java new file mode 100644 index 000000000..da46e6a54 --- /dev/null +++ b/src/com/android/launcher3/StylusEventHelper.java @@ -0,0 +1,84 @@ +package com.android.launcher3; + +import com.android.launcher3.Utilities; + +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +/** + * Helper for identifying when a stylus touches a view while the primary stylus button is pressed. + * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. On a + * stylus button press this performs the view's {@link View#performLongClick()} method, if the view + * is long clickable. + */ +public class StylusEventHelper { + private boolean mIsButtonPressed; + private View mView; + + public StylusEventHelper(View view) { + mView = view; + } + + /** + * Call this in onTouchEvent method of a view to identify a stylus button press and perform a + * long click (if the view is long clickable). + * + * @param event The event to check for a stylus button press. + * @return Whether a stylus event occurred and was handled. + */ + public boolean checkAndPerformStylusEvent(MotionEvent event) { + final float slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop(); + + if (!mView.isLongClickable()) { + // We don't do anything unless the view is long clickable. + return false; + } + + final boolean stylusButtonPressed = isStylusButtonPressed(event); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mIsButtonPressed = false; + if (stylusButtonPressed && mView.performLongClick()) { + mIsButtonPressed = true; + return true; + } + break; + case MotionEvent.ACTION_MOVE: + if (Utilities.pointInView(mView, event.getX(), event.getY(), slop)) { + if (!mIsButtonPressed && stylusButtonPressed && mView.performLongClick()) { + mIsButtonPressed = true; + return true; + } else if (mIsButtonPressed && !stylusButtonPressed) { + mIsButtonPressed = false; + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIsButtonPressed = false; + break; + } + return false; + } + + /** + * Whether a stylus button press is occurring. + */ + public boolean inStylusButtonPressed() { + return mIsButtonPressed; + } + + /** + * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button + * pressed. + * + * @param event The event to check. + * @return Whether a stylus button press occurred. + */ + public static boolean isStylusButtonPressed(MotionEvent event) { + return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS + && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY); + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 3ec164506..2714f5182 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.util.AttributeSet; import android.util.Log; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.widget.LinearLayout; @@ -35,6 +36,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; +import com.android.launcher3.StylusEventHelper; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; import com.android.launcher3.compat.AppWidgetManagerCompat; @@ -73,6 +75,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private WidgetPreviewLoader mWidgetPreviewLoader; private PreviewLoadRequest mActiveRequest; + private StylusEventHelper mStylusEventHelper; private Launcher mLauncher; @@ -89,6 +92,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { final Resources r = context.getResources(); mLauncher = (Launcher) context; + mStylusEventHelper = new StylusEventHelper(this); mDimensionsFormatString = r.getString(R.string.widget_dims_format); setContainerWidth(); @@ -202,6 +206,15 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return Math.min(size[0], info.spanX * cellWidth); } + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean handled = super.onTouchEvent(ev); + if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + return true; + } + return handled; + } + /** * Helper method to get the string info of the tag. */ -- cgit v1.2.3 From 8f1eff7b6cc8621888ee46605c32e601f80a890b Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 28 May 2015 17:33:40 -0700 Subject: Adding context for app launches. Bug: 21492784 Change-Id: I1b28ac8f44498e1d9770e770b074f19c721c3f10 --- src/com/android/launcher3/BubbleTextView.java | 2 - src/com/android/launcher3/Folder.java | 14 +++- src/com/android/launcher3/Hotseat.java | 10 ++- src/com/android/launcher3/Launcher.java | 14 +--- src/com/android/launcher3/LauncherCallbacks.java | 2 + src/com/android/launcher3/Stats.java | 80 ++++++++++++++++++++-- src/com/android/launcher3/Workspace.java | 10 ++- .../launcher3/allapps/AllAppsContainerView.java | 13 +++- .../launcher3/allapps/AllAppsRecyclerView.java | 18 ++++- .../launcher3/allapps/AlphabeticalAppsList.java | 10 +++ 10 files changed, 147 insertions(+), 26 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index edf502112..240f7c03f 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -28,7 +28,6 @@ import android.graphics.Region; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; -import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.view.KeyEvent; @@ -36,7 +35,6 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.ViewParent; import android.widget.TextView; - import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.model.PackageItemInfo; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index fcfb717ab..94f8fc875 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -29,6 +29,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; +import android.os.Bundle; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -66,7 +67,8 @@ import java.util.Collections; */ public class Folder extends LinearLayout implements DragSource, View.OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource { + View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource, + Stats.LaunchSourceProvider { private static final String TAG = "Launcher.Folder"; /** @@ -923,7 +925,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View v = list.get(i); ItemInfo info = (ItemInfo) v.getTag(); LauncherModel.addItemToDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY); + info.cellX, info.cellY); } } @@ -1338,6 +1340,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList outRect.right += mScrollAreaOffset; } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + // Fill in from the folder icon's launch source provider first + Stats.LaunchSourceUtils.populateSourceDataFromAncestorProvider(mFolderIcon, sourceData); + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, Stats.SUB_CONTAINER_FOLDER); + sourceData.putInt(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE, mContent.getCurrentPage()); + } + private class OnScrollHintListener implements OnAlarmListener { private final DragObject mDragObject; diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index ce33164fa..6f097449c 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -19,15 +19,16 @@ package com.android.launcher3; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.TextView; -public class Hotseat extends FrameLayout { +public class Hotseat extends FrameLayout + implements Stats.LaunchSourceProvider{ private CellLayout mContent; @@ -160,4 +161,9 @@ public class Hotseat extends FrameLayout { } return false; } + + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOTSEAT); + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2ff6adc99..867a6e71d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1531,7 +1531,6 @@ public class Launcher extends Activity * Add a shortcut to the workspace. * * @param data The intent describing the shortcut. - * @param cellInfo The position on screen where to create the shortcut. */ private void completeAddShortcut(Intent data, long container, long screenId, int cellX, int cellY) { @@ -2549,13 +2548,6 @@ public class Launcher extends Activity } } - public void onClickPagedViewIcon(View v) { - startAppShortcutOrInfoActivity(v); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onClickPagedViewIcon(v); - } - } - @SuppressLint("ClickableViewAccessibility") public boolean onTouch(View v, MotionEvent event) { return false; @@ -2714,7 +2706,7 @@ public class Launcher extends Activity } boolean success = startActivitySafely(v, intent, tag); - mStats.recordLaunch(intent, shortcut); + mStats.recordLaunch(v, intent, shortcut); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; @@ -2936,7 +2928,7 @@ public class Launcher extends Activity } } - boolean startActivity(View v, Intent intent, Object tag) { + private boolean startActivity(View v, Intent intent, Object tag) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { // Only launch using the new animation if the shortcut has not opted out (this is a @@ -3007,7 +2999,7 @@ public class Launcher extends Activity return false; } - boolean startActivitySafely(View v, Intent intent, Object tag) { + private boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index a5f36ba93..70e400bca 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -56,6 +56,8 @@ public interface LauncherCallbacks { public void bindAllApplications(ArrayList apps); public void onClickFolderIcon(View v); public void onClickAppShortcut(View v); + + @Deprecated public void onClickPagedViewIcon(View v); public void onClickWallpaperPicker(View v); public void onClickSettingsButton(View v); diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index 9d06f755f..cb0e252b2 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -20,9 +20,63 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Bundle; import android.util.Log; +import android.view.View; +import android.view.ViewParent; public class Stats { + + /** + * Implemented by containers to provide a launch source for a given child. + */ + public interface LaunchSourceProvider { + void fillInLaunchSourceData(Bundle sourceData); + } + + /** + * Helpers to add the source to a launch intent. + */ + public static class LaunchSourceUtils { + /** + * Create a default bundle for LaunchSourceProviders to fill in their data. + */ + public static Bundle createSourceData() { + Bundle sourceData = new Bundle(); + sourceData.putString(SOURCE_EXTRA_CONTAINER, CONTAINER_HOMESCREEN); + // Have default container/sub container pages + sourceData.putInt(SOURCE_EXTRA_CONTAINER_PAGE, 0); + sourceData.putInt(SOURCE_EXTRA_SUB_CONTAINER_PAGE, 0); + return sourceData; + } + + /** + * Finds the next launch source provider in the parents of the view hierarchy and populates + * the source data from that provider. + */ + public static void populateSourceDataFromAncestorProvider(View v, Bundle sourceData) { + if (v == null) { + return; + } + + Stats.LaunchSourceProvider provider = null; + ViewParent parent = v.getParent(); + while (parent != null && parent instanceof View) { + if (parent instanceof Stats.LaunchSourceProvider) { + provider = (Stats.LaunchSourceProvider) parent; + break; + } + parent = parent.getParent(); + } + + if (provider != null) { + provider.fillInLaunchSourceData(sourceData); + } else if (LauncherAppState.isDogfoodBuild()) { + throw new RuntimeException("Expected LaunchSourceProvider"); + } + } + } + private static final boolean DEBUG_BROADCASTS = false; public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; @@ -31,6 +85,22 @@ public class Stats { public static final String EXTRA_SCREEN = "screen"; public static final String EXTRA_CELLX = "cellX"; public static final String EXTRA_CELLY = "cellY"; + public static final String EXTRA_SOURCE = "source"; + + public static final String SOURCE_EXTRA_CONTAINER = "container"; + public static final String SOURCE_EXTRA_CONTAINER_PAGE = "container_page"; + public static final String SOURCE_EXTRA_SUB_CONTAINER = "sub_container"; + public static final String SOURCE_EXTRA_SUB_CONTAINER_PAGE = "sub_container_page"; + + public static final String CONTAINER_SEARCH_BOX = "search_box"; + public static final String CONTAINER_ALL_APPS = "all_apps"; + public static final String CONTAINER_HOMESCREEN = "homescreen"; // aka. Workspace + public static final String CONTAINER_HOTSEAT = "hotseat"; + + public static final String SUB_CONTAINER_FOLDER = "folder"; + public static final String SUB_CONTAINER_ALL_APPS_A_Z = "a-z"; + public static final String SUB_CONTAINER_ALL_APPS_PREDICTION = "prediction"; + public static final String SUB_CONTAINER_ALL_APPS_SEARCH = "search"; private final Launcher mLauncher; private final String mLaunchBroadcastPermission; @@ -56,11 +126,7 @@ public class Stats { } } - public void recordLaunch(Intent intent) { - recordLaunch(intent, null); - } - - public void recordLaunch(Intent intent, ShortcutInfo shortcut) { + public void recordLaunch(View v, Intent intent, ShortcutInfo shortcut) { intent = new Intent(intent); intent.setSourceBounds(null); @@ -72,6 +138,10 @@ public class Stats { .putExtra(EXTRA_CELLX, shortcut.cellX) .putExtra(EXTRA_CELLY, shortcut.cellY); } + + Bundle sourceExtras = LaunchSourceUtils.createSourceData(); + LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras); + broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras); mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6d5affb59..193a0af6f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -28,6 +28,7 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; @@ -42,6 +43,7 @@ import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; @@ -86,7 +88,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class Workspace extends PagedView implements DropTarget, DragSource, DragScroller, View.OnTouchListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, - Insettable, UninstallSource, AccessibilityDragSource { + Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider { private static final String TAG = "Launcher.Workspace"; private static boolean ENFORCE_DRAG_EVENT_ORDER = false; @@ -4461,6 +4463,12 @@ public class Workspace extends PagedView mLauncher.getDragLayer().getLocationInDragLayer(this, loc); } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOMESCREEN); + sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage()); + } + /** * Used as a workaround to ensure that the AppWidgetService receives the * PACKAGE_ADDED broadcast before updating widgets. diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 84a6462e5..9386500be 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; +import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; @@ -57,6 +58,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherTransitionable; import com.android.launcher3.R; +import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.AppSearchManager.AppSearchResultCallback; @@ -172,7 +174,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, - ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback { + ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback, Stats.LaunchSourceProvider { public static final boolean GRID_MERGE_SECTIONS = true; @@ -870,6 +872,15 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc return false; } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + // Since the other cases are caught by the AllAppsRecyclerView LaunchSourceProvider, we just + // handle the prediction bar icons here + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, + Stats.SUB_CONTAINER_ALL_APPS_PREDICTION); + } + /** * Returns the predicted app in the prediction bar given a set of local coordinates. */ diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index cc5add3b2..e1b5d918e 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -18,14 +18,15 @@ package com.android.launcher3.allapps; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; - import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import java.util.List; @@ -33,7 +34,8 @@ import java.util.List; /** * A RecyclerView with custom fast scroll support for the all apps view. */ -public class AllAppsRecyclerView extends BaseRecyclerView { +public class AllAppsRecyclerView extends BaseRecyclerView + implements Stats.LaunchSourceProvider { private AlphabeticalAppsList mApps; private int mNumAppsPerRow; @@ -125,6 +127,18 @@ public class AllAppsRecyclerView extends BaseRecyclerView { addOnItemTouchListener(this); } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); + if (mApps.hasFilter()) { + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, + Stats.SUB_CONTAINER_ALL_APPS_SEARCH); + } else { + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, + Stats.SUB_CONTAINER_ALL_APPS_A_Z); + } + } + /** * Maps the touch (from 0..1) to the adapter position that should be visible. */ diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 725616fb3..f4d02a1c9 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -46,6 +46,7 @@ public class AlphabeticalAppsList { public static final String TAG = "AlphabeticalAppsList"; private static final boolean DEBUG = false; + private static final boolean DEBUG_PREDICTIONS = false; /** * Info about a section in the alphabetic list @@ -477,6 +478,15 @@ public class AlphabeticalAppsList { mAdapterItems.clear(); mSections.clear(); + if (DEBUG_PREDICTIONS) { + if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) { + mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(mApps.get(0).componentName); + } + } + // Process the predicted app components mPredictedApps.clear(); if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { -- cgit v1.2.3 From f8c6f885f032dad4e6b09e02ca97545783bccb70 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 3 Jun 2015 11:27:26 -0700 Subject: Fixing issue with hotseat being on the wrong side of the screen in RTL. Bug: 21499466 Change-Id: I566cd89dfb855f89f4e68bf56e48bf99204b11ab --- res/layout-land/launcher.xml | 2 +- src/com/android/launcher3/DeviceProfile.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index bf9296f7d..1c917bf09 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -45,7 +45,7 @@ android:id="@+id/hotseat" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_gravity="end" /> + android:layout_gravity="right" /> Date: Fri, 22 May 2015 12:25:45 -0700 Subject: Fixing different bitmap sizes in different orientations > Using a single bitmap icon size > Picking up appropriate density for satisfy the required icon size > Fixing some icon size assumptions during icon animations Bug: 19514688 Change-Id: Ia7a1d0d2c03a9d39ccc241fa4ae3eb8a0f374585 --- res/values-sw600dp/dimens.xml | 2 - res/values-sw720dp/dimens.xml | 2 - res/values/dimens.xml | 2 - src/com/android/launcher3/BubbleTextView.java | 11 ++-- src/com/android/launcher3/FolderIcon.java | 7 ++- src/com/android/launcher3/Hotseat.java | 2 +- src/com/android/launcher3/IconCache.java | 12 +--- .../android/launcher3/InvariantDeviceProfile.java | 29 +++++++++ src/com/android/launcher3/Launcher.java | 20 +++++- .../launcher3/LauncherAppWidgetProviderInfo.java | 3 +- src/com/android/launcher3/LauncherModel.java | 9 +-- .../launcher3/PendingAppWidgetHostView.java | 4 +- src/com/android/launcher3/Utilities.java | 72 +++++++--------------- .../launcher3/widget/WidgetsContainerView.java | 1 + 14 files changed, 90 insertions(+), 86 deletions(-) diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 994c1925f..daa98ef0b 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -15,8 +15,6 @@ --> - 64dp - 18dp 0dp diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index 89942f741..9d1e3529c 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -15,8 +15,6 @@ --> - 72dp - 54dp 16dp diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 7950862db..4fa997c80 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -15,8 +15,6 @@ --> - 48dp - 6dp 500dp diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 314c21f64..f4e306af3 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -57,6 +57,7 @@ public class BubbleTextView extends TextView { private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_ALL_APPS = 1; + private final Launcher mLauncher; private Drawable mIcon; private final Drawable mBackground; private final CheckLongPressHelper mLongPressHelper; @@ -91,8 +92,8 @@ public class BubbleTextView extends TextView { public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = ((Launcher) context).getDeviceProfile(); + mLauncher = (Launcher) context; + DeviceProfile grid = mLauncher.getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); @@ -142,7 +143,7 @@ public class BubbleTextView extends TextView { boolean promiseStateChanged) { Bitmap b = info.getIcon(iconCache); - FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); + FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b); iconDrawable.setGhostModeEnabled(info.isDisabled != 0); setIcon(iconDrawable, mIconSize); @@ -158,7 +159,7 @@ public class BubbleTextView extends TextView { } public void applyFromApplicationInfo(AppInfo info) { - setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize); + setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -171,7 +172,7 @@ public class BubbleTextView extends TextView { } public void applyFromPackageItemInfo(PackageItemInfo info) { - setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize); + setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index a81e6517e..f1e9dc83b 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -645,9 +645,10 @@ public class FolderIcon extends FrameLayout implements FolderListener { final Runnable onCompleteRunnable) { final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null); - final float scale0 = 1.0f; - final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2; - final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2 + getPaddingTop(); + float iconSize = mLauncher.getDeviceProfile().iconSizePx; + final float scale0 = iconSize / d.getIntrinsicWidth() ; + final float transX0 = (mAvailableSpaceInPreview - iconSize) / 2; + final float transY0 = (mAvailableSpaceInPreview - iconSize) / 2 + getPaddingTop(); mAnimParams.drawable = d; ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 6f097449c..6e33d1014 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -131,7 +131,7 @@ public class Hotseat extends FrameLayout inflater.inflate(R.layout.all_apps_button, mContent, false); Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon); - Utilities.resizeIconDrawable(d); + mLauncher.resizeIconDrawable(d); allAppsButton.setCompoundDrawables(null, d, null, null); allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label)); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index a16067d16..fe25bb9a0 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; @@ -93,14 +92,11 @@ public class IconCache { @Thunk final Handler mWorkerHandler; public IconCache(Context context, InvariantDeviceProfile inv) { - ActivityManager activityManager = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - mContext = context; mPackageManager = context.getPackageManager(); mUserManager = UserManagerCompat.getInstance(mContext); mLauncherApps = LauncherAppsCompat.getInstance(mContext); - mIconDpi = activityManager.getLauncherLargeIconDensity(); + mIconDpi = inv.fillResIconDpi; mIconDb = new IconDB(context); mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); @@ -136,10 +132,6 @@ public class IconCache { return getFullResDefaultActivityIcon(); } - public int getFullResIconDpi() { - return mIconDpi; - } - public Drawable getFullResIcon(ActivityInfo info) { Resources resources; try { @@ -744,7 +736,7 @@ public class IconCache { } private static final class IconDB extends SQLiteOpenHelper { - private final static int DB_VERSION = 4; + private final static int DB_VERSION = 5; private final static String TABLE_NAME = "icons"; private final static String COLUMN_ROWID = "rowid"; diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 7f34593a9..fcaf834a2 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -35,6 +35,8 @@ public class InvariantDeviceProfile { // This is a static that we use for the default icon size on a 4/5-inch phone private static float DEFAULT_ICON_SIZE_DP = 60; + private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; + // Constants that affects the interpolation curve between statically defined device profile // buckets. private static float KNEARESTNEIGHBOR = 3; @@ -60,6 +62,8 @@ public class InvariantDeviceProfile { public int numFolderRows; public int numFolderColumns; float iconSize; + int iconBitmapSize; + int fillResIconDpi; float iconTextSize; /** @@ -135,8 +139,10 @@ public class InvariantDeviceProfile { numFolderColumns = closestProfile.numFolderColumns; iconSize = interpolatedDeviceProfileOut.iconSize; + iconBitmapSize = Utilities.pxFromDp(iconSize, dm); iconTextSize = interpolatedDeviceProfileOut.iconTextSize; hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize; + fillResIconDpi = getLauncherIconDensity(iconBitmapSize); // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize @@ -187,6 +193,29 @@ public class InvariantDeviceProfile { return predefinedDeviceProfiles; } + private int getLauncherIconDensity(int requiredSize) { + // Densities typically defined by an app. + int[] densityBuckets = new int[] { + DisplayMetrics.DENSITY_LOW, + DisplayMetrics.DENSITY_MEDIUM, + DisplayMetrics.DENSITY_TV, + DisplayMetrics.DENSITY_HIGH, + DisplayMetrics.DENSITY_XHIGH, + DisplayMetrics.DENSITY_XXHIGH, + DisplayMetrics.DENSITY_XXXHIGH + }; + + int density = DisplayMetrics.DENSITY_XXXHIGH; + for (int i = densityBuckets.length - 1; i >= 0; i--) { + float expectedSize = ICON_SIZE_DEFINED_IN_APP_DP * densityBuckets[i] + / DisplayMetrics.DENSITY_DEFAULT; + if (expectedSize >= requiredSize) { + density = densityBuckets[i]; + } + } + + return density; + } /** * Apply any Partner customization grid overrides. diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 867a6e71d..dc63a761b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -453,9 +453,6 @@ public class Launcher extends Activity app.getInvariantDeviceProfile().landscapeProfile : app.getInvariantDeviceProfile().portraitProfile; - // TODO: Move this to icon cache. - Utilities.setIconSize(mDeviceProfile.iconSizePx); - // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); @@ -4673,6 +4670,23 @@ public class Launcher extends Activity } } + /** + * Returns a FastBitmapDrawable with the icon, accurately sized. + */ + public FastBitmapDrawable createIconDrawable(Bitmap icon) { + FastBitmapDrawable d = new FastBitmapDrawable(icon); + d.setFilterBitmap(true); + resizeIconDrawable(d); + return d; + } + + /** + * Resizes an icon drawable to the correct icon size. + */ + public void resizeIconDrawable(Drawable icon) { + icon.setBounds(0, 0, mDeviceProfile.iconSizePx, mDeviceProfile.iconSizePx); + } + /** * Prints out out state for debugging. */ diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index 7ca4fe325..85af92f30 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -67,7 +67,8 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { if (isCustomWidget) { return cache.getFullResIcon(provider.getPackageName(), icon); } - return super.loadIcon(context, cache.getFullResIconDpi()); + return super.loadIcon(context, + LauncherAppState.getInstance().getInvariantDeviceProfile().fillResIconDpi); } public String toString(PackageManager pm) { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 776c2bd63..4974dafab 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3114,8 +3114,9 @@ public class LauncherModel extends BroadcastReceiver // Update shortcuts which use iconResource. if ((si.iconResource != null) && packageSet.contains(si.iconResource.packageName)) { - Bitmap icon = Utilities.createIconBitmap(si.iconResource.packageName, - si.iconResource.resourceName, mIconCache, context); + Bitmap icon = Utilities.createIconBitmap( + si.iconResource.packageName, + si.iconResource.resourceName, context); if (icon != null) { si.setIcon(icon); si.usingFallbackIcon = false; @@ -3558,7 +3559,7 @@ public class LauncherModel extends BroadcastReceiver String resourceName = c.getString(iconResourceIndex); info.customIcon = false; // the resource - icon = Utilities.createIconBitmap(packageName, resourceName, mIconCache, context); + icon = Utilities.createIconBitmap(packageName, resourceName, context); // the db if (icon == null) { icon = Utilities.createIconBitmap(c, iconIndex, context); @@ -3612,7 +3613,7 @@ public class LauncherModel extends BroadcastReceiver if (extra instanceof ShortcutIconResource) { iconResource = (ShortcutIconResource) extra; icon = Utilities.createIconBitmap(iconResource.packageName, - iconResource.resourceName, mIconCache, context); + iconResource.resourceName, context); } } diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index c8b27efd3..08f8e5601 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -121,7 +121,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen // 2) Preload icon in the center // 3) Setup icon in the center and app icon in the top right corner. if (mDisabledForSafeMode) { - FastBitmapDrawable disabledIcon = Utilities.createIconDrawable(mIcon); + FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon); disabledIcon.setGhostModeEnabled(true); mCenterDrawable = disabledIcon; mTopCornerDrawable = null; @@ -134,7 +134,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen sPreloaderTheme.applyStyle(R.style.PreloadIcon, true); } - FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon); + FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon); mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); mCenterDrawable.setCallback(this); mTopCornerDrawable = null; diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index a9cbf6970..cffcd3445 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -71,9 +71,6 @@ public final class Utilities { private static final String TAG = "Launcher.Utilities"; - private static int sIconWidth = -1; - private static int sIconHeight = -1; - private static final Rect sOldBounds = new Rect(); private static final Canvas sCanvas = new Canvas(); @@ -87,33 +84,16 @@ public final class Utilities { static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff }; static int sColorIndex = 0; - static int[] sLoc0 = new int[2]; - static int[] sLoc1 = new int[2]; + private static final int[] sLoc0 = new int[2]; + private static final int[] sLoc1 = new int[2]; // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] - static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; - public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); + private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; + private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; - /** - * Returns a FastBitmapDrawable with the icon, accurately sized. - */ - public static FastBitmapDrawable createIconDrawable(Bitmap icon) { - FastBitmapDrawable d = new FastBitmapDrawable(icon); - d.setFilterBitmap(true); - resizeIconDrawable(d); - return d; - } - - /** - * Resizes an icon drawable to the correct icon size. - */ - static void resizeIconDrawable(Drawable icon) { - icon.setBounds(0, 0, sIconWidth, sIconHeight); - } - public static boolean isPropertyEnabled(String propertyName) { return Log.isLoggable(propertyName, Log.VERBOSE); } @@ -141,7 +121,7 @@ public final class Utilities { return Build.VERSION.SDK_INT >= 22; } - static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { + public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { byte[] data = c.getBlob(iconIndex); try { return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); @@ -154,7 +134,7 @@ public final class Utilities { * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. */ - static Bitmap createIconBitmap(String packageName, String resourceName, IconCache cache, + public static Bitmap createIconBitmap(String packageName, String resourceName, Context context) { PackageManager packageManager = context.getPackageManager(); // the resource @@ -163,7 +143,8 @@ public final class Utilities { if (resources != null) { final int id = resources.getIdentifier(resourceName, null, null); return createIconBitmap( - resources.getDrawableForDensity(id, cache.getFullResIconDpi()), context); + resources.getDrawableForDensity(id, LauncherAppState.getInstance() + .getInvariantDeviceProfile().fillResIconDpi), context); } } catch (Exception e) { // Icon not found. @@ -171,16 +152,16 @@ public final class Utilities { return null; } + private static int getIconBitmapSize() { + return LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize; + } + /** * Returns a bitmap which is of the appropriate size to be displayed as an icon */ - static Bitmap createIconBitmap(Bitmap icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } - } - if (sIconWidth == icon.getWidth() && sIconHeight == icon.getHeight()) { + public static Bitmap createIconBitmap(Bitmap icon, Context context) { + final int iconBitmapSize = getIconBitmapSize(); + if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { return icon; } return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context); @@ -190,13 +171,11 @@ public final class Utilities { * Returns a bitmap suitable for the all apps view. */ public static Bitmap createIconBitmap(Drawable icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } + synchronized (sCanvas) { + final int iconBitmapSize = getIconBitmapSize(); - int width = sIconWidth; - int height = sIconHeight; + int width = iconBitmapSize; + int height = iconBitmapSize; if (icon instanceof PaintDrawable) { PaintDrawable painter = (PaintDrawable) icon; @@ -223,8 +202,8 @@ public final class Utilities { } // no intrinsic size --> use default size - int textureWidth = sIconWidth; - int textureHeight = sIconHeight; + int textureWidth = iconBitmapSize; + int textureHeight = iconBitmapSize; final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888); @@ -354,15 +333,6 @@ public final class Utilities { localY < (v.getHeight() + slop); } - private static void initStatics(Context context) { - final Resources resources = context.getResources(); - sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size); - } - - public static void setIconSize(int widthPx) { - sIconWidth = sIconHeight = widthPx; - } - public static void scaleRect(Rect r, float scale) { if (scale != 1.0f) { r.left = (int) (r.left * scale + 0.5f); diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 8d04be5e3..64fc8c528 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -241,6 +241,7 @@ public class WidgetsContainerView extends BaseContainerView Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo); preview = Utilities.createIconBitmap(icon, mLauncher); createItemInfo.spanX = createItemInfo.spanY = 1; + scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth(); } // Don't clip alpha values for the drag outline if we're using the default widget preview -- cgit v1.2.3 From ec84728b270004494dc483c141628ec6417175fb Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 4 Jun 2015 11:37:46 -0700 Subject: Consistent scrolling experience for All apps and widget tray b/21375339 Change-Id: I8362b3ca94b7c4e75932d42cd09a989e0e3919c0 --- res/layout/widgets_view.xml | 1 - res/values/dimens.xml | 1 - src/com/android/launcher3/BaseRecyclerView.java | 7 +-- .../android/launcher3/model/PackageItemInfo.java | 13 +++-- src/com/android/launcher3/model/WidgetsModel.java | 7 +++ .../launcher3/widget/WidgetsContainerView.java | 20 +++++++- .../launcher3/widget/WidgetsRecyclerView.java | 59 ++++++++++++++++++---- 7 files changed, 86 insertions(+), 22 deletions(-) diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index af2c97027..a1a62b32a 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -39,7 +39,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/quantum_panel_dark" - android:scrollbars="vertical" android:elevation="15dp" android:visibility="gone" /> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 246adcdad..fd7e3dabf 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -63,7 +63,6 @@ 12dp 4dp - 64dp -16dp 72dp 48dp diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 8d418f984..a207d9a12 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -86,10 +86,8 @@ public class BaseRecyclerView extends RecyclerView private int mDownX; private int mDownY; - private int mLastX; private int mLastY; private int mScrollbarWidth; - private int mScrollbarMinHeight; private int mScrollbarInset; private Rect mBackgroundPadding = new Rect(); @@ -121,8 +119,6 @@ public class BaseRecyclerView extends RecyclerView mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.all_apps_fast_scroll_text_size)); mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); - mScrollbarMinHeight = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); mScrollbarInset = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(mFastScrollAlpha); @@ -173,7 +169,7 @@ public class BaseRecyclerView extends RecyclerView switch (action) { case MotionEvent.ACTION_DOWN: // Keep track of the down positions - mDownX = mLastX = x; + mDownX = x; mDownY = mLastY = y; if (shouldStopScroll(ev)) { stopScroll(); @@ -188,7 +184,6 @@ public class BaseRecyclerView extends RecyclerView animateFastScrollerVisibility(true); } if (mDraggingFastScroller) { - mLastX = x; mLastY = y; // Scroll to the right position, and update the section name diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java index 0f0134ae3..30f228c68 100644 --- a/src/com/android/launcher3/model/PackageItemInfo.java +++ b/src/com/android/launcher3/model/PackageItemInfo.java @@ -16,7 +16,6 @@ package com.android.launcher3.model; -import android.content.ComponentName; import android.graphics.Bitmap; import com.android.launcher3.ItemInfo; @@ -27,7 +26,6 @@ import java.util.Arrays; * Represents a {@link Package} in the widget tray section. */ public class PackageItemInfo extends ItemInfo { - private static final String TAG = "PackageInfo"; /** * A bitmap version of the application icon. @@ -35,12 +33,21 @@ public class PackageItemInfo extends ItemInfo { public Bitmap iconBitmap; /** - * Indicates whether we're using a low res icon + * Indicates whether we're using a low res icon. */ public boolean usingLowResIcon; + /** + * Package name of the {@link ItemInfo}. + */ public String packageName; + /** + * Character that is used as a section name for the {@link ItemInfo#title}. + * (e.g., "G" will be stored if title is "Google") + */ + public String titleSectionName; + int flags = 0; PackageItemInfo(String packageName) { diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index fdb9795d8..76e6a9dd5 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -10,6 +10,7 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; @@ -39,11 +40,13 @@ public class WidgetsModel { private final Comparator mWidgetAndShortcutNameComparator; private final Comparator mAppNameComparator; private final IconCache mIconCache; + private AlphabeticIndexCompat mIndexer; public WidgetsModel(Context context) { mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); mIconCache = LauncherAppState.getInstance().getIconCache(); + mIndexer = new AlphabeticIndexCompat(context); } private WidgetsModel(WidgetsModel model) { @@ -62,6 +65,9 @@ public class WidgetsModel { // Access methods that may be deleted if the private fields are made package-private. public PackageItemInfo getPackageItemInfo(int pos) { + if (pos >= mPackageItemInfos.size() || pos < 0) { + return null; + } return mPackageItemInfos.get(pos); } @@ -112,6 +118,7 @@ public class WidgetsModel { pInfo = new PackageItemInfo(packageName); mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), true /* userLowResIcon */, pInfo); + pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title); mWidgetsList.put(pInfo, widgetsShortcutsList); tmpPackageItemInfos.put(packageName, pInfo); mPackageItemInfos.add(pInfo); diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 11c2107f2..18b93c3b1 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -20,8 +20,8 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; import android.util.Log; @@ -92,7 +92,6 @@ public class WidgetsContainerView extends BaseContainerView mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher); mAdapter = new WidgetsListAdapter(context, this, this, mLauncher); mIconCache = (LauncherAppState.getInstance()).getIconCache(); - if (DEBUG) { Log.d(TAG, "WidgetsContainerView constructor"); } @@ -345,6 +344,23 @@ public class WidgetsContainerView extends BaseContainerView setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, mFixedBounds.bottom); } + + int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset; + mView.setPadding(inset + mView.getScrollbarWidth(), inset, + inset, inset); + } + + @Override + protected void onUpdateBackgrounds() { + InsetDrawable background; + // Update the background of the reveal view and list to be inset with the fixed bound + // insets instead of the default insets + // TODO: Use quantum_panel instead of quantum_panel_shape. + int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset; + background = new InsetDrawable( + getContext().getResources().getDrawable(R.drawable.quantum_panel_shape), + inset, 0, inset, 0); + mView.updateBackgroundPadding(background); } /** diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index bef255908..4aa332380 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -19,20 +19,25 @@ package com.android.launcher3.widget; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.LinearLayoutManager; import android.util.AttributeSet; -import android.view.MotionEvent; +import android.view.View; import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.model.PackageItemInfo; /** * The widgets recycler view. */ public class WidgetsRecyclerView extends BaseRecyclerView { + private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; private Rect mBackgroundPadding = new Rect(); + private PackageItemInfo mLastPackageItemInfo; public WidgetsRecyclerView(Context context) { this(context, null); @@ -68,8 +73,17 @@ public class WidgetsRecyclerView extends BaseRecyclerView { */ @Override public String scrollToPositionAtProgress(float touchFraction) { - // Ensure that we have any sections - return ""; + float pos = mWidgets.getPackageSize() * touchFraction; + + int posInt = (int) pos; + LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); + getCurScrollState(scrollPosState); + layoutManager.scrollToPositionWithOffset((int) pos, + (int) (scrollPosState.rowHeight * ((float) posInt - pos))); + + posInt = (int) ((touchFraction == 1)? pos -1 : pos); + PackageItemInfo p = mWidgets.getPackageItemInfo(posInt); + return p.titleSectionName; } /** @@ -78,19 +92,41 @@ public class WidgetsRecyclerView extends BaseRecyclerView { @Override public void updateVerticalScrollbarBounds() { int rowCount = mWidgets.getPackageSize(); + verticalScrollbarBounds.setEmpty(); - // Skip early if there are no items. + // Skip early if, there are no items. if (rowCount == 0) { - verticalScrollbarBounds.setEmpty(); return; } - int x, y; + // Skip early if, there no child laid out in the container. getCurScrollState(scrollPosState); if (scrollPosState.rowIndex < 0) { + return; + } + + int actualHeight = getHeight() - getPaddingTop() - getPaddingBottom(); + int totalScrollHeight = rowCount * scrollPosState.rowHeight; + // Skip early if the height of all the rows are actually less than the container height. + if (totalScrollHeight < actualHeight) { verticalScrollbarBounds.setEmpty(); + return; } - // TODO + + int scrollbarHeight = (int) (actualHeight / ((float) totalScrollHeight / actualHeight)); + int availableY = totalScrollHeight - actualHeight; + int availableScrollY = actualHeight - scrollbarHeight; + int y = (scrollPosState.rowIndex * scrollPosState.rowHeight) + - scrollPosState.rowTopOffset; + y = getPaddingTop() + + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); + + // Calculate the position and size of the scroll bar. + int x = getWidth() - getScrollbarWidth() - mBackgroundPadding.right; + if (Utilities.isRtl(getResources())) { + x = mBackgroundPadding.left; + } + verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight); } /** @@ -107,6 +143,11 @@ public class WidgetsRecyclerView extends BaseRecyclerView { if (rowCount == 0) { return; } - // TODO + View child = getChildAt(0); + int position = getChildPosition(child); + + stateOut.rowIndex = position; + stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); + stateOut.rowHeight = child.getHeight(); } } \ No newline at end of file -- cgit v1.2.3 From a5be3ccb53178d702e943c6d19c3de14cc3eb2f1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 4 Jun 2015 12:19:51 -0700 Subject: Adding back icon size dimen Bug: 21638256 Change-Id: I522a8e59e63e4a49a087e4fb3aac102595ea6818 --- res/values/dimens.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4fa997c80..c31a41828 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -15,6 +15,8 @@ --> + 64dp + 6dp 500dp -- cgit v1.2.3 From 091f0ffd929f7a0a9a6af7d202eae13c48c07daa Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 4 Jun 2015 15:19:31 -0700 Subject: Updating widget list when iconCache updates > Fixing bug where items were not getting removed from the memcache > Fixing bug where package entries were not getting removing because of component mismatch. Bug: 21612532 Change-Id: Ie56f3272f7fb7e1a37c5ff9bfa523d814edc1a02 --- src/com/android/launcher3/IconCache.java | 5 ++++- src/com/android/launcher3/LauncherModel.java | 3 +++ src/com/android/launcher3/model/WidgetsModel.java | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index fe25bb9a0..3165337c2 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -275,6 +275,7 @@ public class IconCache { ComponentName component = ComponentName.unflattenFromString(cn); PackageInfo info = pkgInfoMap.get(component.getPackageName()); if (info == null) { + remove(component, user); itemsToRemove.add(c.getInt(rowIndex)); continue; } @@ -291,6 +292,7 @@ public class IconCache { continue; } if (app == null) { + remove(component, user); itemsToRemove.add(c.getInt(rowIndex)); } else { appsToUpdate.add(app); @@ -562,9 +564,10 @@ public class IconCache { */ private CacheEntry getEntryForPackageLocked(String packageName, UserHandleCompat user, boolean useLowResIcon) { - ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME); + ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME); ComponentKey cacheKey = new ComponentKey(cn, user); CacheEntry entry = mCache.get(cacheKey); + if (entry == null || (entry.isLowResIcon && !useLowResIcon)) { entry = new CacheEntry(); boolean entryUpdated = true; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 4974dafab..53966a58f 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2932,6 +2932,9 @@ public class LauncherModel extends BroadcastReceiver } }); } + + // Reload widget list. No need to refresh, as we only want to update the icons and labels. + loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, false); } void enqueuePackageUpdated(PackageUpdatedTask task) { diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index 76e6a9dd5..625d4d696 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -8,7 +8,6 @@ import android.util.Log; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; - import com.android.launcher3.Utilities; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.UserHandleCompat; -- cgit v1.2.3 From 799aa04f2f961742a2f99bc0dc5a19f6bbbe2f56 Mon Sep 17 00:00:00 2001 From: Rahul Chaturvedi Date: Mon, 1 Jun 2015 21:26:41 -0400 Subject: Use a broadcast receiver instead of a settings observer. Settings observer doesn't work if a setting is modified in another process, hence we instead register a receiver which listens for a signal from the settings process that the rotation preference has changed. Change-Id: I570e3c67bb64a32347e84ca00a8ac31d9010eac3 --- AndroidManifest.xml | 8 +++- res/values/strings.xml | 3 ++ src/com/android/launcher3/Launcher.java | 60 +++++++++++++++---------- src/com/android/launcher3/SettingsActivity.java | 22 +++++++-- src/com/android/launcher3/Utilities.java | 3 ++ 5 files changed, 67 insertions(+), 29 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fce469114..f9f5fc298 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -44,6 +44,10 @@ android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" android:protectionLevel="signature" /> + @@ -62,6 +66,7 @@ + + android:autoRemoveFromRecents="true" + android:process=":settings_process"> diff --git a/res/values/strings.xml b/res/values/strings.xml index 440a5378d..305c3101e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -27,6 +27,9 @@ com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS + + com.android.launcher3.permission.RECEIVE_UPDATE_ORIENTATION_BROADCASTS + com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index dc63a761b..6dfe0ebea 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -403,22 +403,34 @@ public class Launcher extends Activity FocusIndicatorView mFocusHandler; @Thunk boolean mRotationEnabled = false; - private boolean mPreferenceObserverRegistered = false; + private boolean mScreenOrientationSettingReceiverRegistered = false; - final private SharedPreferences.OnSharedPreferenceChangeListener mSettingsObserver = - new SharedPreferences.OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) { - if (mRotationEnabled = sharedPreferences.getBoolean( - Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false)) { - unlockScreenOrientation(true); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); + final private BroadcastReceiver mScreenOrientationSettingReceiver = + new BroadcastReceiver() { + @Thunk Runnable mUpdateOrientationRunnable = new Runnable() { + public void run() { + setOrientation(); + } + }; + + @Override + public void onReceive(Context context, Intent intent) { + mRotationEnabled = intent.getBooleanExtra( + Utilities.SCREEN_ROTATION_SETTING_EXTRA, false); + if (!waitUntilResume(mUpdateOrientationRunnable, true)) { + setOrientation(); + } } - } + }; + + @Thunk void setOrientation() { + if (mRotationEnabled) { + unlockScreenOrientation(true); + } else { + setRequestedOrientation( + ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); } - }; + } @Override protected void onCreate(Bundle savedInstanceState) { @@ -520,16 +532,18 @@ public class Launcher extends Activity // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { - getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, - Context.MODE_MULTI_PROCESS).registerOnSharedPreferenceChangeListener( - mSettingsObserver); - mPreferenceObserverRegistered = true; + String updateOrientationBroadcastPermission = getResources().getString( + R.string.receive_update_orientation_broadcasts_permission); + registerReceiver(mScreenOrientationSettingReceiver, + new IntentFilter(Utilities.SCREEN_ROTATION_SETTING_INTENT), + updateOrientationBroadcastPermission, null); + mScreenOrientationSettingReceiverRegistered = true; mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); } // On large interfaces, or on devices that a user has specifically enabled screen rotation, // we want the screen to auto-rotate based on the current orientation - unlockScreenOrientation(true); + setOrientation(); if (mLauncherCallbacks != null) { mLauncherCallbacks.onCreate(savedInstanceState); @@ -2016,11 +2030,9 @@ public class Launcher extends Activity public void onDestroy() { super.onDestroy(); - if (mPreferenceObserverRegistered) { - getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, - Context.MODE_MULTI_PROCESS).unregisterOnSharedPreferenceChangeListener( - mSettingsObserver); - mPreferenceObserverRegistered = false; + if (mScreenOrientationSettingReceiverRegistered) { + unregisterReceiver(mScreenOrientationSettingReceiver); + mScreenOrientationSettingReceiverRegistered = false; } // Remove all pending runnables @@ -3663,7 +3675,7 @@ public class Launcher extends Activity * * @return {@code true} if we are currently paused. The caller might be able to skip some work */ - private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { + @Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { if (mPaused) { if (LOGD) Log.d(TAG, "Deferring update until onResume"); if (deletePreviousRunnables) { diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index a1da1b6fd..27763f545 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -18,12 +18,14 @@ package com.android.launcher3; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.os.Bundle; +import android.preference.Preference; import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; /** - * Settings activity for Launcher. Currently implements the following setting: - * LockToPortrait + * Settings activity for Launcher. Currently implements the following setting: Allow rotation */ public class SettingsActivity extends Activity { @Override @@ -41,13 +43,25 @@ public class SettingsActivity extends Activity { */ @SuppressWarnings("WeakerAccess") public static class LauncherSettingsFragment extends PreferenceFragment { - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS); getPreferenceManager().setSharedPreferencesName(LauncherFiles.ROTATION_PREF_FILE); addPreferencesFromResource(R.xml.launcher_preferences); } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, + Preference preference) { + boolean allowRotation = getPreferenceManager().getSharedPreferences().getBoolean( + Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false); + Intent rotationSetting = new Intent(Utilities.SCREEN_ROTATION_SETTING_INTENT); + String launchBroadcastPermission = getResources().getString( + R.string.receive_update_orientation_broadcasts_permission); + rotationSetting.putExtra(Utilities.SCREEN_ROTATION_SETTING_EXTRA, allowRotation); + getActivity().sendBroadcast(rotationSetting, launchBroadcastPermission); + return true; + } } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index cffcd3445..b267f759d 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -93,6 +93,9 @@ public final class Utilities { private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; + public static final String SCREEN_ROTATION_SETTING_INTENT = + "come.android.launcher3.SCREEN_ORIENTATION_PREF_CHANGED"; + public static final String SCREEN_ROTATION_SETTING_EXTRA = "screenRotationPref"; public static boolean isPropertyEnabled(String propertyName) { return Log.isLoggable(propertyName, Log.VERBOSE); -- cgit v1.2.3 From 0c9143d06b64ca40c64e66ddd65848deeead8131 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 4 Jun 2015 16:29:51 -0700 Subject: Preventing overdraw in custom content screen > Assuming customcontent screen is opaque, setting scrim alpha to 0 when the screen is fully visible Bug: 21584384 Change-Id: Ifa95a10d880532f59573090673e90a87c7edba9b --- src/com/android/launcher3/Workspace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 193a0af6f..d0b9a2206 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1620,7 +1620,7 @@ public class Workspace extends PagedView // We should only update the drag layer background alpha if we are not in all apps or the // widgets tray if (mState == State.NORMAL) { - mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f); + mLauncher.getDragLayer().setBackgroundAlpha(progress == 1 ? 0 : progress * 0.8f); } if (mLauncher.getHotseat() != null) { -- cgit v1.2.3 From 12e13e467e6eadf8efe84525e9266b1f7df91733 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 4 Jun 2015 21:19:02 -0700 Subject: Removing LauncherApplication to enable us to easily catch LauncherAppState errors Change-Id: I56e956067692e5b9c4719c5e08b8f558e6011a60 --- AndroidManifest.xml | 1 - src/com/android/launcher3/Launcher.java | 1 - src/com/android/launcher3/LauncherApplication.java | 34 ---------------------- 3 files changed, 36 deletions(-) delete mode 100644 src/com/android/launcher3/LauncherApplication.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f9f5fc298..5f9d7f436 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -70,7 +70,6 @@ Date: Fri, 5 Jun 2015 00:13:25 -0700 Subject: Code cleanup > Removing obsolete logging > Removing unused methods > Removing resource leak warning due to non-static handler class in launcher Change-Id: Ic38cc8aea82899b0b5ee3235f04e5964e49245fb --- src/com/android/launcher3/FocusHelper.java | 12 +++---- src/com/android/launcher3/Hotseat.java | 24 +++---------- src/com/android/launcher3/Launcher.java | 40 +++++----------------- .../launcher3/LauncherAnimatorUpdateListener.java | 30 ---------------- src/com/android/launcher3/LauncherAppState.java | 11 ------ src/com/android/launcher3/LauncherModel.java | 7 ---- src/com/android/launcher3/LauncherProvider.java | 2 -- src/com/android/launcher3/Workspace.java | 2 +- .../WorkspaceStateTransitionAnimation.java | 11 ++---- 9 files changed, 23 insertions(+), 116 deletions(-) delete mode 100644 src/com/android/launcher3/LauncherAnimatorUpdateListener.java diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 70bb01af0..57aec3280 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -222,8 +222,8 @@ public class FocusHelper { if (keyCode == KeyEvent.KEYCODE_DPAD_UP && !profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, - true /* hotseat horizontal */, hotseat.getAllAppsButtonRank(), - iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */); + true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank, + iconRank == profile.inv.hotseatAllAppsRank /* include all apps icon */); iconIndex += iconParent.getChildCount(); countX = iconLayout.getCountX(); countY = iconLayout.getCountY() + hotseatLayout.getCountY(); @@ -231,8 +231,8 @@ public class FocusHelper { } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, - false /* hotseat horizontal */, hotseat.getAllAppsButtonRank(), - iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */); + false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank, + iconRank == profile.inv.hotseatAllAppsRank /* include all apps icon */); iconIndex += iconParent.getChildCount(); countX = iconLayout.getCountX() + hotseatLayout.getCountX(); countY = iconLayout.getCountY(); @@ -316,13 +316,13 @@ public class FocusHelper { // with the hotseat. if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */, - hotseat.getAllAppsButtonRank(), + profile.inv.hotseatAllAppsRank, !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */); countY = countY + 1; } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */, - hotseat.getAllAppsButtonRank(), + profile.inv.hotseatAllAppsRank, !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */); countX = countX + 1; } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 6e33d1014..ff4c93ab7 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -36,8 +36,7 @@ public class Hotseat extends FrameLayout private int mAllAppsButtonRank; - private boolean mTransposeLayoutWithOrientation; - private boolean mIsLandscape; + private final boolean mHasVerticalHotseat; public Hotseat(Context context) { this(context, null); @@ -49,13 +48,8 @@ public class Hotseat extends FrameLayout public Hotseat(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - - Resources r = context.getResources(); - mTransposeLayoutWithOrientation = - r.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); - mIsLandscape = context.getResources().getConfiguration().orientation == - Configuration.ORIENTATION_LANDSCAPE; mLauncher = (Launcher) context; + mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout(); } CellLayout getLayout() { @@ -77,26 +71,18 @@ public class Hotseat extends FrameLayout mContent.setOnLongClickListener(l); } - private boolean hasVerticalHotseat() { - return (mIsLandscape && mTransposeLayoutWithOrientation); - } - /* Get the orientation invariant order of the item in the hotseat for persistence. */ int getOrderInHotseat(int x, int y) { - return hasVerticalHotseat() ? (mContent.getCountY() - y - 1) : x; + return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x; } /* Get the orientation specific coordinates given an invariant order in the hotseat. */ int getCellXFromOrder(int rank) { - return hasVerticalHotseat() ? 0 : rank; + return mHasVerticalHotseat ? 0 : rank; } int getCellYFromOrder(int rank) { - return hasVerticalHotseat() ? (mContent.getCountY() - (rank + 1)) : 0; - } - - public int getAllAppsButtonRank() { - return mAllAppsButtonRank; + return mHasVerticalHotseat ? (mContent.getCountY() - (rank + 1)) : 0; } public boolean isAllAppsButtonRank(int rank) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 6dfe0ebea..2be2d9d5b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1810,16 +1810,17 @@ public class Launcher extends Activity } } - private final Handler mHandler = new Handler() { + @Thunk final Handler mHandler = new Handler(new Handler.Callback() { + @Override - public void handleMessage(Message msg) { + public boolean handleMessage(Message msg) { if (msg.what == ADVANCE_MSG) { int i = 0; for (View key: mWidgetsToAdvance.keySet()) { final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId); final int delay = mAdvanceStagger * i; if (v instanceof Advanceable) { - postDelayed(new Runnable() { + mHandler.postDelayed(new Runnable() { public void run() { ((Advanceable) v).advance(); } @@ -1829,8 +1830,9 @@ public class Launcher extends Activity } sendAdvanceMessage(mAdvanceInterval); } + return true; } - }; + }); void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) { if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return; @@ -2345,7 +2347,7 @@ public class Launcher extends Activity Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); createShortcutIntent.setComponent(componentName); - processShortcut(createShortcutIntent); + Utilities.startActivityForResultSafely(this, createShortcutIntent, REQUEST_CREATE_SHORTCUT); } /** @@ -2402,14 +2404,6 @@ public class Launcher extends Activity } } - void processShortcut(Intent intent) { - Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT); - } - - void processWallpaper(Intent intent) { - startActivityForResult(intent, REQUEST_PICK_WALLPAPER); - } - FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX, int cellY) { final FolderInfo folderInfo = new FolderInfo(); @@ -2811,20 +2805,10 @@ public class Launcher extends Activity if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickSettingsButton(v); } else { - showSettingsActivity(); + startActivity(new Intent(this, SettingsActivity.class)); } } - public void onTouchDownAllAppsButton(View v) { - // Provide the same haptic feedback that the system offers for virtual keys. - v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); - } - - public void performHapticFeedbackOnTouchDown(View v) { - // Provide the same haptic feedback that the system offers for virtual keys. - v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); - } - public View.OnTouchListener getHapticFeedbackTouchListener() { if (mHapticFeedbackTouchListener == null) { mHapticFeedbackTouchListener = new View.OnTouchListener() { @@ -3774,10 +3758,6 @@ public class Launcher extends Activity @Override public void bindAddScreens(ArrayList orderedScreenIds) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - bindAddScreens()", true); - Launcher.addDumpLog(TAG, "11683562 - orderedScreenIds: " + - TextUtils.join(", ", orderedScreenIds), true); int count = orderedScreenIds.size(); for (int i = 0; i < count; i++) { mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i)); @@ -4535,10 +4515,6 @@ public class Launcher extends Activity editor.apply(); } - private void showSettingsActivity() { - startActivity(new Intent(this, SettingsActivity.class)); - } - /** * To be overridden by subclasses to indicate that there is an in-activity full-screen intro * screen that must be displayed and dismissed. diff --git a/src/com/android/launcher3/LauncherAnimatorUpdateListener.java b/src/com/android/launcher3/LauncherAnimatorUpdateListener.java deleted file mode 100644 index ec9fd4d16..000000000 --- a/src/com/android/launcher3/LauncherAnimatorUpdateListener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2011 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.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; - -abstract class LauncherAnimatorUpdateListener implements AnimatorUpdateListener { - public void onAnimationUpdate(ValueAnimator animation) { - final float b = (Float) animation.getAnimatedValue(); - final float a = 1f - b; - onAnimationUpdate(a, b); - } - - abstract void onAnimationUpdate(float a, float b); -} \ No newline at end of file diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index d4b41e671..afa09ec83 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -41,7 +41,6 @@ public class LauncherAppState { private final WidgetPreviewLoader mWidgetCache; private final boolean mIsScreenLarge; - private final float mScreenDensity; private final int mLongPressTimeout = 300; private boolean mWallpaperChangedSinceLastCheck; @@ -89,7 +88,6 @@ public class LauncherAppState { // set sIsScreenXLarge and mScreenDensity *before* creating icon cache mIsScreenLarge = isScreenLarge(sContext.getResources()); - mScreenDensity = sContext.getResources().getDisplayMetrics().density; mInvariantDeviceProfile = new InvariantDeviceProfile(sContext); mIconCache = new IconCache(sContext, mInvariantDeviceProfile); mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache); @@ -180,15 +178,6 @@ public class LauncherAppState { return res.getBoolean(R.bool.is_large_tablet); } - public static boolean isScreenLandscape(Context context) { - return context.getResources().getConfiguration().orientation == - Configuration.ORIENTATION_LANDSCAPE; - } - - public float getScreenDensity() { - return mScreenDensity; - } - public int getLongPressTimeout() { return mLongPressTimeout; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 53966a58f..10b8648de 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1140,10 +1140,6 @@ public class LauncherModel extends BroadcastReceiver * a list of screen ids in the order that they should appear. */ void updateWorkspaceScreenOrder(Context context, final ArrayList screens) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true); - Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true); - final ArrayList screensCopy = new ArrayList(screens); final ContentResolver cr = context.getContentResolver(); final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; @@ -1764,9 +1760,6 @@ public class LauncherModel extends BroadcastReceiver } private void loadWorkspace() { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true); - final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; final Context context = mContext; diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 45070d190..b5901265c 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -868,8 +868,6 @@ public class LauncherProvider extends ContentProvider { throw new RuntimeException("Error: max screen id was not initialized"); } mMaxScreenId += 1; - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true); return mMaxScreenId; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d0b9a2206..19195b421 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3969,7 +3969,7 @@ public class Workspace extends PagedView @Override public boolean onEnterScrollArea(int x, int y, int direction) { // Ignore the scroll area if we are dragging over the hot seat - boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext()); + boolean isPortrait = !mLauncher.getDeviceProfile().isLandscape; if (mLauncher.getHotseat() != null && isPortrait) { Rect r = new Rect(); mLauncher.getHotseat().getHitRect(r); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 340066d64..e360e889b 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; @@ -335,17 +336,11 @@ public class WorkspaceStateTransitionAnimation { } if (mOldBackgroundAlphas[i] != 0 || mNewBackgroundAlphas[i] != 0) { - ValueAnimator bgAnim = + ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha", + mOldBackgroundAlphas[i], mNewBackgroundAlphas[i]); LauncherAnimUtils.ofFloat(cl, 0f, 1f); bgAnim.setInterpolator(mZoomInInterpolator); bgAnim.setDuration(duration); - bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { - public void onAnimationUpdate(float a, float b) { - cl.setBackgroundAlpha( - a * mOldBackgroundAlphas[i] + - b * mNewBackgroundAlphas[i]); - } - }); mStateAnimator.play(bgAnim); } } -- cgit v1.2.3 From 40b626b930a1cf51f422fc3c41661fed14fedb84 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 4 Jun 2015 22:40:14 -0700 Subject: Updating assets Bug: 21304080 Change-Id: I26cf435779b477f0961d8e0d56dd6b86b418a3be --- res/drawable-hdpi/ic_info_launcher.png | Bin 0 -> 2727 bytes res/drawable-hdpi/ic_launcher_info_normal.png | Bin 4437 -> 0 bytes res/drawable-hdpi/ic_launcher_remove_normal.png | Bin 2142 -> 0 bytes res/drawable-hdpi/ic_launcher_uninstall_normal.png | Bin 1540 -> 0 bytes res/drawable-hdpi/ic_remove_launcher.png | Bin 0 -> 1610 bytes res/drawable-hdpi/ic_uninstall_launcher.png | Bin 0 -> 1194 bytes res/drawable-hdpi/portal_ring_inner.png | Bin 0 -> 4386 bytes res/drawable-hdpi/portal_ring_inner_holo.png | Bin 4429 -> 0 bytes res/drawable-hdpi/portal_ring_inner_nolip.png | Bin 0 -> 4107 bytes res/drawable-hdpi/portal_ring_inner_nolip_holo.png | Bin 4118 -> 0 bytes res/drawable-hdpi/portal_ring_outer.png | Bin 0 -> 7887 bytes res/drawable-hdpi/portal_ring_outer_holo.png | Bin 7658 -> 0 bytes res/drawable-hdpi/portal_ring_rest.png | Bin 2646 -> 2794 bytes res/drawable-hdpi/widget_resize_frame.9.png | Bin 522 -> 405 bytes res/drawable-hdpi/widget_resize_shadow.9.png | Bin 681 -> 527 bytes res/drawable-mdpi/ic_info_launcher.png | Bin 0 -> 1603 bytes res/drawable-mdpi/ic_launcher_info_normal.png | Bin 2729 -> 0 bytes res/drawable-mdpi/ic_launcher_remove_normal.png | Bin 1614 -> 0 bytes res/drawable-mdpi/ic_launcher_uninstall_normal.png | Bin 1275 -> 0 bytes res/drawable-mdpi/ic_remove_launcher.png | Bin 0 -> 1087 bytes res/drawable-mdpi/ic_uninstall_launcher.png | Bin 0 -> 932 bytes res/drawable-mdpi/portal_ring_inner.png | Bin 0 -> 2641 bytes res/drawable-mdpi/portal_ring_inner_holo.png | Bin 2779 -> 0 bytes res/drawable-mdpi/portal_ring_inner_nolip.png | Bin 0 -> 2545 bytes res/drawable-mdpi/portal_ring_inner_nolip_holo.png | Bin 2632 -> 0 bytes res/drawable-mdpi/portal_ring_outer.png | Bin 0 -> 4704 bytes res/drawable-mdpi/portal_ring_outer_holo.png | Bin 4798 -> 0 bytes res/drawable-mdpi/portal_ring_rest.png | Bin 1633 -> 1675 bytes res/drawable-mdpi/widget_resize_frame.9.png | Bin 444 -> 304 bytes res/drawable-mdpi/widget_resize_shadow.9.png | Bin 486 -> 367 bytes res/drawable-xhdpi/ic_info_launcher.png | Bin 0 -> 3777 bytes res/drawable-xhdpi/ic_launcher_info_normal.png | Bin 6041 -> 0 bytes res/drawable-xhdpi/ic_launcher_remove_normal.png | Bin 2393 -> 0 bytes res/drawable-xhdpi/ic_launcher_uninstall_normal.png | Bin 1979 -> 0 bytes res/drawable-xhdpi/ic_remove_launcher.png | Bin 0 -> 1724 bytes res/drawable-xhdpi/ic_uninstall_launcher.png | Bin 0 -> 1456 bytes res/drawable-xhdpi/portal_ring_inner.png | Bin 0 -> 5983 bytes res/drawable-xhdpi/portal_ring_inner_holo.png | Bin 6711 -> 0 bytes res/drawable-xhdpi/portal_ring_inner_nolip.png | Bin 0 -> 5569 bytes res/drawable-xhdpi/portal_ring_inner_nolip_holo.png | Bin 6191 -> 0 bytes res/drawable-xhdpi/portal_ring_outer.png | Bin 0 -> 10394 bytes res/drawable-xhdpi/portal_ring_outer_holo.png | Bin 11037 -> 0 bytes res/drawable-xhdpi/portal_ring_rest.png | Bin 3939 -> 3754 bytes res/drawable-xhdpi/widget_resize_frame.9.png | Bin 670 -> 494 bytes res/drawable-xhdpi/widget_resize_shadow.9.png | Bin 918 -> 669 bytes res/drawable-xxhdpi/ic_info_launcher.png | Bin 0 -> 6343 bytes res/drawable-xxhdpi/ic_launcher_info_normal.png | Bin 9762 -> 0 bytes res/drawable-xxhdpi/ic_launcher_remove_normal.png | Bin 5087 -> 0 bytes res/drawable-xxhdpi/ic_launcher_uninstall_normal.png | Bin 2773 -> 0 bytes res/drawable-xxhdpi/ic_remove_launcher.png | Bin 0 -> 2892 bytes res/drawable-xxhdpi/ic_uninstall_launcher.png | Bin 0 -> 2045 bytes res/drawable-xxhdpi/portal_ring_inner.png | Bin 0 -> 9918 bytes res/drawable-xxhdpi/portal_ring_inner_holo.png | Bin 10046 -> 0 bytes res/drawable-xxhdpi/portal_ring_inner_nolip.png | Bin 0 -> 9127 bytes res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png | Bin 9127 -> 0 bytes res/drawable-xxhdpi/portal_ring_outer.png | Bin 0 -> 19200 bytes res/drawable-xxhdpi/portal_ring_outer_holo.png | Bin 16576 -> 0 bytes res/drawable-xxhdpi/widget_resize_frame.9.png | Bin 953 -> 626 bytes res/drawable-xxhdpi/widget_resize_shadow.9.png | Bin 1320 -> 975 bytes res/drawable-xxxhdpi/ic_info_launcher.png | Bin 0 -> 5724 bytes res/drawable-xxxhdpi/ic_launcher_info_normal.png | Bin 8414 -> 0 bytes res/drawable-xxxhdpi/ic_launcher_remove_normal.png | Bin 3721 -> 0 bytes res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png | Bin 2869 -> 0 bytes res/drawable-xxxhdpi/ic_remove_launcher.png | Bin 0 -> 3210 bytes res/drawable-xxxhdpi/ic_uninstall_launcher.png | Bin 0 -> 2445 bytes res/drawable-xxxhdpi/portal_ring_inner.png | Bin 0 -> 13024 bytes res/drawable-xxxhdpi/portal_ring_inner_nolip.png | Bin 0 -> 12202 bytes res/drawable-xxxhdpi/portal_ring_outer.png | Bin 0 -> 23360 bytes res/drawable-xxxhdpi/portal_ring_rest.png | Bin 0 -> 8628 bytes res/drawable-xxxhdpi/widget_resize_frame.9.png | Bin 2052 -> 1569 bytes res/drawable-xxxhdpi/widget_resize_shadow.9.png | Bin 2512 -> 2058 bytes res/layout/folder_icon.xml | 2 +- src/com/android/launcher3/DeleteDropTarget.java | 2 +- src/com/android/launcher3/FolderIcon.java | 4 ++-- src/com/android/launcher3/InfoDropTarget.java | 2 +- src/com/android/launcher3/UninstallDropTarget.java | 2 +- 76 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 res/drawable-hdpi/ic_info_launcher.png delete mode 100644 res/drawable-hdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-hdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-hdpi/ic_launcher_uninstall_normal.png create mode 100644 res/drawable-hdpi/ic_remove_launcher.png create mode 100644 res/drawable-hdpi/ic_uninstall_launcher.png create mode 100644 res/drawable-hdpi/portal_ring_inner.png delete mode 100644 res/drawable-hdpi/portal_ring_inner_holo.png create mode 100644 res/drawable-hdpi/portal_ring_inner_nolip.png delete mode 100644 res/drawable-hdpi/portal_ring_inner_nolip_holo.png create mode 100644 res/drawable-hdpi/portal_ring_outer.png delete mode 100644 res/drawable-hdpi/portal_ring_outer_holo.png create mode 100644 res/drawable-mdpi/ic_info_launcher.png delete mode 100644 res/drawable-mdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-mdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-mdpi/ic_launcher_uninstall_normal.png create mode 100644 res/drawable-mdpi/ic_remove_launcher.png create mode 100644 res/drawable-mdpi/ic_uninstall_launcher.png create mode 100644 res/drawable-mdpi/portal_ring_inner.png delete mode 100644 res/drawable-mdpi/portal_ring_inner_holo.png create mode 100644 res/drawable-mdpi/portal_ring_inner_nolip.png delete mode 100644 res/drawable-mdpi/portal_ring_inner_nolip_holo.png create mode 100644 res/drawable-mdpi/portal_ring_outer.png delete mode 100644 res/drawable-mdpi/portal_ring_outer_holo.png create mode 100644 res/drawable-xhdpi/ic_info_launcher.png delete mode 100644 res/drawable-xhdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-xhdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-xhdpi/ic_launcher_uninstall_normal.png create mode 100644 res/drawable-xhdpi/ic_remove_launcher.png create mode 100644 res/drawable-xhdpi/ic_uninstall_launcher.png create mode 100644 res/drawable-xhdpi/portal_ring_inner.png delete mode 100644 res/drawable-xhdpi/portal_ring_inner_holo.png create mode 100644 res/drawable-xhdpi/portal_ring_inner_nolip.png delete mode 100644 res/drawable-xhdpi/portal_ring_inner_nolip_holo.png create mode 100644 res/drawable-xhdpi/portal_ring_outer.png delete mode 100644 res/drawable-xhdpi/portal_ring_outer_holo.png create mode 100644 res/drawable-xxhdpi/ic_info_launcher.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-xxhdpi/ic_launcher_uninstall_normal.png create mode 100644 res/drawable-xxhdpi/ic_remove_launcher.png create mode 100644 res/drawable-xxhdpi/ic_uninstall_launcher.png create mode 100644 res/drawable-xxhdpi/portal_ring_inner.png delete mode 100644 res/drawable-xxhdpi/portal_ring_inner_holo.png create mode 100644 res/drawable-xxhdpi/portal_ring_inner_nolip.png delete mode 100644 res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png create mode 100644 res/drawable-xxhdpi/portal_ring_outer.png delete mode 100644 res/drawable-xxhdpi/portal_ring_outer_holo.png create mode 100644 res/drawable-xxxhdpi/ic_info_launcher.png delete mode 100644 res/drawable-xxxhdpi/ic_launcher_info_normal.png delete mode 100644 res/drawable-xxxhdpi/ic_launcher_remove_normal.png delete mode 100644 res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png create mode 100644 res/drawable-xxxhdpi/ic_remove_launcher.png create mode 100644 res/drawable-xxxhdpi/ic_uninstall_launcher.png create mode 100644 res/drawable-xxxhdpi/portal_ring_inner.png create mode 100644 res/drawable-xxxhdpi/portal_ring_inner_nolip.png create mode 100644 res/drawable-xxxhdpi/portal_ring_outer.png create mode 100644 res/drawable-xxxhdpi/portal_ring_rest.png diff --git a/res/drawable-hdpi/ic_info_launcher.png b/res/drawable-hdpi/ic_info_launcher.png new file mode 100644 index 000000000..ec0cdd18c Binary files /dev/null and b/res/drawable-hdpi/ic_info_launcher.png differ diff --git a/res/drawable-hdpi/ic_launcher_info_normal.png b/res/drawable-hdpi/ic_launcher_info_normal.png deleted file mode 100644 index 780d7962a..000000000 Binary files a/res/drawable-hdpi/ic_launcher_info_normal.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_remove_normal.png b/res/drawable-hdpi/ic_launcher_remove_normal.png deleted file mode 100644 index 91e19ca34..000000000 Binary files a/res/drawable-hdpi/ic_launcher_remove_normal.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_launcher_uninstall_normal.png b/res/drawable-hdpi/ic_launcher_uninstall_normal.png deleted file mode 100644 index 7aea5d06e..000000000 Binary files a/res/drawable-hdpi/ic_launcher_uninstall_normal.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_remove_launcher.png b/res/drawable-hdpi/ic_remove_launcher.png new file mode 100644 index 000000000..c0814579a Binary files /dev/null and b/res/drawable-hdpi/ic_remove_launcher.png differ diff --git a/res/drawable-hdpi/ic_uninstall_launcher.png b/res/drawable-hdpi/ic_uninstall_launcher.png new file mode 100644 index 000000000..3d8f72644 Binary files /dev/null and b/res/drawable-hdpi/ic_uninstall_launcher.png differ diff --git a/res/drawable-hdpi/portal_ring_inner.png b/res/drawable-hdpi/portal_ring_inner.png new file mode 100644 index 000000000..c29b4aa64 Binary files /dev/null and b/res/drawable-hdpi/portal_ring_inner.png differ diff --git a/res/drawable-hdpi/portal_ring_inner_holo.png b/res/drawable-hdpi/portal_ring_inner_holo.png deleted file mode 100644 index 857a01ec4..000000000 Binary files a/res/drawable-hdpi/portal_ring_inner_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/portal_ring_inner_nolip.png b/res/drawable-hdpi/portal_ring_inner_nolip.png new file mode 100644 index 000000000..e2f06fe9b Binary files /dev/null and b/res/drawable-hdpi/portal_ring_inner_nolip.png differ diff --git a/res/drawable-hdpi/portal_ring_inner_nolip_holo.png b/res/drawable-hdpi/portal_ring_inner_nolip_holo.png deleted file mode 100644 index 53df36a43..000000000 Binary files a/res/drawable-hdpi/portal_ring_inner_nolip_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/portal_ring_outer.png b/res/drawable-hdpi/portal_ring_outer.png new file mode 100644 index 000000000..e7b436b7b Binary files /dev/null and b/res/drawable-hdpi/portal_ring_outer.png differ diff --git a/res/drawable-hdpi/portal_ring_outer_holo.png b/res/drawable-hdpi/portal_ring_outer_holo.png deleted file mode 100644 index b711cf350..000000000 Binary files a/res/drawable-hdpi/portal_ring_outer_holo.png and /dev/null differ diff --git a/res/drawable-hdpi/portal_ring_rest.png b/res/drawable-hdpi/portal_ring_rest.png index 2979b736b..e3b13393e 100644 Binary files a/res/drawable-hdpi/portal_ring_rest.png and b/res/drawable-hdpi/portal_ring_rest.png differ diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png index 5772672b1..b0a740384 100644 Binary files a/res/drawable-hdpi/widget_resize_frame.9.png and b/res/drawable-hdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png index a67da6e04..6e2932dcd 100644 Binary files a/res/drawable-hdpi/widget_resize_shadow.9.png and b/res/drawable-hdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-mdpi/ic_info_launcher.png b/res/drawable-mdpi/ic_info_launcher.png new file mode 100644 index 000000000..c72d0c2a9 Binary files /dev/null and b/res/drawable-mdpi/ic_info_launcher.png differ diff --git a/res/drawable-mdpi/ic_launcher_info_normal.png b/res/drawable-mdpi/ic_launcher_info_normal.png deleted file mode 100644 index 8c60159f7..000000000 Binary files a/res/drawable-mdpi/ic_launcher_info_normal.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_remove_normal.png b/res/drawable-mdpi/ic_launcher_remove_normal.png deleted file mode 100644 index 60829b9e1..000000000 Binary files a/res/drawable-mdpi/ic_launcher_remove_normal.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_launcher_uninstall_normal.png b/res/drawable-mdpi/ic_launcher_uninstall_normal.png deleted file mode 100644 index aefbc695b..000000000 Binary files a/res/drawable-mdpi/ic_launcher_uninstall_normal.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_remove_launcher.png b/res/drawable-mdpi/ic_remove_launcher.png new file mode 100644 index 000000000..4a2c3197e Binary files /dev/null and b/res/drawable-mdpi/ic_remove_launcher.png differ diff --git a/res/drawable-mdpi/ic_uninstall_launcher.png b/res/drawable-mdpi/ic_uninstall_launcher.png new file mode 100644 index 000000000..af456690c Binary files /dev/null and b/res/drawable-mdpi/ic_uninstall_launcher.png differ diff --git a/res/drawable-mdpi/portal_ring_inner.png b/res/drawable-mdpi/portal_ring_inner.png new file mode 100644 index 000000000..99aaa60a9 Binary files /dev/null and b/res/drawable-mdpi/portal_ring_inner.png differ diff --git a/res/drawable-mdpi/portal_ring_inner_holo.png b/res/drawable-mdpi/portal_ring_inner_holo.png deleted file mode 100644 index 72e0af8f8..000000000 Binary files a/res/drawable-mdpi/portal_ring_inner_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/portal_ring_inner_nolip.png b/res/drawable-mdpi/portal_ring_inner_nolip.png new file mode 100644 index 000000000..f981778f5 Binary files /dev/null and b/res/drawable-mdpi/portal_ring_inner_nolip.png differ diff --git a/res/drawable-mdpi/portal_ring_inner_nolip_holo.png b/res/drawable-mdpi/portal_ring_inner_nolip_holo.png deleted file mode 100644 index 483f0ebec..000000000 Binary files a/res/drawable-mdpi/portal_ring_inner_nolip_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/portal_ring_outer.png b/res/drawable-mdpi/portal_ring_outer.png new file mode 100644 index 000000000..c98f64b76 Binary files /dev/null and b/res/drawable-mdpi/portal_ring_outer.png differ diff --git a/res/drawable-mdpi/portal_ring_outer_holo.png b/res/drawable-mdpi/portal_ring_outer_holo.png deleted file mode 100644 index e9b35f3f3..000000000 Binary files a/res/drawable-mdpi/portal_ring_outer_holo.png and /dev/null differ diff --git a/res/drawable-mdpi/portal_ring_rest.png b/res/drawable-mdpi/portal_ring_rest.png index d0a976e55..5c33b424d 100644 Binary files a/res/drawable-mdpi/portal_ring_rest.png and b/res/drawable-mdpi/portal_ring_rest.png differ diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png index 8bc8a5c12..856cec619 100644 Binary files a/res/drawable-mdpi/widget_resize_frame.9.png and b/res/drawable-mdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png index 2bae2b603..02522f344 100644 Binary files a/res/drawable-mdpi/widget_resize_shadow.9.png and b/res/drawable-mdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-xhdpi/ic_info_launcher.png b/res/drawable-xhdpi/ic_info_launcher.png new file mode 100644 index 000000000..076b59bf7 Binary files /dev/null and b/res/drawable-xhdpi/ic_info_launcher.png differ diff --git a/res/drawable-xhdpi/ic_launcher_info_normal.png b/res/drawable-xhdpi/ic_launcher_info_normal.png deleted file mode 100644 index 5c49816ff..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_info_normal.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_remove_normal.png b/res/drawable-xhdpi/ic_launcher_remove_normal.png deleted file mode 100644 index 8188805cc..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_remove_normal.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_launcher_uninstall_normal.png b/res/drawable-xhdpi/ic_launcher_uninstall_normal.png deleted file mode 100644 index a093f28c9..000000000 Binary files a/res/drawable-xhdpi/ic_launcher_uninstall_normal.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_remove_launcher.png b/res/drawable-xhdpi/ic_remove_launcher.png new file mode 100644 index 000000000..de058893a Binary files /dev/null and b/res/drawable-xhdpi/ic_remove_launcher.png differ diff --git a/res/drawable-xhdpi/ic_uninstall_launcher.png b/res/drawable-xhdpi/ic_uninstall_launcher.png new file mode 100644 index 000000000..dd50e88c6 Binary files /dev/null and b/res/drawable-xhdpi/ic_uninstall_launcher.png differ diff --git a/res/drawable-xhdpi/portal_ring_inner.png b/res/drawable-xhdpi/portal_ring_inner.png new file mode 100644 index 000000000..7b6a8a04e Binary files /dev/null and b/res/drawable-xhdpi/portal_ring_inner.png differ diff --git a/res/drawable-xhdpi/portal_ring_inner_holo.png b/res/drawable-xhdpi/portal_ring_inner_holo.png deleted file mode 100644 index f9acfa284..000000000 Binary files a/res/drawable-xhdpi/portal_ring_inner_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/portal_ring_inner_nolip.png b/res/drawable-xhdpi/portal_ring_inner_nolip.png new file mode 100644 index 000000000..4b84436ee Binary files /dev/null and b/res/drawable-xhdpi/portal_ring_inner_nolip.png differ diff --git a/res/drawable-xhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-xhdpi/portal_ring_inner_nolip_holo.png deleted file mode 100644 index eb2cf5f8b..000000000 Binary files a/res/drawable-xhdpi/portal_ring_inner_nolip_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/portal_ring_outer.png b/res/drawable-xhdpi/portal_ring_outer.png new file mode 100644 index 000000000..79c188873 Binary files /dev/null and b/res/drawable-xhdpi/portal_ring_outer.png differ diff --git a/res/drawable-xhdpi/portal_ring_outer_holo.png b/res/drawable-xhdpi/portal_ring_outer_holo.png deleted file mode 100644 index f32051dcf..000000000 Binary files a/res/drawable-xhdpi/portal_ring_outer_holo.png and /dev/null differ diff --git a/res/drawable-xhdpi/portal_ring_rest.png b/res/drawable-xhdpi/portal_ring_rest.png index ff0369a26..544a74f9f 100644 Binary files a/res/drawable-xhdpi/portal_ring_rest.png and b/res/drawable-xhdpi/portal_ring_rest.png differ diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png index e6cf0afde..bf7cc6b82 100644 Binary files a/res/drawable-xhdpi/widget_resize_frame.9.png and b/res/drawable-xhdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png index 99e9e78cd..96dee7f01 100644 Binary files a/res/drawable-xhdpi/widget_resize_shadow.9.png and b/res/drawable-xhdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-xxhdpi/ic_info_launcher.png b/res/drawable-xxhdpi/ic_info_launcher.png new file mode 100644 index 000000000..386d09147 Binary files /dev/null and b/res/drawable-xxhdpi/ic_info_launcher.png differ diff --git a/res/drawable-xxhdpi/ic_launcher_info_normal.png b/res/drawable-xxhdpi/ic_launcher_info_normal.png deleted file mode 100644 index c270be286..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_info_normal.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_remove_normal.png b/res/drawable-xxhdpi/ic_launcher_remove_normal.png deleted file mode 100644 index 5bc8f0c8d..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_remove_normal.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_launcher_uninstall_normal.png b/res/drawable-xxhdpi/ic_launcher_uninstall_normal.png deleted file mode 100644 index 4fce55b1e..000000000 Binary files a/res/drawable-xxhdpi/ic_launcher_uninstall_normal.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_remove_launcher.png b/res/drawable-xxhdpi/ic_remove_launcher.png new file mode 100644 index 000000000..7c28bb0bb Binary files /dev/null and b/res/drawable-xxhdpi/ic_remove_launcher.png differ diff --git a/res/drawable-xxhdpi/ic_uninstall_launcher.png b/res/drawable-xxhdpi/ic_uninstall_launcher.png new file mode 100644 index 000000000..872e82933 Binary files /dev/null and b/res/drawable-xxhdpi/ic_uninstall_launcher.png differ diff --git a/res/drawable-xxhdpi/portal_ring_inner.png b/res/drawable-xxhdpi/portal_ring_inner.png new file mode 100644 index 000000000..d088a172d Binary files /dev/null and b/res/drawable-xxhdpi/portal_ring_inner.png differ diff --git a/res/drawable-xxhdpi/portal_ring_inner_holo.png b/res/drawable-xxhdpi/portal_ring_inner_holo.png deleted file mode 100644 index 8cd6a5930..000000000 Binary files a/res/drawable-xxhdpi/portal_ring_inner_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/portal_ring_inner_nolip.png b/res/drawable-xxhdpi/portal_ring_inner_nolip.png new file mode 100644 index 000000000..0fad65629 Binary files /dev/null and b/res/drawable-xxhdpi/portal_ring_inner_nolip.png differ diff --git a/res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png deleted file mode 100644 index 0fad65629..000000000 Binary files a/res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/portal_ring_outer.png b/res/drawable-xxhdpi/portal_ring_outer.png new file mode 100644 index 000000000..45ac0406c Binary files /dev/null and b/res/drawable-xxhdpi/portal_ring_outer.png differ diff --git a/res/drawable-xxhdpi/portal_ring_outer_holo.png b/res/drawable-xxhdpi/portal_ring_outer_holo.png deleted file mode 100644 index 0aee4f027..000000000 Binary files a/res/drawable-xxhdpi/portal_ring_outer_holo.png and /dev/null differ diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png index 7a49d01da..7e189d442 100644 Binary files a/res/drawable-xxhdpi/widget_resize_frame.9.png and b/res/drawable-xxhdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png index ae0f564dd..41c448bbb 100644 Binary files a/res/drawable-xxhdpi/widget_resize_shadow.9.png and b/res/drawable-xxhdpi/widget_resize_shadow.9.png differ diff --git a/res/drawable-xxxhdpi/ic_info_launcher.png b/res/drawable-xxxhdpi/ic_info_launcher.png new file mode 100644 index 000000000..bf39e5be2 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_info_launcher.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher_info_normal.png b/res/drawable-xxxhdpi/ic_launcher_info_normal.png deleted file mode 100644 index 270e15d7f..000000000 Binary files a/res/drawable-xxxhdpi/ic_launcher_info_normal.png and /dev/null differ diff --git a/res/drawable-xxxhdpi/ic_launcher_remove_normal.png b/res/drawable-xxxhdpi/ic_launcher_remove_normal.png deleted file mode 100644 index ed96c55e8..000000000 Binary files a/res/drawable-xxxhdpi/ic_launcher_remove_normal.png and /dev/null differ diff --git a/res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png b/res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png deleted file mode 100644 index 61490b9ca..000000000 Binary files a/res/drawable-xxxhdpi/ic_launcher_uninstall_normal.png and /dev/null differ diff --git a/res/drawable-xxxhdpi/ic_remove_launcher.png b/res/drawable-xxxhdpi/ic_remove_launcher.png new file mode 100644 index 000000000..7043be015 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_remove_launcher.png differ diff --git a/res/drawable-xxxhdpi/ic_uninstall_launcher.png b/res/drawable-xxxhdpi/ic_uninstall_launcher.png new file mode 100644 index 000000000..77a3302f6 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_uninstall_launcher.png differ diff --git a/res/drawable-xxxhdpi/portal_ring_inner.png b/res/drawable-xxxhdpi/portal_ring_inner.png new file mode 100644 index 000000000..34a359997 Binary files /dev/null and b/res/drawable-xxxhdpi/portal_ring_inner.png differ diff --git a/res/drawable-xxxhdpi/portal_ring_inner_nolip.png b/res/drawable-xxxhdpi/portal_ring_inner_nolip.png new file mode 100644 index 000000000..8cebb3535 Binary files /dev/null and b/res/drawable-xxxhdpi/portal_ring_inner_nolip.png differ diff --git a/res/drawable-xxxhdpi/portal_ring_outer.png b/res/drawable-xxxhdpi/portal_ring_outer.png new file mode 100644 index 000000000..d2df32267 Binary files /dev/null and b/res/drawable-xxxhdpi/portal_ring_outer.png differ diff --git a/res/drawable-xxxhdpi/portal_ring_rest.png b/res/drawable-xxxhdpi/portal_ring_rest.png new file mode 100644 index 000000000..11e92eeb6 Binary files /dev/null and b/res/drawable-xxxhdpi/portal_ring_rest.png differ diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png index 9b711f2c5..cb609cef9 100644 Binary files a/res/drawable-xxxhdpi/widget_resize_frame.9.png and b/res/drawable-xxxhdpi/widget_resize_frame.9.png differ diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png index defb311ca..82c8b9c7c 100644 Binary files a/res/drawable-xxxhdpi/widget_resize_shadow.9.png and b/res/drawable-xxxhdpi/widget_resize_shadow.9.png differ diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml index d9a7671af..237af6890 100644 --- a/res/layout/folder_icon.xml +++ b/res/layout/folder_icon.xml @@ -26,7 +26,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:antialias="true" - android:src="@drawable/portal_ring_inner_holo"/> + android:src="@drawable/portal_ring_inner"/> Date: Mon, 1 Jun 2015 14:38:24 -0700 Subject: Ensuring that we use the screen width more optimally in AllApps. - Fixing issue with prediction bar height being calculated incorrectly - Fixing issue where the prediction bar divider was not drawn exactly between the bar and the first row of apps - Removing padding to allow scrollbars to reach full height Bug: 20222023 Bug: 21335377 Change-Id: I2c0614a36e2294d0d6184a6bff4847876ffe971e --- res/layout/all_apps_container.xml | 1 + res/layout/all_apps_icon.xml | 2 +- res/layout/all_apps_prediction_bar_icon.xml | 2 +- res/values/dimens.xml | 5 +-- src/com/android/launcher3/BubbleTextView.java | 1 - src/com/android/launcher3/DeviceProfile.java | 8 ++--- src/com/android/launcher3/FolderPagedView.java | 4 +-- .../android/launcher3/InvariantDeviceProfile.java | 36 +++++++++++++--------- src/com/android/launcher3/Utilities.java | 11 ++++++- .../launcher3/allapps/AllAppsContainerView.java | 19 ++++++------ .../launcher3/allapps/AllAppsGridAdapter.java | 5 ++- 11 files changed, 58 insertions(+), 36 deletions(-) diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml index a20ab46d8..fc77cd31f 100644 --- a/res/layout/all_apps_container.xml +++ b/res/layout/all_apps_container.xml @@ -38,6 +38,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/all_apps_search_bar_height" + android:paddingBottom="@dimen/all_apps_prediction_bar_bottom_padding" android:orientation="horizontal" android:descendantFocusability="afterDescendants" android:focusable="true" diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml index 10ed25c47..0985e95c0 100644 --- a/res/layout/all_apps_icon.xml +++ b/res/layout/all_apps_icon.xml @@ -20,7 +20,7 @@ android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="left|center_vertical" + android:layout_gravity="center" android:paddingTop="@dimen/all_apps_icon_top_bottom_padding" android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding" android:focusable="true" diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml index 1e75d14b6..5f63f6bdb 100644 --- a/res/layout/all_apps_prediction_bar_icon.xml +++ b/res/layout/all_apps_prediction_bar_icon.xml @@ -19,7 +19,7 @@ style="@style/Icon.AllApps" android:id="@+id/icon" android:layout_width="0dp" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:focusable="true" diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 63edd888b..2184eadb3 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -58,9 +58,10 @@ 8dp 24sp 52dp + 8dp 8dp - 18dp - 12dp + 24dp + 16dp 4dp -16dp diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f4e306af3..9a59feb04 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -52,7 +52,6 @@ public class BubbleTextView extends TextView { private static final float SHADOW_Y_OFFSET = 2.0f; private static final int SHADOW_LARGE_COLOUR = 0xDD000000; private static final int SHADOW_SMALL_COLOUR = 0xCC000000; - static final float PADDING_V = 3.0f; private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_ALL_APPS = 1; diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 82be409dd..9c59dab53 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -230,12 +230,12 @@ public class DeviceProfile { public boolean updateAppsViewNumCols(Resources res, int containerWidth) { int appsViewLeftMarginPx = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); - int allAppsCellPaddingPx = - res.getDimensionPixelSize(R.dimen.all_apps_icon_left_right_padding); + int allAppsCellWidthGap = + res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap); int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) / - (allAppsIconSizePx + 2 * allAppsCellPaddingPx); - int numPredictiveAppCols = isPhone ? 4 : numAppsCols; + (allAppsIconSizePx + allAppsCellWidthGap); + int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols); if ((numAppsCols != allAppsNumCols) || (numPredictiveAppCols != allAppsNumPredictiveCols)) { allAppsNumCols = numAppsCols; diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index 7d90ba2f7..b7a5aa8cf 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -87,8 +87,8 @@ public class FolderPagedView extends PagedView { LauncherAppState app = LauncherAppState.getInstance(); InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); - mMaxCountX = (int) profile.numFolderColumns; - mMaxCountY = (int) profile.numFolderRows; + mMaxCountX = profile.numFolderColumns; + mMaxCountY = profile.numFolderRows; mMaxItemsPerPage = mMaxCountX * mMaxCountY; diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index fcaf834a2..ae204c40c 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -56,6 +56,11 @@ public class InvariantDeviceProfile { public int numRows; public int numColumns; + /** + * The minimum number of predicted apps in all apps. + */ + int minAllAppsPredictionColumns; + /** * Number of icons per row and column in the folder. */ @@ -84,11 +89,12 @@ public class InvariantDeviceProfile { public InvariantDeviceProfile(InvariantDeviceProfile p) { this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns, - p.numFolderRows, p.numFolderColumns, p.iconSize, p.iconTextSize, p.numHotseatIcons, - p.hotseatIconSize, p.defaultLayoutId); + p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns, + p.iconSize, p.iconTextSize, p.numHotseatIcons, p.hotseatIconSize, + p.defaultLayoutId); } - InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, + InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc, float is, float its, float hs, float his, int dlId) { // Ensure that we have an odd number of hotseat items (since we need to place all apps) if (hs % 2 == 0) { @@ -102,6 +108,7 @@ public class InvariantDeviceProfile { numColumns = c; numFolderRows = fr; numFolderColumns = fc; + minAllAppsPredictionColumns = maapc; iconSize = is; iconTextSize = its; numHotseatIcons = hs; @@ -137,6 +144,7 @@ public class InvariantDeviceProfile { defaultLayoutId = closestProfile.defaultLayoutId; numFolderRows = closestProfile.numFolderRows; numFolderColumns = closestProfile.numFolderColumns; + minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns; iconSize = interpolatedDeviceProfileOut.iconSize; iconBitmapSize = Utilities.pxFromDp(iconSize, dm); @@ -166,30 +174,30 @@ public class InvariantDeviceProfile { // width, height, #rows, #columns, #folder rows, #folder columns, // iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId. predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + 255, 300, 2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); + 255, 400, 3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby", - 275, 420, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 275, 420, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby", - 255, 450, 3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 255, 450, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); + 296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4", - 335, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + 335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5", - 359, 567, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); + 359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone", - 406, 694, 5, 5, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); + 406, 694, 5, 5, 4, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); // The tablet profile is odd in that the landscape orientation // also includes the nav bar on the side predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7", - 575, 904, 5, 6, 4, 5, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); + 575, 904, 5, 6, 4, 5, 4, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); // Larger tablet profiles always have system bars on the top & bottom predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10", - 727, 1207, 5, 6, 4, 5, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); + 727, 1207, 5, 6, 4, 5, 4, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet", - 1527, 2527, 7, 7, 6, 6, 100, 20, 7, 72, R.xml.default_workspace_4x4)); + 1527, 2527, 7, 7, 6, 6, 4, 100, 20, 7, 72, R.xml.default_workspace_4x4)); return predefinedDeviceProfiles; } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index cffcd3445..0877b4527 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -54,7 +54,6 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.View; import android.widget.Toast; - import junit.framework.Assert; import java.io.ByteArrayOutputStream; @@ -625,6 +624,16 @@ public final class Utilities { return m.replaceAll("$1"); } + /** + * Calculates the height of a given string at a specific text size. + */ + public static float calculateTextHeight(float textSizePx) { + Paint p = new Paint(); + p.setTextSize(textSizePx); + Paint.FontMetrics fm = p.getFontMetrics(); + return -fm.top + fm.bottom; + } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static boolean isRtl(Resources res) { return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) && diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 9386500be..76ca9633d 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -20,6 +20,7 @@ import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; +import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -239,11 +240,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mLauncher = (Launcher) context; DeviceProfile grid = mLauncher.getDeviceProfile(); - mContainerInset = context.getResources().getDimensionPixelSize( - R.dimen.all_apps_container_inset); - mPredictionBarHeight = grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx + - grid.allAppsIconTextSizePx + - 2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_top_bottom_padding); + mContainerInset = res.getDimensionPixelSize(R.dimen.all_apps_container_inset); + mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx + + Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) + + 2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) + + res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding)); mLayoutInflater = LayoutInflater.from(context); @@ -497,11 +498,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc int startMargin = grid.isPhone ? mContentMarginStart : 0; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; if (isRtl) { - mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset, - inset + startMargin, inset); + mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), 0, + inset + startMargin, 0); } else { - mAppsRecyclerView.setPadding(inset + startMargin, inset, - inset + mAppsRecyclerView.getScrollbarWidth(), inset); + mAppsRecyclerView.setPadding(inset + startMargin, 0, + inset + mAppsRecyclerView.getScrollbarWidth(), 0); } // Update the header bar diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 307d9403d..102e7ae8d 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -139,7 +139,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter Date: Tue, 2 Jun 2015 11:24:28 -0700 Subject: Ensure that we use the system long press duration when dragging from AllApps. - Removing some old code out of LauncherAppState Bug: 21559400 Change-Id: I3c586094efb7ad8a17d2169bc8aaccf6b0df40a2 --- src/com/android/launcher3/BubbleTextView.java | 6 ++++++ src/com/android/launcher3/ButtonDropTarget.java | 9 +++----- .../android/launcher3/CheckLongPressHelper.java | 12 +++++++++-- src/com/android/launcher3/LauncherAppState.java | 24 +++++----------------- .../launcher3/allapps/AllAppsContainerView.java | 1 + .../launcher3/allapps/AllAppsGridAdapter.java | 3 +++ 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f4e306af3..6a13f3d69 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -184,6 +184,12 @@ public class BubbleTextView extends TextView { verifyHighRes(); } + /** + * Overrides the default long press timeout. + */ + public void setLongPressTimeout(int longPressTimeout) { + mLongPressHelper.setLongPressTimeout(longPressTimeout); + } @Override protected boolean setFrame(int left, int top, int right, int bottom) { diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 09a71b0cc..b7f89d02a 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -51,7 +51,6 @@ public abstract class ButtonDropTarget extends TextView protected Launcher mLauncher; private int mBottomDragPadding; - protected TextView mText; protected SearchDropTargetBar mSearchDropTargetBar; /** Whether this drop target is active for the current drag */ @@ -82,11 +81,9 @@ public abstract class ButtonDropTarget extends TextView mOriginalTextColor = getTextColors(); // Remove the text in the Phone UI in landscape - int orientation = getResources().getConfiguration().orientation; - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - if (!LauncherAppState.getInstance().isScreenLarge()) { - setText(""); - } + DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile(); + if (grid.isVerticalBarLayout()) { + setText(""); } } diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java index 381b678f9..483c62249 100644 --- a/src/com/android/launcher3/CheckLongPressHelper.java +++ b/src/com/android/launcher3/CheckLongPressHelper.java @@ -21,9 +21,11 @@ import android.view.View; import com.android.launcher3.util.Thunk; public class CheckLongPressHelper { + @Thunk View mView; @Thunk View.OnLongClickListener mListener; @Thunk boolean mHasPerformedLongPress; + private int mLongPressTimeout = 300; private CheckForLongPress mPendingCheckForLongPress; class CheckForLongPress implements Runnable { @@ -53,14 +55,20 @@ public class CheckLongPressHelper { mListener = listener; } + /** + * Overrides the default long press timeout. + */ + public void setLongPressTimeout(int longPressTimeout) { + mLongPressTimeout = longPressTimeout; + } + public void postCheckForLongPress() { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } - mView.postDelayed(mPendingCheckForLongPress, - LauncherAppState.getInstance().getLongPressTimeout()); + mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout); } public void cancelLongPress() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index afa09ec83..81d595aaf 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -25,6 +25,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.util.Log; +import android.view.ViewConfiguration; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; @@ -40,9 +41,6 @@ public class LauncherAppState { private final IconCache mIconCache; private final WidgetPreviewLoader mWidgetCache; - private final boolean mIsScreenLarge; - private final int mLongPressTimeout = 300; - private boolean mWallpaperChangedSinceLastCheck; private static WeakReference sLauncherProvider; @@ -86,8 +84,6 @@ public class LauncherAppState { MemoryTracker.startTrackingMe(sContext, "L"); } - // set sIsScreenXLarge and mScreenDensity *before* creating icon cache - mIsScreenLarge = isScreenLarge(sContext.getResources()); mInvariantDeviceProfile = new InvariantDeviceProfile(sContext); mIconCache = new IconCache(sContext, mInvariantDeviceProfile); mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache); @@ -149,6 +145,9 @@ public class LauncherAppState { return mModel; } + /** + * TODO(winsonc, hyunyoungs): We need to respect this + */ boolean shouldShowAppOrWidgetProvider(ComponentName componentName) { return mAppFilter == null || mAppFilter.shouldShowApp(componentName); } @@ -168,20 +167,7 @@ public class LauncherAppState { public WidgetPreviewLoader getWidgetCache() { return mWidgetCache; } - - public boolean isScreenLarge() { - return mIsScreenLarge; - } - - // Need a version that doesn't require an instance of LauncherAppState for the wallpaper picker - public static boolean isScreenLarge(Resources res) { - return res.getBoolean(R.bool.is_large_tablet); - } - - public int getLongPressTimeout() { - return mLongPressTimeout; - } - + public void onWallpaperChanged() { mWallpaperChangedSinceLastCheck = true; } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 9386500be..09f6a2c4d 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -432,6 +432,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc icon = (BubbleTextView) mLayoutInflater.inflate( R.layout.all_apps_prediction_bar_icon, mPredictionBarView, false); icon.setFocusable(true); + icon.setLongPressTimeout(ViewConfiguration.get(getContext()).getLongPressTimeout()); mPredictionBarView.addView(icon); } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 307d9403d..02a950fde 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -27,6 +27,7 @@ import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.TextView; import com.android.launcher3.AppInfo; @@ -418,6 +419,8 @@ class AllAppsGridAdapter extends RecyclerView.Adapter Date: Fri, 5 Jun 2015 11:14:01 -0700 Subject: Fixing crash in restore > LauncherAppState cannot be initialized during restore, as it is not called from a looper thread. Bug: 21275736 Change-Id: Ifdb3f9913fa2ee63a7e1566d0c5cfc6f72b4f41e --- src/com/android/launcher3/LauncherAppState.java | 1 - .../android/launcher3/LauncherBackupHelper.java | 61 +++++++--------------- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index afa09ec83..0950f9f8d 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Configuration; import android.content.res.Resources; import android.util.Log; diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index b40ace3fb..dc0bccc3c 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -146,7 +146,6 @@ public class LauncherBackupHelper implements BackupHelper { private long mLastBackupRestoreTime; private boolean mBackupDataWasUpdated; - private LauncherAppState mLauncherAppState; private IconCache mIconCache; private DeviceProfieData mDeviceProfileData; @@ -205,7 +204,11 @@ public class LauncherBackupHelper implements BackupHelper { return; } - lazyInitAppState(true /* noCreate */); + if (mDeviceProfileData == null) { + LauncherAppState app = LauncherAppState.getInstance(); + mDeviceProfileData = initDeviceProfileData(app.getInvariantDeviceProfile()); + mIconCache = app.getIconCache(); + } Log.v(TAG, "lastBackupTime = " + in.t); mKeys.clear(); @@ -240,7 +243,7 @@ public class LauncherBackupHelper implements BackupHelper { // Check if any metadata has changed mBackupDataWasUpdated = (in.profile == null) || !Arrays.equals(DeviceProfieData.toByteArray(in.profile), - DeviceProfieData.toByteArray(getDeviceProfieData())) + DeviceProfieData.toByteArray(mDeviceProfileData)) || (in.backupVersion != BACKUP_VERSION) || (in.appVersion != getAppVersion()); } @@ -268,8 +271,7 @@ public class LauncherBackupHelper implements BackupHelper { * to this device. */ private boolean isBackupCompatible(Journal oldState) { - DeviceProfieData currentProfile = getDeviceProfieData(); - + DeviceProfieData currentProfile = mDeviceProfileData; DeviceProfieData oldProfile = oldState.profile; if (oldProfile == null || oldProfile.desktopCols == 0) { @@ -302,7 +304,14 @@ public class LauncherBackupHelper implements BackupHelper { if (!restoreSuccessful) { return; } - lazyInitAppState(false /* noCreate */); + + if (mDeviceProfileData == null) { + // This call does not happen on a looper thread. So LauncherAppState + // can't be created . Instead initialize required dependencies directly. + InvariantDeviceProfile profile = new InvariantDeviceProfile(mContext); + mDeviceProfileData = initDeviceProfileData(profile); + mIconCache = new IconCache(mContext, profile); + } int dataSize = data.size(); if (mBuffer.length < dataSize) { @@ -379,7 +388,7 @@ public class LauncherBackupHelper implements BackupHelper { journal.key = mKeys.toArray(new BackupProtos.Key[mKeys.size()]); journal.appVersion = getAppVersion(); journal.backupVersion = BACKUP_VERSION; - journal.profile = getDeviceProfieData(); + journal.profile = mDeviceProfileData; return journal; } @@ -392,31 +401,6 @@ public class LauncherBackupHelper implements BackupHelper { } } - /** - * @return the current device profile information. - */ - private DeviceProfieData getDeviceProfieData() { - return mDeviceProfileData; - } - - private void lazyInitAppState(boolean noCreate) { - if (mLauncherAppState != null) { - return; - } - - if (noCreate) { - mLauncherAppState = LauncherAppState.getInstanceNoCreate(); - } else { - LauncherAppState.setApplicationContext(mContext); - mLauncherAppState = LauncherAppState.getInstance(); - } - - mIconCache = mLauncherAppState.getIconCache(); - InvariantDeviceProfile profile = mLauncherAppState.getInvariantDeviceProfile(); - - mDeviceProfileData = initDeviceProfileData(profile); - } - private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) { DeviceProfieData data = new DeviceProfieData(); data.desktopRows = profile.numRows; @@ -631,7 +615,6 @@ public class LauncherBackupHelper implements BackupHelper { private void backupWidgets(BackupDataOutput data) throws IOException { // persist static widget info that hasn't been persisted yet final ContentResolver cr = mContext.getContentResolver(); - final WidgetPreviewLoader previewLoader = mLauncherAppState.getWidgetCache(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; int backupWidgetCount = 0; @@ -644,7 +627,6 @@ public class LauncherBackupHelper implements BackupHelper { while(cursor.moveToNext()) { final long id = cursor.getLong(ID_INDEX); final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX); - final int spanX = cursor.getInt(SPANX_INDEX); final ComponentName provider = ComponentName.unflattenFromString(providerName); Key key = null; String backupKey = null; @@ -664,9 +646,7 @@ public class LauncherBackupHelper implements BackupHelper { if (backupWidgetCount < MAX_WIDGETS_PER_PASS) { if (DEBUG) Log.d(TAG, "saving widget " + backupKey); UserHandleCompat user = UserHandleCompat.myUserHandle(); - writeRowToBackup(key, - packWidget(dpi, previewLoader, mIconCache, provider, user), - data); + writeRowToBackup(key, packWidget(dpi, provider, user), data); mKeys.add(key); backupWidgetCount ++; } else { @@ -895,7 +875,7 @@ public class LauncherBackupHelper implements BackupHelper { UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle); values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber); - DeviceProfieData currentProfile = getDeviceProfieData(); + DeviceProfieData currentProfile = mDeviceProfileData; if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) { if (!TextUtils.isEmpty(favorite.appWidgetProvider)) { @@ -972,8 +952,7 @@ public class LauncherBackupHelper implements BackupHelper { } /** Serialize a widget for persistence, including a checksum wrapper. */ - private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache, - ComponentName provider, UserHandleCompat user) { + private Widget packWidget(int dpi, ComponentName provider, UserHandleCompat user) { final LauncherAppWidgetProviderInfo info = LauncherModel.getProviderInfo(mContext, provider, user); Widget widget = new Widget(); @@ -982,7 +961,7 @@ public class LauncherBackupHelper implements BackupHelper { widget.configure = info.configure != null; if (info.icon != 0) { widget.icon = new Resource(); - Drawable fullResIcon = iconCache.getFullResIcon(provider.getPackageName(), info.icon); + Drawable fullResIcon = mIconCache.getFullResIcon(provider.getPackageName(), info.icon); Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext); widget.icon.data = Utilities.flattenBitmap(icon); widget.icon.dpi = dpi; -- cgit v1.2.3 From f00d02b254d94eeaf52742e0640e9eacd63fafca Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 5 Jun 2015 13:30:19 -0700 Subject: Improve AyncTask throughput inside WidgetPreviewLoader b/21133230 > Synchronized block was creating a bottleneck for the AsyncTasks. > Remove calls that doesn't need to be synchronized outside synchronized block. > Also removed setAlpha call as after the bottleneck was removed, Inefficient alpha view usage alert started popping up in traceview. Due to less jankness, removing the fadein animation doesn't have any visible effect. Link to lock congestion visualization: https://x20web.corp.google.com/~hyunyoungs/no_crawl/traceview/traceview_lockcontention.html Result: gfx-avg-jank delta = "-1" Change-Id: If12817df0730f346cdba7e2f38f232eb9a4336c0 --- src/com/android/launcher3/WidgetPreviewLoader.java | 20 ++++++++++---------- src/com/android/launcher3/widget/WidgetCell.java | 4 +++- .../android/launcher3/widget/WidgetImageView.java | 8 ++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index a62177142..cfeced2df 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -586,26 +586,26 @@ public class WidgetPreviewLoader { protected Bitmap doInBackground(Void... params) { Bitmap unusedBitmap = null; + // If already cancelled before this gets to run in the background, then return early + if (isCancelled()) { + return null; + } synchronized (mUnusedBitmaps) { - // If already cancelled before this gets to run in the background, then return early - if (isCancelled()) { - return null; - } - // Check if we can use a bitmap + // Check if we can re-use a bitmap for (Bitmap candidate : mUnusedBitmaps) { if (candidate != null && candidate.isMutable() && candidate.getWidth() == mPreviewWidth && candidate.getHeight() == mPreviewHeight) { unusedBitmap = candidate; + mUnusedBitmaps.remove(unusedBitmap); break; } } + } - if (unusedBitmap == null) { - unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888); - } else { - mUnusedBitmaps.remove(unusedBitmap); - } + // creating a bitmap is expensive. Do not do this inside synchronized block. + if (unusedBitmap == null) { + unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888); } // If cancelled now, don't bother reading the preview from the DB if (isCancelled()) { diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 2714f5182..7496ea2ef 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -26,6 +26,7 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLayoutChangeListener; +import android.view.ViewPropertyAnimator; import android.widget.LinearLayout; import android.widget.TextView; @@ -175,7 +176,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { if (bitmap != null) { mWidgetImage.setBitmap(bitmap); mWidgetImage.setAlpha(0f); - mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS); + ViewPropertyAnimator anim = mWidgetImage.animate(); + anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); } } diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java index 6f8fd897b..b0fbe1ed9 100644 --- a/src/com/android/launcher3/widget/WidgetImageView.java +++ b/src/com/android/launcher3/widget/WidgetImageView.java @@ -64,6 +64,14 @@ public class WidgetImageView extends View { } } + /** + * Prevents the inefficient alpha view rendering. + */ + @Override + public boolean hasOverlappingRendering() { + return false; + } + private void updateDstRectF() { if (mBitmap.getWidth() > getWidth()) { float scale = ((float) getWidth()) / mBitmap.getWidth(); -- cgit v1.2.3 From c1f174a244367a756a0661b50ebd2557477c680b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 5 Jun 2015 13:41:23 -0700 Subject: Fixing touch feedback clipped on SetWallpaper button Bug: 19534312 Change-Id: I97dfd92c560caaf83e525f7e94286a7a77d2ba0b --- WallpaperPicker/res/values-v21/styles.xml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 WallpaperPicker/res/values-v21/styles.xml diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml new file mode 100644 index 000000000..04f39de73 --- /dev/null +++ b/WallpaperPicker/res/values-v21/styles.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file -- cgit v1.2.3 From 9e6344212a53cd1ae997cd85da5751585a9de504 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 8 Jun 2015 18:17:13 -0700 Subject: Make WidgetsRecyclerView not crash on MNC support lib Change-Id: Ia7bee39752ad0c0b58e74df4507caaf7a3cd8c29 --- src/com/android/launcher3/widget/WidgetsRecyclerView.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 4aa332380..9d265f87e 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -25,7 +25,6 @@ import android.view.View; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.Utilities; -import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.PackageItemInfo; @@ -37,7 +36,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; private Rect mBackgroundPadding = new Rect(); - private PackageItemInfo mLastPackageItemInfo; public WidgetsRecyclerView(Context context) { this(context, null); @@ -48,9 +46,15 @@ public class WidgetsRecyclerView extends BaseRecyclerView { } public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + // API 21 and below only support 3 parameter ctor. super(context, attrs, defStyleAttr); } + public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + this(context, attrs, defStyleAttr); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); -- cgit v1.2.3 From 3abd5488cca5b06c97251db453a5a6ed61e33ce8 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 8 Jun 2015 18:41:04 -0700 Subject: Catch TransactionTooLargeException when loading widget provider list b/19904873 Change-Id: Ic0d13ee7513731706bc0f9fde8502acba0899cb3 Note: fix for almonte will be separate due to diff btw the branches.. --- src/com/android/launcher3/LauncherModel.java | 63 +++++++++++++++++++--------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 10b8648de..a132e919a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -45,6 +45,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.os.TransactionTooLargeException; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; @@ -3284,27 +3285,51 @@ public class LauncherModel extends BroadcastReceiver public static List getWidgetProviders(Context context, boolean refresh) { - synchronized (sBgLock) { - if (sBgWidgetProviders == null || refresh) { - sBgWidgetProviders = new HashMap<>(); - AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); - LauncherAppWidgetProviderInfo info; - - List widgets = wm.getAllProviders(); - for (AppWidgetProviderInfo pInfo : widgets) { - info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); - UserHandleCompat user = wm.getUser(info); - sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); - } - - Collection customWidgets = Launcher.getCustomAppWidgets().values(); - for (CustomAppWidget widget : customWidgets) { - info = new LauncherAppWidgetProviderInfo(context, widget); - UserHandleCompat user = wm.getUser(info); - sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + ArrayList results = + new ArrayList(); + try { + synchronized (sBgLock) { + if (sBgWidgetProviders == null || refresh) { + HashMap tmpWidgetProviders + = new HashMap<>(); + AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); + LauncherAppWidgetProviderInfo info; + + List widgets = wm.getAllProviders(); + for (AppWidgetProviderInfo pInfo : widgets) { + info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); + UserHandleCompat user = wm.getUser(info); + tmpWidgetProviders.put(new ComponentKey(info.provider, user), info); + } + + Collection customWidgets = Launcher.getCustomAppWidgets().values(); + for (CustomAppWidget widget : customWidgets) { + info = new LauncherAppWidgetProviderInfo(context, widget); + UserHandleCompat user = wm.getUser(info); + tmpWidgetProviders.put(new ComponentKey(info.provider, user), info); + } + // Replace the global list at the very end, so that if there is an exception, + // previously loaded provider list is used. + sBgWidgetProviders = tmpWidgetProviders; + } + results.addAll(sBgWidgetProviders.values()); + return results; + } + } catch (Exception e) { + if (e.getCause() instanceof TransactionTooLargeException) { + // the returned value may be incomplete and will not be refreshed until the next + // time Launcher starts. + // TODO: after figuring out a repro step, introduce a dirty bit to check when + // onResume is called to refresh the widget provider list. + synchronized (sBgLock) { + if (sBgWidgetProviders != null) { + results.addAll(sBgWidgetProviders.values()); + } + return results; } + } else { + throw e; } - return new ArrayList(sBgWidgetProviders.values()); } } -- cgit v1.2.3 From 45478022977de9025dfc887cc1507d90d15febf8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 8 Jun 2015 16:52:41 -0700 Subject: Tying accessibility drag lifecycle to that of dragController Bug: 20865291 Change-Id: I1e0aceb20efcf4c32d76a656c499a1a4a5a32a65 --- src/com/android/launcher3/Launcher.java | 6 ++---- .../LauncherAccessibilityDelegate.java | 24 +++++++++++----------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 076a6e657..796de3fa0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -98,7 +98,6 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; -import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AppSearchManager; import com.android.launcher3.compat.AppWidgetManagerCompat; @@ -2470,9 +2469,8 @@ public class Launcher extends Activity return; } - LauncherAccessibilityDelegate delegate = - LauncherAppState.getInstance().getAccessibilityDelegate(); - if (delegate != null && delegate.onBackPressed()) { + if (mDragController.isDragging()) { + mDragController.cancelDrag(); return; } diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 3c49ccc41..fe7b25edd 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -20,6 +20,8 @@ import com.android.launcher3.AppInfo; import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DragController.DragListener; +import com.android.launcher3.DragSource; import com.android.launcher3.Folder; import com.android.launcher3.FolderInfo; import com.android.launcher3.InfoDropTarget; @@ -39,7 +41,7 @@ import com.android.launcher3.util.Thunk; import java.util.ArrayList; @TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class LauncherAccessibilityDelegate extends AccessibilityDelegate { +public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener { private static final String TAG = "LauncherAccessibilityDelegate"; @@ -328,7 +330,6 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc); mLauncher.getDragController().completeAccessibleDrag(loc); - endAccessibleDrag(); if (!TextUtils.isEmpty(confirmation)) { announceConfirmation(confirmation); } @@ -366,22 +367,21 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { } mDragSource.enableAccessibleDrag(true); mDragSource.startDrag(cellInfo, true); - } - public boolean onBackPressed() { - if (isInAccessibleDrag()) { - cancelAccessibleDrag(); - return true; + if (mLauncher.getDragController().isDragging()) { + mLauncher.getDragController().addDragListener(this); } - return false; } - private void cancelAccessibleDrag() { - mLauncher.getDragController().cancelDrag(); - endAccessibleDrag(); + + @Override + public void onDragStart(DragSource source, Object info, int dragAction) { + // No-op } - private void endAccessibleDrag() { + @Override + public void onDragEnd() { + mLauncher.getDragController().removeDragListener(this); mDragInfo = null; if (mDragSource != null) { mDragSource.enableAccessibleDrag(false); -- cgit v1.2.3 From 0c979d1f2fda45b9d098f9399e1079e6bc22c2b0 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 8 Jun 2015 18:41:04 -0700 Subject: Catch TransactionTooLargeException when loading widget provider list b/19904873 Change-Id: Ic0d13ee7513731706bc0f9fde8502acba0899cb3 Note: fix for almonte will be separate due to diff btw the branches.. (cherry picked from commit 3abd5488cca5b06c97251db453a5a6ed61e33ce8) --- src/com/android/launcher3/LauncherModel.java | 59 ++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 2e5d4e9b3..ee35457b7 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -47,6 +47,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.os.TransactionTooLargeException; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; @@ -3338,27 +3339,51 @@ public class LauncherModel extends BroadcastReceiver public static List getWidgetProviders(Context context, boolean refresh) { - synchronized (sBgLock) { - if (sBgWidgetProviders == null || refresh) { - sBgWidgetProviders = new HashMap<>(); - AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); - LauncherAppWidgetProviderInfo info; + ArrayList results = + new ArrayList(); + try { + synchronized (sBgLock) { + if (sBgWidgetProviders == null || refresh) { + HashMap tmpWidgetProviders + = new HashMap<>(); + AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); + LauncherAppWidgetProviderInfo info; + + List widgets = wm.getAllProviders(); + for (AppWidgetProviderInfo pInfo : widgets) { + info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); + UserHandleCompat user = wm.getUser(info); + tmpWidgetProviders.put(new ComponentKey(info.provider, user), info); + } - List widgets = wm.getAllProviders(); - for (AppWidgetProviderInfo pInfo : widgets) { - info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo); - UserHandleCompat user = wm.getUser(info); - sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + Collection customWidgets = Launcher.getCustomAppWidgets().values(); + for (CustomAppWidget widget : customWidgets) { + info = new LauncherAppWidgetProviderInfo(context, widget); + UserHandleCompat user = wm.getUser(info); + tmpWidgetProviders.put(new ComponentKey(info.provider, user), info); + } + // Replace the global list at the very end, so that if there is an exception, + // previously loaded provider list is used. + sBgWidgetProviders = tmpWidgetProviders; } - - Collection customWidgets = Launcher.getCustomAppWidgets().values(); - for (CustomAppWidget widget : customWidgets) { - info = new LauncherAppWidgetProviderInfo(context, widget); - UserHandleCompat user = wm.getUser(info); - sBgWidgetProviders.put(new ComponentKey(info.provider, user), info); + results.addAll(sBgWidgetProviders.values()); + return results; + } + } catch (Exception e) { + if (e.getCause() instanceof TransactionTooLargeException) { + // the returned value may be incomplete and will not be refreshed until the next + // time Launcher starts. + // TODO: after figuring out a repro step, introduce a dirty bit to check when + // onResume is called to refresh the widget provider list. + synchronized (sBgLock) { + if (sBgWidgetProviders != null) { + results.addAll(sBgWidgetProviders.values()); + } + return results; } + } else { + throw e; } - return new ArrayList(sBgWidgetProviders.values()); } } -- cgit v1.2.3 From b2b02b9bd2e0b0da7373125c06f6d67d9758d8fe Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 10 Jun 2015 18:40:05 -0700 Subject: Fix a small UI nit with screen reordering -> When in overview mode, flinging the pages can leave the scroller running (invisibly) for much additional time, since the scroller fling bounds far exceed the alloawble scroll bounds (in order to achieve a hard wall type effect) -> When this is happening, user couldn't pick up a page for reordering -> Ended the scroller early in this case to avoid the problem Change-Id: I8b6f140d9a87bb742e57625e90ca7d76a2158e28 --- src/com/android/launcher3/PagedView.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 18832c680..4a85b448f 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -567,6 +567,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public void scrollTo(int x, int y) { // In free scroll mode, we clamp the scrollX if (mFreeScroll) { + // If the scroller is trying to move to a location beyond the maximum allowed + // in the free scroll mode, we make sure to end the scroll operation. + if (!mScroller.isFinished() && + (x > mFreeScrollMaxScrollX || x < mFreeScrollMinScrollX)) { + forceFinishScroller(); + } + x = Math.min(x, mFreeScrollMaxScrollX); x = Math.max(x, mFreeScrollMinScrollX); } -- cgit v1.2.3 From 7779d62308b87ca26e3be47df836893f6f7693ec Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 11 Jun 2015 16:18:39 -0700 Subject: Using content provider to update launcher settings > Removing cross process preference file > Removed broadcast listener management for settings changes > Defining content provider method to get/set laucnehr preferences Change-Id: Ida36eac0ab17c1d48fedc9404817a53a89b36c4f --- AndroidManifest.xml | 5 --- res/values/strings.xml | 3 -- src/com/android/launcher3/Launcher.java | 50 ++++++++-------------- src/com/android/launcher3/LauncherAppState.java | 3 +- src/com/android/launcher3/LauncherFiles.java | 5 +-- src/com/android/launcher3/LauncherProvider.java | 34 +++++++++++++++ .../launcher3/LauncherProviderChangeListener.java | 2 + src/com/android/launcher3/LauncherSettings.java | 15 +++++++ src/com/android/launcher3/SettingsActivity.java | 41 +++++++++++------- src/com/android/launcher3/Utilities.java | 7 +-- 10 files changed, 97 insertions(+), 68 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 5f9d7f436..03e9bbf8f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -44,10 +44,6 @@ android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" android:protectionLevel="signature" /> - @@ -66,7 +62,6 @@ - com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS - - com.android.launcher3.permission.RECEIVE_UPDATE_ORIENTATION_BROADCASTS - com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 076a6e657..ba9a96e77 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -399,29 +399,8 @@ public class Launcher extends Activity } private Stats mStats; - FocusIndicatorView mFocusHandler; - - @Thunk boolean mRotationEnabled = false; - private boolean mScreenOrientationSettingReceiverRegistered = false; - - final private BroadcastReceiver mScreenOrientationSettingReceiver = - new BroadcastReceiver() { - @Thunk Runnable mUpdateOrientationRunnable = new Runnable() { - public void run() { - setOrientation(); - } - }; - - @Override - public void onReceive(Context context, Intent intent) { - mRotationEnabled = intent.getBooleanExtra( - Utilities.SCREEN_ROTATION_SETTING_EXTRA, false); - if (!waitUntilResume(mUpdateOrientationRunnable, true)) { - setOrientation(); - } - } - }; + private boolean mRotationEnabled = false; @Thunk void setOrientation() { if (mRotationEnabled) { @@ -432,6 +411,12 @@ public class Launcher extends Activity } } + private Runnable mUpdateOrientationRunnable = new Runnable() { + public void run() { + setOrientation(); + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { if (DEBUG_STRICT_MODE) { @@ -531,12 +516,6 @@ public class Launcher extends Activity // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { - String updateOrientationBroadcastPermission = getResources().getString( - R.string.receive_update_orientation_broadcasts_permission); - registerReceiver(mScreenOrientationSettingReceiver, - new IntentFilter(Utilities.SCREEN_ROTATION_SETTING_INTENT), - updateOrientationBroadcastPermission, null); - mScreenOrientationSettingReceiverRegistered = true; mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); } @@ -563,6 +542,16 @@ public class Launcher extends Activity } } + @Override + public void onSettingsChanged(String settings, boolean value) { + if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(settings)) { + mRotationEnabled = value; + if (!waitUntilResume(mUpdateOrientationRunnable, true)) { + mUpdateOrientationRunnable.run(); + } + } + } + private LauncherCallbacks mLauncherCallbacks; public void onPostCreate(Bundle savedInstanceState) { @@ -2031,11 +2020,6 @@ public class Launcher extends Activity public void onDestroy() { super.onDestroy(); - if (mScreenOrientationSettingReceiverRegistered) { - unregisterReceiver(mScreenOrientationSettingReceiver); - mScreenOrientationSettingReceiverRegistered = false; - } - // Remove all pending runnables mHandler.removeMessages(ADVANCE_MSG); mHandler.removeMessages(0); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 76ad8c164..540bdf814 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -21,10 +21,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.util.Log; -import android.view.ViewConfiguration; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; @@ -48,6 +46,7 @@ public class LauncherAppState { private static LauncherAppState INSTANCE; private InvariantDeviceProfile mInvariantDeviceProfile; + private LauncherAccessibilityDelegate mAccessibilityDelegate; public static LauncherAppState getInstance() { diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index ec4e4f942..c08cd0bf5 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -26,8 +26,6 @@ public class LauncherFiles { public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; public static final String APP_ICONS_DB = "app_icons.db"; - public static final String ROTATION_PREF_FILE = "com.android.launcher3.rotation.prefs"; - public static final List ALL_FILES = Collections.unmodifiableList(Arrays.asList( DEFAULT_WALLPAPER_THUMBNAIL, DEFAULT_WALLPAPER_THUMBNAIL_OLD, @@ -37,8 +35,7 @@ public class LauncherFiles { WALLPAPER_IMAGES_DB, WIDGET_PREVIEWS_DB, MANAGED_USER_PREFERENCES_KEY, - APP_ICONS_DB, - ROTATION_PREF_FILE)); + APP_ICONS_DB)); // TODO: Delete these files on upgrade public static final List OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList( diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index b5901265c..cb808c22b 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -39,8 +39,10 @@ import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteStatement; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Process; import android.os.StrictMode; import android.os.UserManager; import android.text.TextUtils; @@ -237,6 +239,38 @@ public class LauncherProvider extends ContentProvider { return count; } + @Override + public Bundle call(String method, String arg, Bundle extras) { + if (Binder.getCallingUid() != Process.myUid()) { + return null; + } + + switch (method) { + case LauncherSettings.Settings.METHOD_GET_BOOLEAN: { + Bundle result = new Bundle(); + result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, + getContext().getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE) + .getBoolean(arg, extras.getBoolean( + LauncherSettings.Settings.EXTRA_DEFAULT_VALUE))); + return result; + } + case LauncherSettings.Settings.METHOD_SET_BOOLEAN: { + boolean value = extras.getBoolean(LauncherSettings.Settings.EXTRA_VALUE); + getContext().getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE) + .edit().putBoolean(arg, value).apply(); + if (mListener != null) { + mListener.onSettingsChanged(arg, value); + } + Bundle result = new Bundle(); + result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value); + return result; + } + } + return null; + } + private void notifyListeners() { // always notify the backup agent LauncherBackupAgentHelper.dataChanged(getContext()); diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java index 0de96fbc4..5b5c6c5ab 100644 --- a/src/com/android/launcher3/LauncherProviderChangeListener.java +++ b/src/com/android/launcher3/LauncherProviderChangeListener.java @@ -8,4 +8,6 @@ package com.android.launcher3; public interface LauncherProviderChangeListener { public void onLauncherProviderChange(); + + public void onSettingsChanged(String settings, boolean value); } diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 90e60e450..afdb3dd00 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -305,4 +305,19 @@ public class LauncherSettings { */ static final String OPTIONS = "options"; } + + /** + * Launcher settings + */ + public static final class Settings { + + public static final Uri CONTENT_URI = Uri.parse("content://" + + ProviderConfig.AUTHORITY + "/settings"); + + public static final String METHOD_GET_BOOLEAN = "get_boolean_setting"; + public static final String METHOD_SET_BOOLEAN = "set_boolean_setting"; + + public static final String EXTRA_VALUE = "value"; + public static final String EXTRA_DEFAULT_VALUE = "default_value"; + } } diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index 27763f545..dab71c862 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -17,12 +17,11 @@ package com.android.launcher3; import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.os.Bundle; import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceFragment; -import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; /** * Settings activity for Launcher. Currently implements the following setting: Allow rotation @@ -41,26 +40,36 @@ public class SettingsActivity extends Activity { /** * This fragment shows the launcher preferences. */ - @SuppressWarnings("WeakerAccess") - public static class LauncherSettingsFragment extends PreferenceFragment { + public static class LauncherSettingsFragment extends PreferenceFragment + implements OnPreferenceChangeListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS); - getPreferenceManager().setSharedPreferencesName(LauncherFiles.ROTATION_PREF_FILE); addPreferencesFromResource(R.xml.launcher_preferences); + + SwitchPreference pref = (SwitchPreference) findPreference( + Utilities.ALLOW_ROTATION_PREFERENCE_KEY); + pref.setPersistent(false); + + Bundle extras = new Bundle(); + extras.putBoolean(LauncherSettings.Settings.EXTRA_DEFAULT_VALUE, false); + Bundle value = getActivity().getContentResolver().call( + LauncherSettings.Settings.CONTENT_URI, + LauncherSettings.Settings.METHOD_GET_BOOLEAN, + Utilities.ALLOW_ROTATION_PREFERENCE_KEY, extras); + pref.setChecked(value.getBoolean(LauncherSettings.Settings.EXTRA_VALUE)); + + pref.setOnPreferenceChangeListener(this); } @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, - Preference preference) { - boolean allowRotation = getPreferenceManager().getSharedPreferences().getBoolean( - Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false); - Intent rotationSetting = new Intent(Utilities.SCREEN_ROTATION_SETTING_INTENT); - String launchBroadcastPermission = getResources().getString( - R.string.receive_update_orientation_broadcasts_permission); - rotationSetting.putExtra(Utilities.SCREEN_ROTATION_SETTING_EXTRA, allowRotation); - getActivity().sendBroadcast(rotationSetting, launchBroadcastPermission); + public boolean onPreferenceChange(Preference preference, Object newValue) { + Bundle extras = new Bundle(); + extras.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, (Boolean) newValue); + getActivity().getContentResolver().call( + LauncherSettings.Settings.CONTENT_URI, + LauncherSettings.Settings.METHOD_SET_BOOLEAN, + preference.getKey(), extras); return true; } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 0cd980cb2..0f52cba2b 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -92,17 +92,14 @@ public final class Utilities { private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; - public static final String SCREEN_ROTATION_SETTING_INTENT = - "come.android.launcher3.SCREEN_ORIENTATION_PREF_CHANGED"; - public static final String SCREEN_ROTATION_SETTING_EXTRA = "screenRotationPref"; public static boolean isPropertyEnabled(String propertyName) { return Log.isLoggable(propertyName, Log.VERBOSE); } public static boolean isAllowRotationPrefEnabled(Context context) { - SharedPreferences sharedPrefs = context.getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, - Context.MODE_MULTI_PROCESS); + SharedPreferences sharedPrefs = context.getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); return sForceEnableRotation || allowRotationPref; } -- cgit v1.2.3 From fd98e3e3fcceffbba06fa7e10da5d521831714ba Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 12 Jun 2015 12:20:34 -0700 Subject: Fixing proguard stripping animation method. --- proguard.flags | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proguard.flags b/proguard.flags index 5e2c384ce..7ec488b2d 100644 --- a/proguard.flags +++ b/proguard.flags @@ -44,7 +44,7 @@ public void setBrightness(int); } --keep class com.android.launcher3.AppsContainerRecyclerView { +-keep class com.android.launcher3.BaseRecyclerView { public void setFastScrollerAlpha(float); public float getFastScrollerAlpha(); } -- cgit v1.2.3 From d7da55fc3230df43b3b0090960b3184e88309ff5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 12 Jun 2015 12:25:45 -0700 Subject: Removing developer options check for app info target Bug: 21626561 Change-Id: Ic65155ca5b1e5268ccdf2b20295a5067d5d3fa5d --- src/com/android/launcher3/InfoDropTarget.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index 0ede2fc11..d93cdcc1b 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -16,11 +16,8 @@ package com.android.launcher3; -import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; -import android.os.Build; -import android.provider.Settings; import android.util.AttributeSet; import com.android.launcher3.compat.UserHandleCompat; @@ -70,18 +67,8 @@ public class InfoDropTarget extends ButtonDropTarget { return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info); } - @SuppressWarnings("deprecation") - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static boolean supportsDrop(Context context, Object info) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return (Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) && - (info instanceof AppInfo || info instanceof PendingAddItemInfo); - } else { - return (Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) && - (info instanceof AppInfo || info instanceof PendingAddItemInfo); - } + return info instanceof AppInfo || info instanceof PendingAddItemInfo; } @Override -- cgit v1.2.3 From b1622cc30f2fd9b579cb918083e063685950df92 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 10 Jun 2015 16:00:42 -0700 Subject: Deleting empty folders based on DB state > Previously folders were getting deleted based on in-memory loader state. If for some reason, an item failed to load, we would delete the folder from DB as well. Bug: 21354058 Change-Id: I5318ee8a99afa9cafd93ed2b9ef0e155f502a41b --- src/com/android/launcher3/IconCache.java | 2 +- src/com/android/launcher3/LauncherModel.java | 77 ++++++---------------- src/com/android/launcher3/LauncherProvider.java | 37 +++++++++++ src/com/android/launcher3/Utilities.java | 6 ++ .../launcher3/compat/LauncherAppsCompatVL.java | 2 +- .../launcher3/compat/UserManagerCompatVL.java | 2 +- 6 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 3165337c2..432b33c6b 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -301,7 +301,7 @@ public class IconCache { c.close(); if (!itemsToRemove.isEmpty()) { mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, - IconDB.COLUMN_ROWID + " IN ( " + TextUtils.join(", ", itemsToRemove) +" )", + Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index a132e919a..e1425121a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -20,7 +20,6 @@ import android.app.SearchManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentValues; @@ -43,7 +42,6 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Parcelable; import android.os.Process; -import android.os.RemoteException; import android.os.SystemClock; import android.os.TransactionTooLargeException; import android.provider.BaseColumns; @@ -1866,6 +1864,7 @@ public class LauncherModel extends BroadcastReceiver int itemType = c.getInt(itemTypeIndex); boolean restored = 0 != c.getInt(restoredIndex); boolean allowMissingTarget = false; + container = c.getInt(containerIndex); switch (itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: @@ -2004,7 +2003,6 @@ public class LauncherModel extends BroadcastReceiver continue; } - container = c.getInt(containerIndex); boolean useLowResIcon = container >= 0 && c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW; @@ -2111,7 +2109,6 @@ public class LauncherModel extends BroadcastReceiver // Do not trim the folder label, as is was set by the user. folderInfo.title = c.getString(titleIndex); folderInfo.id = id; - container = c.getInt(containerIndex); folderInfo.container = container; folderInfo.screenId = c.getInt(screenIndex); folderInfo.cellX = c.getInt(cellXIndex); @@ -2233,7 +2230,6 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo.spanX = c.getInt(spanXIndex); appWidgetInfo.spanY = c.getInt(spanYIndex); - container = c.getInt(containerIndex); if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP && container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) { Log.e(TAG, "Widget found where container != " + @@ -2241,7 +2237,7 @@ public class LauncherModel extends BroadcastReceiver continue; } - appWidgetInfo.container = c.getInt(containerIndex); + appWidgetInfo.container = container; // check & update map of what's occupied if (!checkItemPlacement(occupied, appWidgetInfo)) { itemsToRemove.add(id); @@ -2283,56 +2279,32 @@ public class LauncherModel extends BroadcastReceiver return; } - // Remove any empty folder - LongArrayMap emptyFolders = sBgFolders.clone(); - for (ItemInfo item: sBgItemsIdMap) { - long container = item.container; - if (emptyFolders.containsKey(container)) { - emptyFolders.remove(container); - } - } - for (FolderInfo folder : emptyFolders) { - long folderId = folder.id; - sBgFolders.remove(folderId); - sBgItemsIdMap.remove(folderId); - sBgWorkspaceItems.remove(folder); - itemsToRemove.add(folderId); - } - if (itemsToRemove.size() > 0) { - ContentProviderClient client = contentResolver.acquireContentProviderClient( - contentUri); // Remove dead items - for (long id : itemsToRemove) { - if (DEBUG_LOADERS) { - Log.d(TAG, "Removed id = " + id); - } - // Don't notify content observers - try { - client.delete(LauncherSettings.Favorites.getContentUri(id), null, null); - } catch (RemoteException e) { - Log.w(TAG, "Could not remove id = " + id); - } + contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI, + Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, itemsToRemove), null); + if (DEBUG_LOADERS) { + Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, itemsToRemove)); + } + + // Remove any empty folder + for (long folderId : LauncherAppState.getLauncherProvider() + .deleteEmptyFolders()) { + sBgWorkspaceItems.remove(sBgFolders.get(folderId)); + sBgFolders.remove(folderId); + sBgItemsIdMap.remove(folderId); } } if (restoredRows.size() > 0) { - ContentProviderClient updater = contentResolver.acquireContentProviderClient( - contentUri); // Update restored items that no longer require special handling - try { - StringBuilder selectionBuilder = new StringBuilder(); - selectionBuilder.append(LauncherSettings.Favorites._ID); - selectionBuilder.append(" IN ("); - selectionBuilder.append(TextUtils.join(", ", restoredRows)); - selectionBuilder.append(")"); - ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.RESTORED, 0); - updater.update(LauncherSettings.Favorites.CONTENT_URI, - values, selectionBuilder.toString(), null); - } catch (RemoteException e) { - Log.w(TAG, "Could not update restored rows"); - } + ContentValues values = new ContentValues(); + values.put(LauncherSettings.Favorites.RESTORED, 0); + contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values, + Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, restoredRows), null); } if (!isSdCardReady && !sPendingPackages.isEmpty()) { @@ -2342,9 +2314,6 @@ public class LauncherModel extends BroadcastReceiver } sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext)); - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + - TextUtils.join(", ", sBgWorkspaceScreens), true); // Remove any empty screens ArrayList unusedScreens = new ArrayList(sBgWorkspaceScreens); @@ -2358,10 +2327,6 @@ public class LauncherModel extends BroadcastReceiver // If there are any empty screens remove them, and update. if (unusedScreens.size() != 0) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " + - TextUtils.join(", ", unusedScreens), true); - sBgWorkspaceScreens.removeAll(unusedScreens); updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index cb808c22b..9b52a80c9 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -62,6 +62,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; @@ -271,6 +272,42 @@ public class LauncherProvider extends ContentProvider { return null; } + /** + * Deletes any empty folder from the DB. + * @return Ids of deleted folders. + */ + public List deleteEmptyFolders() { + ArrayList folderIds = new ArrayList(); + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + // Select folders whose id do not match any container value. + String selection = LauncherSettings.Favorites.ITEM_TYPE + " = " + + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND " + + LauncherSettings.Favorites._ID + " NOT IN (SELECT " + + LauncherSettings.Favorites.CONTAINER + " FROM " + + TABLE_FAVORITES + ")"; + Cursor c = db.query(TABLE_FAVORITES, + new String[] {LauncherSettings.Favorites._ID}, + selection, null, null, null, null); + while (c.moveToNext()) { + folderIds.add(c.getLong(0)); + } + c.close(); + if (folderIds.size() > 0) { + db.delete(TABLE_FAVORITES, Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, folderIds), null); + } + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + folderIds.clear(); + } finally { + db.endTransaction(); + } + return folderIds; + } + private void notifyListeners() { // always notify the backup agent LauncherBackupAgentHelper.dataChanged(getContext()); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 0f52cba2b..6c300c524 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -54,12 +54,14 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.View; import android.widget.Toast; + import junit.framework.Assert; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -676,4 +678,8 @@ public final class Utilities { return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics)); } + + public static String createDbSelectionQuery(String columnName, Iterable values) { + return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values)); + } } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java index c862ffc0a..fbf91b548 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java @@ -51,7 +51,7 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat { List list = mLauncherApps.getActivityList(packageName, user.getUser()); if (list.size() == 0) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } ArrayList compatList = new ArrayList(list.size()); diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index f6434c5b2..dd7a72617 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -46,7 +46,7 @@ public class UserManagerCompatVL extends UserManagerCompatV17 { public List getUserProfiles() { List users = mUserManager.getUserProfiles(); if (users == null) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } ArrayList compatUsers = new ArrayList( users.size()); -- cgit v1.2.3 From 4eda8048c1705a716e8f2b9ddaf026a8a8b03863 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 12 Jun 2015 12:54:31 -0700 Subject: Fixing issue with click shadow not being drawn correctly. - It was due to the view parent (prediction bar) which has its own translation. --- src/com/android/launcher3/ClickShadowView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java index 42fafe275..e31d7f7f6 100644 --- a/src/com/android/launcher3/ClickShadowView.java +++ b/src/com/android/launcher3/ClickShadowView.java @@ -96,12 +96,14 @@ public class ClickShadowView extends View { float drawableWidth = view.getIcon().getBounds().width(); setTranslationX(leftShift + + viewParent.getTranslationX() + view.getCompoundPaddingLeft() * view.getScaleX() + (iconHSpace - drawableWidth) * view.getScaleX() / 2 /* drawable gap */ + iconWidth * (1 - view.getScaleX()) / 2 /* gap due to scale */ - mShadowPadding /* extra shadow size */ ); setTranslationY(topShift + + viewParent.getTranslationY() + view.getPaddingTop() * view.getScaleY() /* drawable gap */ + view.getHeight() * (1 - view.getScaleY()) / 2 /* gap due to scale */ - mShadowPadding /* extra shadow size */ -- cgit v1.2.3 From 4b9f9791475bb9e1f28b6363299e927e676cf01d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 12 Jun 2015 18:02:52 -0700 Subject: Fixing regression in fast scroller animation. - Fast scroller was not fading out since we were missing the getter for the fast scroller alpha (so the initial alpha to animate from was wrong). Change-Id: I2fd13eee3deb084ba37cf4a849e2378f23758391 --- src/com/android/launcher3/BaseRecyclerView.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index a207d9a12..3a741f2d3 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -289,6 +289,13 @@ public class BaseRecyclerView extends RecyclerView invalidateFastScroller(mFastScrollerBounds); } + /** + * Returns the fast scroller alpha. + */ + public float getFastScrollerAlpha() { + return mFastScrollAlpha; + } + /** * Maps the touch (from 0..1) to the adapter position that should be visible. *

Override in each subclass of this base class. -- cgit v1.2.3 From e863fed1400d00fab11a61c5b4b989d86ca55386 Mon Sep 17 00:00:00 2001 From: Rahul Chaturvedi Date: Mon, 15 Jun 2015 14:09:42 -0400 Subject: Read rotation setting correctly from the wallpaper picker. If reading the rotation setting without the multi-process flag, the WallpaperPickerActivity usually picks up a cached value. Specifying the multi-process flag during the read fixes this and the wallpaper picker always has the correct rotation setting. Change-Id: Ic3639f8cd694674e92c8940b753c6bc30486076d --- .../src/com/android/launcher3/WallpaperPickerActivity.java | 2 +- src/com/android/launcher3/Launcher.java | 2 +- src/com/android/launcher3/Utilities.java | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 94159416a..d9bfc30eb 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -1147,6 +1147,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return true; // Check if the user has specifically enabled rotation via preferences. - return Utilities.isAllowRotationPrefEnabled(getApplicationContext()); + return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true); } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d52191bf1..191fdf457 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -515,7 +515,7 @@ public class Launcher extends Activity // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { - mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); + mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false); } // On large interfaces, or on devices that a user has specifically enabled screen rotation, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 0f52cba2b..2d8a1b1cf 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -97,9 +97,10 @@ public final class Utilities { return Log.isLoggable(propertyName, Log.VERBOSE); } - public static boolean isAllowRotationPrefEnabled(Context context) { + public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) { SharedPreferences sharedPrefs = context.getSharedPreferences( - LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE | (multiProcess ? + Context.MODE_MULTI_PROCESS : 0)); boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); return sForceEnableRotation || allowRotationPref; } -- cgit v1.2.3 From 5b139a5ca768a87e98bfbd547ecac5a10e1cf023 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Mon, 15 Jun 2015 11:16:18 -0700 Subject: Remove inifinite recursion from PagedView -> thanks adamp@ Change-Id: I36836357f5f194ac69530475b124595174c8b636 --- src/com/android/launcher3/PagedView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 4a85b448f..3d00034cc 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1045,7 +1045,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public void removeViewAt(int index) { // XXX: We should find a better way to hook into this before the view // gets removed form its parent... - removeViewAt(index); + removeMarkerForView(index); super.removeViewAt(index); } @Override -- cgit v1.2.3 From 41e3369d5fffb2e8dfa33bfcb14386ad339b0f2f Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 15 Jun 2015 12:26:54 -0700 Subject: Work profile badge alignment b/21336417 Change-Id: Icd54320fa0d0976cac0ae66d180cc2c99ddd0c1c --- res/values/dimens.xml | 2 +- src/com/android/launcher3/WidgetPreviewLoader.java | 10 ++++++---- .../android/launcher3/compat/AppWidgetManagerCompatV16.java | 3 ++- src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java | 10 +++++----- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 2184eadb3..370b31200 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -117,7 +117,7 @@ 24dp - 4dp + 5dp 2dp diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index cfeced2df..75952d1a6 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -64,7 +64,7 @@ public class WidgetPreviewLoader { private final UserManagerCompat mUserManager; private final AppWidgetManagerCompat mManager; private final CacheDb mDb; - private final InvariantDeviceProfile mDeviceProfile; + private final int mProfileBadgeMargin; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); @Thunk final Handler mWorkerHandler; @@ -76,7 +76,8 @@ public class WidgetPreviewLoader { mUserManager = UserManagerCompat.getInstance(context); mDb = new CacheDb(context); mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); - mDeviceProfile = inv; + mProfileBadgeMargin = context.getResources() + .getDimensionPixelSize(R.dimen.profile_badge_margin); } /** @@ -401,7 +402,7 @@ public class WidgetPreviewLoader { } // Draw the scaled preview into the final bitmap - int x = (preview.getWidth() - previewWidth) / 2; + int x = (preview.getWidth() - previewWidth - mProfileBadgeMargin) / 2; if (widgetPreviewExists) { drawable.setBounds(x, 0, x + previewWidth, previewHeight); drawable.draw(c); @@ -445,7 +446,8 @@ public class WidgetPreviewLoader { } catch (Resources.NotFoundException e) { } c.setBitmap(null); } - return mManager.getBadgeBitmap(info, preview, Math.min(preview.getHeight(), previewHeight)); + int imageHeight = Math.min(preview.getHeight(), previewHeight + mProfileBadgeMargin); + return mManager.getBadgeBitmap(info, preview, imageHeight); } private Bitmap generateShortcutPreview( diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java index a64c70535..f7f4b7e4f 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java @@ -87,7 +87,8 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { } @Override - public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap, int imageHeight) { + public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap, + int imageHeight) { return bitmap; } } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index 96ace8477..13712d8c7 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -108,7 +108,8 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { } @Override - public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap, int imageHeight) { + public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap, + int imageHeight) { if (info.isCustomWidget || info.getProfile().equals(android.os.Process.myUserHandle())) { return bitmap; } @@ -116,15 +117,14 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { // Add a user badge in the bottom right of the image. final Resources res = mContext.getResources(); final int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size); - final int badgeMargin = res.getDimensionPixelSize(R.dimen.profile_badge_margin); final int badgeMinTop = res.getDimensionPixelSize(R.dimen.profile_badge_minimum_top); final Rect badgeLocation = new Rect(0, 0, badgeSize, badgeSize); - final int top = Math.max(imageHeight - badgeSize - badgeMargin, badgeMinTop); + final int top = Math.max(imageHeight - badgeSize, badgeMinTop); if (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { - badgeLocation.offset(badgeMargin, top); + badgeLocation.offset(0, top); } else { - badgeLocation.offset(bitmap.getWidth() - badgeSize - badgeMargin, top); + badgeLocation.offset(bitmap.getWidth() - badgeSize, top); } Drawable drawable = mPm.getUserBadgedDrawableForDensity( -- cgit v1.2.3 From ef7f874a889b609bd34e692b9c9a1f8cefd1ea95 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 4 Jun 2015 17:18:17 -0700 Subject: Refactoring all apps search to support external search bar. - Adding support for an external search bar that can be used to search a container view. This adds a new interface AllAppsSearchController which manages the external search bar. Each controller will have its own search implementation which means that we no longer need a common AppSearchManager interface. - Removing elevation controller as we no longer have a builtin search bar in all apps - Refactoring container view insets so that they behave the same in all containers. - Refactoring apps view to ensure that we only update the number of columns with the available width - Cleaning up LauncherCallbacks interface Bug: 20127840 Bug: 21494973 Change-Id: I710b8e18196961d77d8a29f0c345531d480936fe --- res/drawable/all_apps_search_bg.xml | 11 +- res/layout-sw600dp/all_apps.xml | 33 -- res/layout/all_apps.xml | 40 +- res/layout/all_apps_container.xml | 80 +-- res/layout/all_apps_reveal.xml | 24 - res/layout/all_apps_search_bar.xml | 72 +++ res/layout/widgets_view.xml | 22 +- res/values-sw600dp/dimens.xml | 1 - res/values/dimens.xml | 9 +- src/com/android/launcher3/BaseContainerView.java | 96 +-- src/com/android/launcher3/BaseRecyclerView.java | 13 +- src/com/android/launcher3/DeviceProfile.java | 18 +- src/com/android/launcher3/Launcher.java | 127 +--- src/com/android/launcher3/LauncherCallbacks.java | 15 +- src/com/android/launcher3/LauncherExtension.java | 18 +- .../LauncherStateTransitionAnimation.java | 70 ++- .../android/launcher3/LauncherTransitionable.java | 3 - src/com/android/launcher3/Workspace.java | 9 +- .../WorkspaceStateTransitionAnimation.java | 257 +++++--- .../launcher3/allapps/AllAppsContainerView.java | 644 +++++++-------------- .../launcher3/allapps/AllAppsGridAdapter.java | 35 +- .../launcher3/allapps/AllAppsRecyclerView.java | 23 +- .../allapps/AllAppsSearchBarController.java | 97 ++++ .../launcher3/allapps/AlphabeticalAppsList.java | 21 +- .../launcher3/allapps/AppSearchManager.java | 59 -- .../allapps/DefaultAppSearchAlgorithm.java | 91 +++ .../allapps/DefaultAppSearchController.java | 270 +++++++++ .../allapps/SimpleAppSearchManagerImpl.java | 98 ---- .../launcher3/widget/WidgetsContainerView.java | 42 +- .../launcher3/widget/WidgetsRecyclerView.java | 7 - 30 files changed, 1164 insertions(+), 1141 deletions(-) delete mode 100644 res/layout-sw600dp/all_apps.xml delete mode 100644 res/layout/all_apps_reveal.xml create mode 100644 res/layout/all_apps_search_bar.xml create mode 100644 src/com/android/launcher3/allapps/AllAppsSearchBarController.java delete mode 100644 src/com/android/launcher3/allapps/AppSearchManager.java create mode 100644 src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java create mode 100644 src/com/android/launcher3/allapps/DefaultAppSearchController.java delete mode 100644 src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java diff --git a/res/drawable/all_apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml index 57eb5825e..a09f88fd4 100644 --- a/res/drawable/all_apps_search_bg.xml +++ b/res/drawable/all_apps_search_bg.xml @@ -14,10 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - \ No newline at end of file + \ No newline at end of file diff --git a/res/layout-sw600dp/all_apps.xml b/res/layout-sw600dp/all_apps.xml deleted file mode 100644 index 368e6abdd..000000000 --- a/res/layout-sw600dp/all_apps.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index b907c34f1..1bf54eefb 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -21,15 +21,37 @@ android:id="@+id/apps_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:descendantFocusability="afterDescendants"> - + + + - + + + android:layout_height="0dp" + android:layout_weight="1"> + + + \ No newline at end of file diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml index fc77cd31f..5801a0e8e 100644 --- a/res/layout/all_apps_container.xml +++ b/res/layout/all_apps_container.xml @@ -14,95 +14,35 @@ See the License for the specific language governing permissions and limitations under the License. --> + + android:focusable="true" + android:focusableInTouchMode="true"> + android:focusable="true" + android:descendantFocusability="afterDescendants" /> - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/all_apps_reveal.xml b/res/layout/all_apps_reveal.xml deleted file mode 100644 index 5f4665642..000000000 --- a/res/layout/all_apps_reveal.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - \ No newline at end of file diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml new file mode 100644 index 000000000..9f4e3228c --- /dev/null +++ b/res/layout/all_apps_search_bar.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml index a1a62b32a..c27e79bbd 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_view.xml @@ -21,25 +21,29 @@ android:id="@+id/widgets_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="@dimen/widget_container_inset" - android:paddingBottom="@dimen/widget_container_inset" + android:orientation="vertical" android:descendantFocusability="afterDescendants"> + android:layout_height="match_parent"> + + \ No newline at end of file diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index daa98ef0b..2651fbb3f 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -16,7 +16,6 @@ - 18dp 0dp 26sp 12dp diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 2184eadb3..0311a8977 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -51,17 +51,18 @@ - 8dp + 8dp + + 4dp - 8dp 56dp 8dp 24sp - 52dp + 48dp 8dp 8dp 24dp - 16dp + 16dp 4dp -16dp diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java index bd1c625e3..4b7b97775 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -19,16 +19,25 @@ package com.android.launcher3; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.widget.FrameLayout; +import android.widget.LinearLayout; /** * A base container view, which supports resizing. */ -public class BaseContainerView extends FrameLayout implements Insettable { +public abstract class BaseContainerView extends LinearLayout implements Insettable { - protected Rect mInsets = new Rect(); - protected Rect mFixedBounds = new Rect(); - protected int mFixedBoundsContainerInset; + // The window insets + private Rect mInsets = new Rect(); + // The bounds of the search bar. Only the left, top, right are used to inset the + // search bar and the height is determined by the measurement of the layout + private Rect mSearchBarBounds = new Rect(); + // The bounds of the container + protected Rect mContentBounds = new Rect(); + // The padding to apply to the container to achieve the bounds + protected Rect mContentPadding = new Rect(); + // The inset to apply to the edges and between the search bar and the container + private int mContainerBoundsInset; + private boolean mHasSearchBar; public BaseContainerView(Context context) { this(context, null); @@ -40,62 +49,73 @@ public class BaseContainerView extends FrameLayout implements Insettable { public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize( - R.dimen.container_fixed_bounds_inset); + mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset); } @Override final public void setInsets(Rect insets) { mInsets.set(insets); - onUpdateBackgrounds(); - onUpdatePaddings(); + updateBackgroundAndPaddings(); + } + + protected void setHasSearchBar() { + mHasSearchBar = true; } /** - * Sets the fixed bounds for this container view. + * Sets the search bar bounds for this container view to match. */ - final public void setFixedBounds(Rect fixedBounds) { - if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) { - mFixedBounds.set(fixedBounds); - if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { - mFixedBounds.top = mInsets.top; - mFixedBounds.bottom = mInsets.bottom; - } - // To ensure that the child RecyclerView has the full width to handle touches right to - // the edge of the screen, we only apply the top and bottom padding to the bounds - mFixedBounds.top += mFixedBoundsContainerInset; - mFixedBounds.bottom += mFixedBoundsContainerInset; - onFixedBoundsUpdated(); - } + final public void setSearchBarBounds(Rect bounds) { + mSearchBarBounds.set(bounds); + // Post the updates since they can trigger a relayout, and this call can be triggered from // a layout pass itself. post(new Runnable() { @Override public void run() { - onUpdateBackgrounds(); - onUpdatePaddings(); + updateBackgroundAndPaddings(); } }); } /** - * Update the UI in response to a change in the fixed bounds. + * Update the backgrounds and padding in response to a change in the bounds or insets. */ - protected void onFixedBoundsUpdated() { - // Do nothing - } + protected void updateBackgroundAndPaddings() { + Rect padding; + Rect searchBarBounds = new Rect(mSearchBarBounds); + if (mSearchBarBounds.isEmpty()) { + // Use the normal bounds + padding = new Rect(mInsets.left + mContainerBoundsInset, + (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)), + mInsets.right + mContainerBoundsInset, + mInsets.bottom + mContainerBoundsInset); - /** - * Update the paddings in response to a change in the bounds or insets. - */ - protected void onUpdatePaddings() { - // Do nothing + // Special case -- we have the search bar, but no specific bounds, so just give it + // the inset bounds without a height. + searchBarBounds.set(mInsets.left + mContainerBoundsInset, + mInsets.top + mContainerBoundsInset, + getMeasuredWidth() - (mInsets.right + mContainerBoundsInset), 0); + } else { + // Use the search bounds, if there is a search bar, the bounds will contain + // the offsets for the insets so we can ignore that + padding = new Rect(mSearchBarBounds.left, + (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)), + getMeasuredWidth() - mSearchBarBounds.right, + mInsets.bottom + mContainerBoundsInset); + } + if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) { + mContentPadding.set(padding); + mContentBounds.set(padding.left, padding.top, + getMeasuredWidth() - padding.right, + getMeasuredHeight() - padding.bottom); + mSearchBarBounds.set(searchBarBounds); + onUpdateBackgroundAndPaddings(mSearchBarBounds, padding); + } } /** - * Update the backgrounds in response to a change in the bounds or insets. + * To be implemented by container views to update themselves when the bounds changes. */ - protected void onUpdateBackgrounds() { - // Do nothing - } + protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding); } \ No newline at end of file diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index a207d9a12..081c4f502 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -89,9 +89,7 @@ public class BaseRecyclerView extends RecyclerView private int mLastY; private int mScrollbarWidth; private int mScrollbarInset; - private Rect mBackgroundPadding = new Rect(); - - + protected Rect mBackgroundPadding = new Rect(); public BaseRecyclerView(Context context) { this(context, null); @@ -230,6 +228,10 @@ public class BaseRecyclerView extends RecyclerView return false; } + public void updateBackgroundPadding(Rect padding) { + mBackgroundPadding.set(padding); + } + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -340,9 +342,10 @@ public class BaseRecyclerView extends RecyclerView // Calculate the position for the fast scroller popup Rect bgBounds = mFastScrollerBg.getBounds(); if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left + getScrollBarSize(); + x = mBackgroundPadding.left + (2 * getScrollbarWidth()); } else { - x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); + x = getWidth() - mBackgroundPadding.right - (2 * getScrollbarWidth()) - + bgBounds.width(); } y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 9c59dab53..a50540d26 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -223,26 +223,22 @@ public class DeviceProfile { folderCellHeightPx = cellHeightPx + edgeMarginPx; folderBackgroundOffset = -edgeMarginPx; folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset; - - updateAppsViewNumCols(res, 0); } - public boolean updateAppsViewNumCols(Resources res, int containerWidth) { + /** + * @param recyclerViewWidth the available width of the AllAppsRecyclerView + */ + public void updateAppsViewNumCols(Resources res, int recyclerViewWidth) { int appsViewLeftMarginPx = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); int allAppsCellWidthGap = res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap); - int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx; + int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx; int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) / (allAppsIconSizePx + allAppsCellWidthGap); int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols); - if ((numAppsCols != allAppsNumCols) || - (numPredictiveAppCols != allAppsNumPredictiveCols)) { - allAppsNumCols = numAppsCols; - allAppsNumPredictiveCols = numPredictiveAppCols; - return true; - } - return false; + allAppsNumCols = numAppsCols; + allAppsNumPredictiveCols = numPredictiveAppCols; } /** Returns the search bar top offset */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 796de3fa0..335a77bcf 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -99,7 +99,7 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.allapps.AllAppsContainerView; -import com.android.launcher3.allapps.AppSearchManager; +import com.android.launcher3.allapps.AllAppsSearchBarController; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; @@ -138,9 +138,6 @@ public class Launcher extends Activity static final String TAG = "Launcher"; static final boolean LOGD = false; - // Temporary flag - static final boolean DISABLE_ALL_APPS_SEARCH_INTEGRATION = true; - static final boolean PROFILE_STARTUP = false; static final boolean DEBUG_WIDGETS = true; static final boolean DEBUG_STRICT_MODE = false; @@ -573,32 +570,6 @@ public class Launcher extends Activity public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { mLauncherCallbacks = callbacks; - mLauncherCallbacks.setLauncherAppsCallback(new Launcher.LauncherAppsCallbacks() { - @Override - public void onAllAppsBoundsChanged(Rect bounds) { - if (LOGD) { - Log.d(TAG, "onAllAppsBoundsChanged(Rect): " + bounds); - } - mAppsView.setFixedBounds(bounds); - mWidgetsView.setFixedBounds(bounds); - } - - @Override - public void dismissAllApps() { - if (!DISABLE_ALL_APPS_SEARCH_INTEGRATION) { - // Dismiss All Apps if we aren't already paused/invisible - if (!mPaused) { - showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, - null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */); - } - } - } - - @Override - public void setSearchManager(AppSearchManager manager) { - mAppsView.setSearchManager(manager); - } - }); mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() { private boolean mImportanceStored = false; private int mWorkspaceImportanceForAccessibility = @@ -638,6 +609,14 @@ public class Launcher extends Activity } } + /** + * Updates the bounds of all the overlays to match the new fixed bounds. + */ + public void updateOverlayBounds(Rect newBounds) { + mAppsView.setSearchBarBounds(newBounds); + mWidgetsView.setSearchBarBounds(newBounds); + } + /** To be overridden by subclasses to hint to Launcher that we have custom content */ protected boolean hasCustomContentToLeft() { if (mLauncherCallbacks != null) { @@ -1012,16 +991,6 @@ public class Launcher extends Activity } mOnResumeState = State.NONE; - // Restore the apps state if we are in all apps - if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { - // Otherwise, notify the callbacks if we are in all apps mode - if (mState == State.APPS) { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onAllAppsShown(); - } - } - } - // Background was set to gradient in onPause(), restore to transparent if in all apps. setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT : WORKSPACE_BACKGROUND_TRANSPARENT); @@ -1167,17 +1136,20 @@ public class Launcher extends Activity * Updates launcher to the available space that AllApps can take so as not to overlap with * any other views. */ + @Deprecated public void onAllAppsBoundsChanged(Rect bounds); /** * Called to dismiss all apps if it is showing. */ + @Deprecated public void dismissAllApps(); /** * Sets the search manager to be used for app search. */ - public void setSearchManager(AppSearchManager manager); + @Deprecated + public void setSearchManager(Object manager); } public interface LauncherSearchCallbacks { @@ -1463,14 +1435,14 @@ public class Launcher extends Activity mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.search_drop_target_bar); - // Setup Apps + // Setup Apps and Widgets mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view); - if (isAllAppsSearchOverridden()) { - mAppsView.hideHeaderBar(); - } - - // Setup AppsCustomize mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view); + if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) { + mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController()); + } else { + mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController()); + } // Setup the drag controller (drop targets have to be added in reverse order in priority) dragController.setDragScoller(mWorkspace); @@ -2866,17 +2838,8 @@ public class Launcher extends Activity public void updateInteraction(Workspace.State fromState, Workspace.State toState) { // Only update the interacting state if we are transitioning to/from a view with an // overlay - boolean fromStateWithOverlay; - boolean toStateWithOverlay; - if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) { - fromStateWithOverlay = fromState != Workspace.State.NORMAL; - toStateWithOverlay = toState != Workspace.State.NORMAL; - } else { - fromStateWithOverlay = fromState != Workspace.State.NORMAL && - fromState != Workspace.State.NORMAL_HIDDEN; - toStateWithOverlay = toState != Workspace.State.NORMAL && - toState != Workspace.State.NORMAL_HIDDEN; - } + boolean fromStateWithOverlay = fromState != Workspace.State.NORMAL; + boolean toStateWithOverlay = toState != Workspace.State.NORMAL; if (toStateWithOverlay) { onInteractionBegin(); } else if (fromStateWithOverlay) { @@ -3320,21 +3283,19 @@ public class Launcher extends Activity } public void showWorkspace(boolean animated) { - showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null, - true); + showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null); } public void showWorkspace(boolean animated, Runnable onCompleteRunnable) { showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, - onCompleteRunnable, true); + onCompleteRunnable); } protected void showWorkspace(int snapToPage, boolean animated) { - showWorkspace(snapToPage, animated, null, true); + showWorkspace(snapToPage, animated, null); } - void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable, - boolean notifyLauncherCallbacks) { + void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) { boolean changed = mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL; if (changed) { @@ -3366,12 +3327,6 @@ public class Launcher extends Activity // Send an accessibility event to announce the context change getWindow().getDecorView() .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - if (notifyLauncherCallbacks) { - // Dismiss all apps when the workspace is shown - if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) { - mLauncherCallbacks.onAllAppsHidden(); - } - } } } @@ -3431,10 +3386,7 @@ public class Launcher extends Activity } if (toState == State.APPS) { - mStateTransitionAnimation.startAnimationToAllApps(animated); - if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) { - mLauncherCallbacks.onAllAppsShown(); - } + mStateTransitionAnimation.startAnimationToAllApps(mState, animated); } else { mStateTransitionAnimation.startAnimationToWidgets(animated); } @@ -3458,9 +3410,10 @@ public class Launcher extends Activity * new state. */ public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage, - boolean animated, HashMap layerViews) { + boolean animated, boolean hasOverlaySearchBar, HashMap layerViews) { Workspace.State fromState = mWorkspace.getState(); - Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews); + Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, + hasOverlaySearchBar, layerViews); updateInteraction(fromState, toState); return anim; } @@ -3482,14 +3435,6 @@ public class Launcher extends Activity final Runnable onCompleteRunnable) { if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; - if (successfulDrop) { - // We need to trigger all apps hidden to notify search to update itself before the - // delayed call to showWorkspace below - if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) { - mLauncherCallbacks.onAllAppsHidden(); - } - } - mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -4473,20 +4418,6 @@ public class Launcher extends Activity return null; } - /** - * Returns whether the launcher callbacks overrides search in all apps. - */ - @Thunk boolean isAllAppsSearchOverridden() { - if (DISABLE_ALL_APPS_SEARCH_INTEGRATION) { - return false; - } - - if (mLauncherCallbacks != null) { - return mLauncherCallbacks.overrideAllAppsSearch(); - } - return false; - } - private boolean shouldRunFirstRunActivity() { return !ActivityManager.isRunningInTestHarness() && !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false); diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 70e400bca..e73275400 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -7,6 +7,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.allapps.AllAppsSearchBarController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -51,12 +52,9 @@ public interface LauncherCallbacks { public void onLauncherProviderChange(); public void finishBindingItems(final boolean upgradePath); public void onClickAllAppsButton(View v); - public void onAllAppsShown(); - public void onAllAppsHidden(); public void bindAllApplications(ArrayList apps); public void onClickFolderIcon(View v); public void onClickAppShortcut(View v); - @Deprecated public void onClickPagedViewIcon(View v); public void onClickWallpaperPicker(View v); @@ -89,10 +87,11 @@ public interface LauncherCallbacks { public View getIntroScreen(); public boolean shouldMoveToDefaultScreenOnHomeIntent(); public boolean hasSettings(); + @Deprecated public ComponentName getWallpaperPickerComponent(); public boolean overrideWallpaperDimensions(); public boolean isLauncherPreinstalled(); - public boolean overrideAllAppsSearch(); + public AllAppsSearchBarController getAllAppsSearchBarController(); public List getPredictedApps(); /** @@ -113,14 +112,6 @@ public interface LauncherCallbacks { public Launcher.LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container, Launcher.LauncherOverlayCallbacks callbacks); - /** - * Sets the callbacks to allow any extensions to callback to the launcher. - * - * @param callbacks A set of callbacks to the Launcher, is actually a LauncherAppsCallback, but - * for implementation purposes is passed around as an object. - */ - public void setLauncherAppsCallback(Object callbacks); - /** * Sets the callbacks to allow reacting the actions of search overlays of the launcher. * diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index 09a105bcc..fafb070ec 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -11,6 +11,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.allapps.AllAppsSearchBarController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -124,14 +125,6 @@ public class LauncherExtension extends Launcher { public void onClickAllAppsButton(View v) { } - @Override - public void onAllAppsShown() { - } - - @Override - public void onAllAppsHidden() { - } - @Override public void bindAllApplications(ArrayList apps) { } @@ -255,8 +248,8 @@ public class LauncherExtension extends Launcher { } @Override - public boolean overrideAllAppsSearch() { - return false; + public AllAppsSearchBarController getAllAppsSearchBarController() { + return null; } @Override @@ -284,11 +277,6 @@ public class LauncherExtension extends Launcher { return mLauncherOverlay; } - @Override - public void setLauncherAppsCallback(Object callbacks) { - // Do nothing - } - @Override public void setLauncherSearchCallback(Object callbacks) { // Do nothing diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index a006d141b..e94a2ac0b 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -129,7 +129,7 @@ public class LauncherStateTransitionAnimation { /** * Starts an animation to the apps view. */ - public void startAnimationToAllApps(final boolean animated) { + public void startAnimationToAllApps(final Launcher.State fromState, final boolean animated) { final AllAppsContainerView toView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { private int[] mAllAppsToPanelDelta; @@ -137,7 +137,6 @@ public class LauncherStateTransitionAnimation { @Override public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - toView.setBackground(null); // Get the y delta between the center of the page and the center of the all apps // button mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, @@ -173,9 +172,9 @@ public class LauncherStateTransitionAnimation { }; } }; + // Only animate the search bar if animating from spring loaded mode back to all apps startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), animated, - !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb); + toView.getRevealView(), toView.getSearchBarView(), animated, true, cb); } /** @@ -188,7 +187,6 @@ public class LauncherStateTransitionAnimation { @Override public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); } @Override public float getMaterialRevealViewFinalAlpha(View revealView) { @@ -200,8 +198,8 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, - toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */, - cb); + toView.getContentView(), toView.getRevealView(), null, animated, + true /* hideSearchBar */, cb); } /** @@ -217,10 +215,10 @@ public class LauncherStateTransitionAnimation { } if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { - startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage, + startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage, animated, onCompleteRunnable); } else { - startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage, + startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage, animated, onCompleteRunnable); } } @@ -230,8 +228,9 @@ public class LauncherStateTransitionAnimation { */ @SuppressLint("NewApi") private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView, - final View contentView, final View revealView, final boolean animated, - final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) { + final View contentView, final View revealView, final View overlaySearchBarView, + final boolean animated, final boolean hideSearchBar, + final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); @@ -252,7 +251,7 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1, - animated, layerViews); + animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); @@ -297,6 +296,15 @@ public class LauncherStateTransitionAnimation { layerViews.put(revealView, BUILD_AND_SET_LAYER); mStateAnimation.play(panelAlphaAndDrift); + if (overlaySearchBarView != null) { + overlaySearchBarView.setAlpha(0f); + ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f); + searchBarAlpha.setDuration(100); + searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); + layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER); + mStateAnimation.play(searchBarAlpha); + } + // Setup the animation for the content view contentView.setVisibility(View.VISIBLE); contentView.setAlpha(0f); @@ -426,9 +434,8 @@ public class LauncherStateTransitionAnimation { /** * Starts and animation to the workspace from the apps view. */ - private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final int toWorkspacePage, - final boolean animated, final Runnable onCompleteRunnable) { + private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState, + final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { AllAppsContainerView appsView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { int[] mAllAppsToPanelDelta; @@ -479,24 +486,23 @@ public class LauncherStateTransitionAnimation { }; } }; + // Only animate the search bar if animating to spring loaded mode from all apps startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView, - appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable, - cb); + appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(), + animated, onCompleteRunnable, cb); } /** * Starts and animation to the workspace from the widgets view. */ - private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final int toWorkspacePage, - final boolean animated, final Runnable onCompleteRunnable) { + private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState, + final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); final Resources res = mLauncher.getResources(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); } @Override public float getMaterialRevealViewFinalYDrift(View revealView) { @@ -518,7 +524,7 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView, - widgetsView.getContentView(), widgetsView.getRevealView(), animated, + widgetsView.getContentView(), widgetsView.getRevealView(), null, animated, onCompleteRunnable, cb); } @@ -527,8 +533,8 @@ public class LauncherStateTransitionAnimation { */ private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, final int toWorkspacePage, final View fromView, final View contentView, - final View revealView, final boolean animated, final Runnable onCompleteRunnable, - final PrivateTransitionCallbacks pCb) { + final View revealView, final View overlaySearchBarView, final boolean animated, + final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); @@ -549,7 +555,8 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, - toWorkspacePage, animated, layerViews); + toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */, + layerViews); if (animated && initialized) { mStateAnimation = LauncherAnimUtils.createAnimatorSet(); @@ -633,6 +640,16 @@ public class LauncherStateTransitionAnimation { itemsAlpha.setInterpolator(decelerateInterpolator); mStateAnimation.play(itemsAlpha); + if (overlaySearchBarView != null) { + overlaySearchBarView.setAlpha(1f); + ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f); + searchAlpha.setDuration(material ? 100 : 150); + searchAlpha.setInterpolator(decelerateInterpolator); + searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); + layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER); + mStateAnimation.play(searchAlpha); + } + if (material) { // Animate the all apps button float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); @@ -681,6 +698,9 @@ public class LauncherStateTransitionAnimation { contentView.setTranslationY(0); contentView.setAlpha(1); } + if (overlaySearchBarView != null) { + overlaySearchBarView.setAlpha(1f); + } // This can hold unnecessary references to views. mStateAnimation = null; diff --git a/src/com/android/launcher3/LauncherTransitionable.java b/src/com/android/launcher3/LauncherTransitionable.java index 9442abcde..49af6928a 100644 --- a/src/com/android/launcher3/LauncherTransitionable.java +++ b/src/com/android/launcher3/LauncherTransitionable.java @@ -16,13 +16,10 @@ package com.android.launcher3; -import android.view.View; - /** * An interface to get callbacks during a launcher transition. */ public interface LauncherTransitionable { - View getContent(); void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace); void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace); void onLauncherTransitionStep(Launcher l, float t); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 19195b421..76f872bae 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1998,10 +1998,10 @@ public class Workspace extends PagedView * to that new state. */ public Animator setStateWithAnimation(State toState, int toPage, boolean animated, - HashMap layerViews) { + boolean hasOverlaySearchBar, HashMap layerViews) { // Create the animation to the new state Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(getState(), - toState, toPage, animated, layerViews); + toState, toPage, animated, hasOverlaySearchBar, layerViews); // Update the current state mState = toState; @@ -2100,11 +2100,6 @@ public class Workspace extends PagedView } } - @Override - public View getContent() { - return this; - } - /** * Returns the drawable for the given text view. */ diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index e360e889b..13e4a59f1 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -121,6 +121,52 @@ class ZoomInInterpolator implements TimeInterpolator { } } +/** + * Stores the transition states for convenience. + */ +class TransitionStates { + + // Raw states + final boolean oldStateIsNormal; + final boolean oldStateIsSpringLoaded; + final boolean oldStateIsNormalHidden; + final boolean oldStateIsOverviewHidden; + final boolean oldStateIsOverview; + + final boolean stateIsNormal; + final boolean stateIsSpringLoaded; + final boolean stateIsNormalHidden; + final boolean stateIsOverviewHidden; + final boolean stateIsOverview; + + // Convenience members + final boolean workspaceToAllApps; + final boolean overviewToAllApps; + final boolean allAppsToWorkspace; + final boolean workspaceToOverview; + final boolean overviewToWorkspace; + + public TransitionStates(final Workspace.State fromState, final Workspace.State toState) { + oldStateIsNormal = (fromState == Workspace.State.NORMAL); + oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED); + oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN); + oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN); + oldStateIsOverview = (fromState == Workspace.State.OVERVIEW); + + stateIsNormal = (toState == Workspace.State.NORMAL); + stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED); + stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN); + stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN); + stateIsOverview = (toState == Workspace.State.OVERVIEW); + + workspaceToOverview = (oldStateIsNormal && stateIsOverview); + workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); + overviewToWorkspace = (oldStateIsOverview && stateIsNormal); + overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); + allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); + } +} + /** * Manages the animations between each of the workspace states. */ @@ -175,9 +221,17 @@ public class WorkspaceStateTransitionAnimation { } public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, - int toPage, boolean animated, - HashMap layerViews) { - getAnimation(fromState, toState, toPage, animated, layerViews); + int toPage, boolean animated, boolean hasOverlaySearchBar, + HashMap layerViews) { + AccessibilityManager am = (AccessibilityManager) + mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); + final boolean accessibilityEnabled = am.isEnabled(); + TransitionStates states = new TransitionStates(fromState, toState); + int duration = getAnimationDuration(states); + animateWorkspace(states, toPage, animated, duration, layerViews, + accessibilityEnabled); + animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews, + accessibilityEnabled); return mStateAnimator; } @@ -186,15 +240,37 @@ public class WorkspaceStateTransitionAnimation { } /** - * Starts a transition animation for the workspace. + * Reinitializes the arrays that we need for the animations on each page. */ - private void getAnimation(final Workspace.State fromState, final Workspace.State toState, - int toPage, final boolean animated, - final HashMap layerViews) { - AccessibilityManager am = (AccessibilityManager) - mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); - final boolean accessibilityEnabled = am.isEnabled(); + private void reinitializeAnimationArrays() { + final int childCount = mWorkspace.getChildCount(); + if (mLastChildCount == childCount) return; + mOldBackgroundAlphas = new float[childCount]; + mOldAlphas = new float[childCount]; + mNewBackgroundAlphas = new float[childCount]; + mNewAlphas = new float[childCount]; + } + + /** + * Returns the proper animation duration for a transition. + */ + private int getAnimationDuration(TransitionStates states) { + if (states.workspaceToAllApps || states.overviewToAllApps) { + return mAllAppsTransitionTime; + } else if (states.workspaceToOverview || states.overviewToWorkspace) { + return mOverviewTransitionTime; + } else { + return mOverlayTransitionTime; + } + } + + /** + * Starts a transition animation for the workspace. + */ + private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated, + final int duration, final HashMap layerViews, + final boolean accessibilityEnabled) { // Reinitialize animation arrays for the current workspace state reinitializeAnimationArrays(); @@ -205,32 +281,12 @@ public class WorkspaceStateTransitionAnimation { } // Update the workspace state - final boolean oldStateIsNormal = (fromState == Workspace.State.NORMAL); - final boolean oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED); - final boolean oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN); - final boolean oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN); - final boolean oldStateIsOverview = (fromState == Workspace.State.OVERVIEW); - - final boolean stateIsNormal = (toState == Workspace.State.NORMAL); - final boolean stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED); - final boolean stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN); - final boolean stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN); - final boolean stateIsOverview = (toState == Workspace.State.OVERVIEW); - - final boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); - final boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); - final boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); - final boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); - final boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); - - float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; - float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; - float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; - // We keep the search bar visible on the workspace and in AllApps now - boolean showSearchBar = stateIsNormal || - (mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden); - float finalSearchBarAlpha = showSearchBar ? 1f : 0f; - float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? + float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ? + 1.0f : 0f; + float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ? + 1f : 0f; + float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f; + float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ? mWorkspace.getOverviewModeTranslationY() : 0; final int childCount = mWorkspace.getChildCount(); @@ -238,29 +294,20 @@ public class WorkspaceStateTransitionAnimation { mNewScale = 1.0f; - if (oldStateIsOverview) { + if (states.oldStateIsOverview) { mWorkspace.disableFreeScroll(); - } else if (stateIsOverview) { + } else if (states.stateIsOverview) { mWorkspace.enableFreeScroll(); } - if (!stateIsNormal) { - if (stateIsSpringLoaded) { + if (!states.stateIsNormal) { + if (states.stateIsSpringLoaded) { mNewScale = mSpringLoadedShrinkFactor; - } else if (stateIsOverview || stateIsOverviewHidden) { + } else if (states.stateIsOverview || states.stateIsOverviewHidden) { mNewScale = mOverviewModeShrinkFactor; } } - final int duration; - if (workspaceToAllApps || overviewToAllApps) { - duration = mAllAppsTransitionTime; - } else if (workspaceToOverview || overviewToWorkspace) { - duration = mOverviewTransitionTime; - } else { - duration = mOverlayTransitionTime; - } - if (toPage == SCROLL_TO_CURRENT_PAGE) { toPage = mWorkspace.getPageNearestToCenterOfScreen(); } @@ -271,9 +318,9 @@ public class WorkspaceStateTransitionAnimation { boolean isCurrentPage = (i == toPage); float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); float finalAlpha; - if (stateIsNormalHidden || stateIsOverviewHidden) { + if (states.stateIsNormalHidden || states.stateIsOverviewHidden) { finalAlpha = 0f; - } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) { + } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) { finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; } else { finalAlpha = 1f; @@ -282,8 +329,8 @@ public class WorkspaceStateTransitionAnimation { // If we are animating to/from the small state, then hide the side pages and fade the // current page in if (!mWorkspace.isSwitchingState()) { - if (workspaceToAllApps || allAppsToWorkspace) { - if (allAppsToWorkspace && isCurrentPage) { + if (states.workspaceToAllApps || states.allAppsToWorkspace) { + if (states.allAppsToWorkspace && isCurrentPage) { initialAlpha = 0f; } else if (!isCurrentPage) { initialAlpha = finalAlpha = 0f; @@ -303,7 +350,6 @@ public class WorkspaceStateTransitionAnimation { } } - final View searchBar = mLauncher.getOrCreateQsbBar(); final ViewGroup overviewPanel = mLauncher.getOverviewPanel(); final View hotseat = mLauncher.getHotseat(); final View pageIndicator = mWorkspace.getPageIndicator(); @@ -345,7 +391,7 @@ public class WorkspaceStateTransitionAnimation { } } } - Animator pageIndicatorAlpha = null; + Animator pageIndicatorAlpha; if (pageIndicator != null) { pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator) .alpha(finalHotseatAndPageIndicatorAlpha).withLayer(); @@ -380,11 +426,11 @@ public class WorkspaceStateTransitionAnimation { overviewPanelAlpha.withLayer(); } - if (workspaceToOverview) { + if (states.workspaceToOverview) { pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2)); hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); overviewPanelAlpha.setInterpolator(null); - } else if (overviewToWorkspace) { + } else if (states.overviewToWorkspace) { pageIndicatorAlpha.setInterpolator(null); hotseatAlpha.setInterpolator(null); overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); @@ -394,26 +440,6 @@ public class WorkspaceStateTransitionAnimation { pageIndicatorAlpha.setDuration(duration); hotseatAlpha.setDuration(duration); - // TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the - // bar has no idea that it is hidden, and this has no idea what state the bar is - // actually in. - if (searchBar != null) { - LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar) - .alpha(finalSearchBarAlpha); - searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, accessibilityEnabled)); - searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (layerViews != null) { - // If layerViews is not null, we add these views, and indicate that - // the caller can manage layer state. - layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - } else { - // Otherwise let the animator handle layer management. - searchBarAlpha.withLayer(); - } - searchBarAlpha.setDuration(duration); - mStateAnimator.play(searchBarAlpha); - } - mStateAnimator.play(overviewPanelAlpha); mStateAnimator.play(hotseatAlpha); mStateAnimator.play(pageIndicatorAlpha); @@ -437,10 +463,6 @@ public class WorkspaceStateTransitionAnimation { pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha); AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled); } - if (searchBar != null) { - searchBar.setAlpha(finalSearchBarAlpha); - AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); - } mWorkspace.updateCustomContentVisibility(); mWorkspace.setScaleX(mNewScale); mWorkspace.setScaleY(mNewScale); @@ -452,7 +474,7 @@ public class WorkspaceStateTransitionAnimation { } } - if (stateIsNormal) { + if (states.stateIsNormal) { animateBackgroundGradient(0f, animated); } else { animateBackgroundGradient(mWorkspaceScrimAlpha, animated); @@ -460,16 +482,69 @@ public class WorkspaceStateTransitionAnimation { } /** - * Reinitializes the arrays that we need for the animations on each page. + * Coordinates with the workspace animation to animate the search bar. + * + * TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the + * bar has no idea that it is hidden, and this has no idea what state the bar is + * actually in. */ - private void reinitializeAnimationArrays() { - final int childCount = mWorkspace.getChildCount(); - if (mLastChildCount == childCount) return; + private void animateSearchBar(TransitionStates states, boolean animated, int duration, + boolean hasOverlaySearchBar, final HashMap layerViews, + final boolean accessibilityEnabled) { - mOldBackgroundAlphas = new float[childCount]; - mOldAlphas = new float[childCount]; - mNewBackgroundAlphas = new float[childCount]; - mNewAlphas = new float[childCount]; + // The search bar is only visible in the workspace + final View searchBar = mLauncher.getOrCreateQsbBar(); + if (searchBar != null) { + final boolean searchBarWillBeShown = states.stateIsNormal; + final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f; + if (animated) { + if (hasOverlaySearchBar) { + // If there is an overlay search bar, then we will coordinate with it. + mStateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // If we are transitioning to a visible search bar, show it immediately + // and let the overlay search bar has faded out + if (searchBarWillBeShown) { + searchBar.setAlpha(finalSearchBarAlpha); + AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + // If we are transitioning to a hidden search bar, hide it only after + // the overlay search bar has faded in + if (!searchBarWillBeShown) { + searchBar.setAlpha(finalSearchBarAlpha); + AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); + } + } + }); + } else { + // Otherwise, we can just do the normal animation + LauncherViewPropertyAnimator searchBarAlpha = + new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha); + searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, + accessibilityEnabled)); + searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (layerViews != null) { + // If layerViews is not null, we add these views, and indicate that + // the caller can manage layer state. + layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + } else { + // Otherwise let the animator handle layer management. + searchBarAlpha.withLayer(); + } + searchBarAlpha.setDuration(duration); + mStateAnimator.play(searchBarAlpha); + } + } else { + // Set the search bar state immediately + searchBar.setAlpha(finalSearchBarAlpha); + AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); + } + } } /** diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 855a4430d..b300cae62 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -20,17 +20,15 @@ import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; -import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; import android.os.Bundle; import android.support.v7.widget.RecyclerView; -import android.text.Editable; -import android.text.TextWatcher; +import android.text.Selection; +import android.text.SpannableStringBuilder; +import android.text.method.TextKeyListener; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -39,11 +37,8 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewTreeObserver; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; -import android.widget.TextView; - +import android.widget.LinearLayout; import com.android.launcher3.AppInfo; import com.android.launcher3.BaseContainerView; import com.android.launcher3.BubbleTextView; @@ -54,7 +49,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.Folder; -import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherTransitionable; @@ -62,131 +56,24 @@ import com.android.launcher3.R; import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; -import com.android.launcher3.allapps.AppSearchManager.AppSearchResultCallback; import com.android.launcher3.util.Thunk; import java.util.ArrayList; import java.util.List; -/** - * Interface for controlling the header elevation in response to RecyclerView scroll. - */ -interface HeaderElevationController { - void onScroll(int scrollY); - void updateBackgroundPadding(Drawable bg); - void disable(); -} - -/** - * Implementation of the header elevation mechanism for pre-L devices. It simulates elevation - * by drawing a gradient under the header bar. - */ -final class HeaderElevationControllerV16 implements HeaderElevationController { - - private final View mShadow; - private final float mScrollToElevation; - private final Rect mTmpRect = new Rect(); - - public HeaderElevationControllerV16(View header) { - Resources res = header.getContext().getResources(); - mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); - - mShadow = new View(header.getContext()); - mShadow.setBackground(new GradientDrawable( - GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000})); - mShadow.setAlpha(0); - - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height)); - lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height; - - ((ViewGroup) header.getParent()).addView(mShadow, lp); - } - - @Override - public void onScroll(int scrollY) { - float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / - mScrollToElevation; - mShadow.setAlpha(elevationPct); - } - - @Override - public void updateBackgroundPadding(Drawable bg) { - bg.getPadding(mTmpRect); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams(); - lp.leftMargin = mTmpRect.left; - lp.rightMargin = mTmpRect.right; - mShadow.requestLayout(); - } - - @Override - public void disable() { - ViewGroup parent = (ViewGroup) mShadow.getParent(); - if (parent != null) { - parent.removeView(mShadow); - } - } -} - -/** - * Implementation of the header elevation mechanism for L+ devices, which makes use of the native - * view elevation. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -final class HeaderElevationControllerVL implements HeaderElevationController { - - private final View mHeader; - private final float mMaxElevation; - private final float mScrollToElevation; - - public HeaderElevationControllerVL(View header) { - mHeader = header; - - Resources res = header.getContext().getResources(); - mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation); - mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation); - } - - @Override - public void onScroll(int scrollY) { - float elevationPct = (float) Math.min(scrollY, mScrollToElevation) / - mScrollToElevation; - float newElevation = mMaxElevation * elevationPct; - if (Float.compare(mHeader.getElevation(), newElevation) != 0) { - mHeader.setElevation(newElevation); - } - } - - @Override - public void updateBackgroundPadding(Drawable bg) { - // Do nothing, the background padding on the header view is already applied - } - - @Override - public void disable() { } -} /** * The all apps view container. */ -public class AllAppsContainerView extends BaseContainerView implements DragSource, Insettable, - TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, - AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks, - View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, - ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback, Stats.LaunchSourceProvider { +public class AllAppsContainerView extends BaseContainerView implements DragSource, + LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback, + AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener, + View.OnLongClickListener, ViewTreeObserver.OnPreDrawListener, + AllAppsSearchBarController.Callbacks, Stats.LaunchSourceProvider { public static final boolean GRID_MERGE_SECTIONS = true; - private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; - private static final boolean DYNAMIC_HEADER_ELEVATION = true; - private static final boolean DISMISS_SEARCH_ON_BACK = true; - - private static final int FADE_IN_DURATION = 175; - private static final int FADE_OUT_DURATION = 100; - private static final int SEARCH_TRANSLATION_X_DP = 18; - @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; private LayoutInflater mLayoutInflater; @@ -194,16 +81,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.ItemDecoration mItemDecoration; - @Thunk FrameLayout mContentView; + @Thunk View mContent; + @Thunk View mContainerView; + @Thunk View mRevealView; @Thunk AllAppsRecyclerView mAppsRecyclerView; @Thunk ViewGroup mPredictionBarView; - private View mHeaderView; - @Thunk View mSearchBarContainerView; - private View mSearchButtonView; - private View mDismissSearchButtonView; - @Thunk AllAppsSearchEditView mSearchBarEditView; - - private HeaderElevationController mElevationController; + @Thunk AllAppsSearchBarController mSearchBarController; + private ViewGroup mSearchBarContainerView; + private View mSearchBarView; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; @@ -213,18 +98,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private final Point mIconLastTouchPos = new Point(); // This coordinate is used to proxy click and long-click events to the prediction bar icons private final Point mPredictionIconTouchDownPos = new Point(); - private int mContentMarginStart; // Normal container insets - private int mContainerInset; private int mPredictionBarHeight; private int mLastRecyclerViewScrollPos = -1; @Thunk boolean mFocusPredictionBarOnFirstBind; + private SpannableStringBuilder mSearchQueryBuilder = null; + private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; - private AppSearchManager mSearchManager; - public AllAppsContainerView(Context context) { this(context, null); } @@ -238,30 +121,24 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc Resources res = context.getResources(); mLauncher = (Launcher) context; + mLayoutInflater = LayoutInflater.from(context); DeviceProfile grid = mLauncher.getDeviceProfile(); - - mContainerInset = res.getDimensionPixelSize(R.dimen.all_apps_container_inset); mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx + Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) + 2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) + - res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding)); - - mLayoutInflater = LayoutInflater.from(context); + 2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding)); - mNumAppsPerRow = grid.allAppsNumCols; - mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols; - mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow); + mApps = new AlphabeticalAppsList(context); mApps.setAdapterChangedCallback(this); - mAdapter = new AllAppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this); + mAdapter = new AllAppsGridAdapter(context, mApps, this, this, mLauncher, this); mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message)); - mAdapter.setNumAppsPerRow(mNumAppsPerRow); mAdapter.setPredictionRowHeight(mPredictionBarHeight); + mApps.setAdapter(mAdapter); mLayoutManager = mAdapter.getLayoutManager(); mItemDecoration = mAdapter.getItemDecoration(); - mContentMarginStart = mAdapter.getContentMarginStart(); - mApps.setAdapter(mAdapter); - mSearchManager = mApps.newSimpleAppSearchManager(); + mSearchQueryBuilder = new SpannableStringBuilder(); + Selection.setSelection(mSearchQueryBuilder, 0); } /** @@ -285,11 +162,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mApps.addApps(apps); } - public void setSearchManager(AppSearchManager searchManager) { - mSearchManager.cancel(true); - mSearchManager = searchManager; - } - /** * Updates existing apps in the list */ @@ -305,13 +177,23 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } /** - * Hides the header bar + * Sets the search bar that shows above the a-z list. */ - public void hideHeaderBar() { - mHeaderView.setVisibility(View.GONE); - mElevationController.disable(); - onUpdateBackgrounds(); - onUpdatePaddings(); + public void setSearchBarController(AllAppsSearchBarController searchController) { + if (mSearchBarController != null) { + throw new RuntimeException("Expected search bar controller to only be set once"); + } + mSearchBarController = searchController; + mSearchBarController.initialize(mApps, this); + + // Add the new search view to the layout + View searchBarView = searchController.getView(mSearchBarContainerView); + mSearchBarContainerView.addView(searchBarView); + mSearchBarContainerView.setVisibility(View.VISIBLE); + mSearchBarView = searchBarView; + setHasSearchBar(); + + updateBackgroundAndPaddings(); } /** @@ -325,28 +207,43 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc * Returns the content view used for the launcher transitions. */ public View getContentView() { - return mContentView; + return mContainerView; + } + + /** + * Returns the all apps search view. + */ + public View getSearchBarView() { + return mSearchBarView; } /** * Returns the reveal view used for the launcher transitions. */ public View getRevealView() { - return findViewById(R.id.apps_view_transition_overlay); + return mRevealView; + } + + /** + * Returns an new instance of the default app search controller. + */ + public AllAppsSearchBarController newDefaultAppSearchController() { + return new DefaultAppSearchController(getContext(), this, mAppsRecyclerView); } @Override protected void onFinishInflate() { + super.onFinishInflate(); boolean isRtl = Utilities.isRtl(getResources()); mAdapter.setRtl(isRtl); + mContent = findViewById(R.id.content); - // Work around the search box getting first focus and showing the cursor by - // proxying the focus from the content view to the recycler view directly - mContentView = (FrameLayout) findViewById(R.id.apps_list); - mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() { + // This is a focus listener that proxies focus from a view into the list view. This is to + // work around the search box from getting first focus and showing the cursor. + View.OnFocusChangeListener focusProxyListener = new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { - if (v == mContentView && hasFocus) { + if (hasFocus) { if (!mApps.getPredictedApps().isEmpty()) { // If the prediction bar is going to be bound, then defer focusing until // it is first bound @@ -360,52 +257,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } } } - }); - - // Fix the header view elevation if not dynamically calculating it - mHeaderView = findViewById(R.id.header); - mHeaderView.setOnClickListener(this); - - mElevationController = Utilities.isLmpOrAbove() ? - new HeaderElevationControllerVL(mHeaderView) : - new HeaderElevationControllerV16(mHeaderView); - if (!DYNAMIC_HEADER_ELEVATION) { - mElevationController.onScroll(getResources() - .getDimensionPixelSize(R.dimen.all_apps_header_scroll_to_elevation)); - } - - // Fix the prediction bar size - mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); - lp.height = mPredictionBarHeight; - - mSearchButtonView = mHeaderView.findViewById(R.id.search_button); - mSearchBarContainerView = findViewById(R.id.app_search_container); - mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button); - mDismissSearchButtonView.setOnClickListener(this); - mSearchBarEditView = (AllAppsSearchEditView) findViewById(R.id.apps_search_box); - if (mSearchBarEditView != null) { - mSearchBarEditView.addTextChangedListener(this); - mSearchBarEditView.setOnEditorActionListener(this); - if (DISMISS_SEARCH_ON_BACK) { - mSearchBarEditView.setOnBackKeyListener( - new AllAppsSearchEditView.OnBackKeyListener() { - @Override - public void onBackKey() { - // Only hide the search field if there is no query, or if there - // are no filtered results - String query = Utilities.trim( - mSearchBarEditView.getEditableText().toString()); - if (query.isEmpty() || mApps.hasNoFilteredResults()) { - hideSearchField(true, true); - } - } - }); - } - } - mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view); + }; + mSearchBarContainerView = (ViewGroup) findViewById(R.id.search_box_container); + mSearchBarContainerView.setOnFocusChangeListener(focusProxyListener); + mContainerView = findViewById(R.id.all_apps_container); + mContainerView.setOnFocusChangeListener(focusProxyListener); + mRevealView = findViewById(R.id.all_apps_reveal); + + // Load the all apps recycler view + mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.collection); mAppsRecyclerView.setApps(mApps); - mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight); mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); @@ -413,8 +274,18 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc if (mItemDecoration != null) { mAppsRecyclerView.addItemDecoration(mItemDecoration); } - onUpdateBackgrounds(); - onUpdatePaddings(); + + // Fix the prediction bar height + mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); + lp.height = mPredictionBarHeight; + + updateBackgroundAndPaddings(); + } + + @Override + public void onBoundsChanged(Rect newBounds) { + mLauncher.updateOverlayBounds(newBounds); } @Override @@ -422,6 +293,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc updatePredictionBarVisibility(); List predictedApps = mApps.getPredictedApps(); + + // Remove extra prediction icons + while (mPredictionBarView.getChildCount() > mNumPredictedAppsPerRow) { + mPredictionBarView.removeViewAt(mPredictionBarView.getChildCount() - 1); + } + int childCount = mPredictionBarView.getChildCount(); for (int i = 0; i < mNumPredictedAppsPerRow; i++) { BubbleTextView icon; @@ -455,95 +332,111 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } @Override - protected void onFixedBoundsUpdated() { - // Update the number of items in the grid + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Update the number of items in the grid before we measure the view + int availableWidth = !mContentBounds.isEmpty() ? mContentBounds.width() : + MeasureSpec.getSize(widthMeasureSpec); DeviceProfile grid = mLauncher.getDeviceProfile(); - if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) { + grid.updateAppsViewNumCols(getResources(), availableWidth); + if (mNumAppsPerRow != grid.allAppsNumCols || + mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) { mNumAppsPerRow = grid.allAppsNumCols; mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols; mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); mAdapter.setNumAppsPerRow(mNumAppsPerRow); mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** - * Update the padding of the Apps view and children. To ensure that the RecyclerView has the - * full width to handle touches right to the edge of the screen, we only apply the top and - * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView - * itself. In particular, the left/right padding is applied to the background of the view, - * and then additionally inset by the start margin. + * Update the background and padding of the Apps view and children. Instead of insetting the + * container view, we inset the background and padding of the recycler view to allow for the + * recycler view to handle touch events (for fast scrolling) all the way to the edge. */ @Override - protected void onUpdatePaddings() { + protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) { boolean isRtl = Utilities.isRtl(getResources()); - boolean hasSearchBar = (mSearchBarEditView != null) && - (mSearchBarEditView.getVisibility() == View.VISIBLE); - // Set the background on the container, but let the recyclerView extend the full screen, - // so that the fast-scroller works on the edge as well. - mContentView.setPadding(0, 0, 0, 0); - - if (mFixedBounds.isEmpty()) { - // If there are no fixed bounds, then use the default padding and insets - setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right, - mContainerInset + mInsets.bottom); - } else { - // If there are fixed bounds, then we update the padding to reflect the fixed bounds. - setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, - mFixedBounds.bottom); - } - - // Update the apps recycler view, inset it by the container inset as well + // TODO: Use quantum_panel instead of quantum_panel_shape. + InsetDrawable background = new InsetDrawable( + getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0, + padding.right, 0); + mContainerView.setBackground(background); + mRevealView.setBackground(background.getConstantState().newDrawable()); + mAppsRecyclerView.updateBackgroundPadding(padding); + mAdapter.updateBackgroundPadding(padding); + + // Hack: We are going to let the recycler view take the full width, so reset the padding on + // the container to zero after setting the background and apply the top-bottom padding to + // the content view instead so that the launcher transition clips correctly. + mContent.setPadding(0, padding.top, 0, padding.bottom); + mContainerView.setPadding(0, 0, 0, 0); + + // Pad the recycler view by the background padding plus the start margin (for the section + // names) DeviceProfile grid = mLauncher.getDeviceProfile(); - int startMargin = grid.isPhone ? mContentMarginStart : 0; - int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; + int startMargin = grid.isPhone ? getResources().getDimensionPixelSize( + R.dimen.all_apps_grid_view_start_margin) : mAppsRecyclerView.getScrollbarWidth(); if (isRtl) { - mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), 0, - inset + startMargin, 0); + mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getScrollbarWidth(), 0, + padding.right + startMargin, 0); } else { - mAppsRecyclerView.setPadding(inset + startMargin, 0, - inset + mAppsRecyclerView.getScrollbarWidth(), 0); + mAppsRecyclerView.setPadding(padding.left + startMargin, 0, + padding.right + mAppsRecyclerView.getScrollbarWidth(), 0); } - // Update the header bar - if (hasSearchBar) { - FrameLayout.LayoutParams lp = - (FrameLayout.LayoutParams) mHeaderView.getLayoutParams(); - lp.leftMargin = lp.rightMargin = inset; - mHeaderView.requestLayout(); + // Inset the search bar to fit its bounds above the container + if (mSearchBarView != null) { + Rect backgroundPadding = new Rect(); + if (mSearchBarView.getBackground() != null) { + mSearchBarView.getBackground().getPadding(backgroundPadding); + } + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) + mSearchBarContainerView.getLayoutParams(); + lp.leftMargin = searchBarBounds.left - backgroundPadding.left; + lp.topMargin = searchBarBounds.top - backgroundPadding.top; + lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right; + mSearchBarContainerView.requestLayout(); } + // Update the prediction bar insets as well + mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); - lp.leftMargin = inset + mAppsRecyclerView.getScrollbarWidth(); - lp.rightMargin = inset + mAppsRecyclerView.getScrollbarWidth(); + lp.leftMargin = padding.left + mAppsRecyclerView.getScrollbarWidth(); + lp.rightMargin = padding.right + mAppsRecyclerView.getScrollbarWidth(); mPredictionBarView.requestLayout(); } - /** - * Update the background of the Apps view and children. - */ @Override - protected void onUpdateBackgrounds() { - int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; - - // Update the background of the reveal view and list to be inset with the fixed bound - // insets instead of the default insets - // TODO: Use quantum_panel instead of quantum_panel_shape. - InsetDrawable background = new InsetDrawable( - getContext().getResources().getDrawable(R.drawable.quantum_panel_shape), - inset, 0, inset, 0); - mContentView.setBackground(background); - mAppsRecyclerView.updateBackgroundPadding(background); - mAdapter.updateBackgroundPadding(background); - mElevationController.updateBackgroundPadding(background); - getRevealView().setBackground(background.getConstantState().newDrawable()); + public boolean onPreDraw() { + if (mNumAppsPerRow > 0) { + // Update the position of the prediction bar to match the scroll of the all apps list + synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition()); + } + return true; } @Override - public boolean onPreDraw() { - synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition()); - return true; + public boolean dispatchKeyEvent(KeyEvent event) { + // Determine if the key event was actual text, if so, focus the search bar and then dispatch + // the key normally so that it can process this key event + if (!mSearchBarController.isSearchFieldFocused() && + event.getAction() == KeyEvent.ACTION_DOWN) { + final int unicodeChar = event.getUnicodeChar(); + final boolean isKeyNotWhitespace = unicodeChar > 0 && + !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar); + if (isKeyNotWhitespace) { + boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder, + event.getKeyCode(), event); + if (gotKey && mSearchQueryBuilder.length() > 0) { + mSearchBarController.focusSearchField(); + } + } + } + + return super.dispatchKeyEvent(event); } @Override @@ -569,15 +462,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc return false; } - @Override - public void onClick(View v) { - if (v == mHeaderView) { - showSearchField(); - } else if (v == mDismissSearchButtonView) { - hideSearchField(true, true); - } - } - @Override public boolean onLongClick(View v) { // Return early if this is not initiated from a touch @@ -660,70 +544,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } } - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // Do nothing - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - // Do nothing - } - - @Override - public void afterTextChanged(final Editable s) { - String queryText = s.toString(); - if (queryText.isEmpty()) { - mSearchManager.cancel(true); - mApps.setOrderedFilter(null); - } else { - String formatStr = getResources().getString(R.string.all_apps_no_search_results); - mAdapter.setEmptySearchText(String.format(formatStr, queryText)); - - mSearchManager.cancel(false); - mSearchManager.doSearch(queryText, this); - } - scrollToTop(); - } - - @Override - public void onSearchResult(ArrayList apps) { - if (apps != null) { - mApps.setOrderedFilter(apps); - } - } - - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) { - // Skip the quick-launch if there isn't exactly one item - if (mApps.getSize() != 1) { - return false; - } - - List items = mApps.getAdapterItems(); - for (int i = 0; i < items.size(); i++) { - AlphabeticalAppsList.AdapterItem item = items.get(i); - if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { - mAppsRecyclerView.getChildAt(i).performClick(); - getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); - return true; - } - } - } - return false; - } - @Override public void onAdapterItemsChanged() { updatePredictionBarVisibility(); } - @Override - public View getContent() { - return null; - } - @Override public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { // Register for a pre-draw listener to synchronize the recycler view scroll to other views @@ -745,14 +570,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - if (mSearchBarEditView != null) { - if (toWorkspace) { - hideSearchField(false, false); - } - } if (toWorkspace) { getViewTreeObserver().removeOnPreDrawListener(this); mLastRecyclerViewScrollPos = -1; + + // Reset the search bar after transitioning home + mSearchBarController.reset(); } } @@ -763,9 +586,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private void synchronizeToRecyclerViewScrollPosition(int scrollY) { if (mLastRecyclerViewScrollPos != scrollY) { mLastRecyclerViewScrollPos = scrollY; - if (DYNAMIC_HEADER_ELEVATION) { - mElevationController.onScroll(scrollY); - } // Scroll the prediction bar with the contents of the recycler view mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop()); @@ -806,9 +626,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } } - if (!mFixedBounds.isEmpty()) { + if (!mContentBounds.isEmpty()) { // Outset the fixed bounds and check if the touch is outside all apps - Rect tmpRect = new Rect(mFixedBounds); + Rect tmpRect = new Rect(mContentBounds); tmpRect.inset(-grid.allAppsIconSizePx / 2, 0); if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) { mBoundsCheckLastTouchDownPos.set(x, y); @@ -874,6 +694,29 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc return false; } + @Override + public void onSearchResult(String query, ArrayList apps) { + if (apps != null) { + if (apps.isEmpty()) { + String formatStr = getResources().getString(R.string.all_apps_no_search_results); + mAdapter.setEmptySearchText(String.format(formatStr, query)); + } else { + mAppsRecyclerView.scrollToTop(); + } + mApps.setOrderedFilter(apps); + } + } + + @Override + public void clearSearchResult() { + mApps.setOrderedFilter(null); + + // Clear the search query + mSearchQueryBuilder.clear(); + mSearchQueryBuilder.clearSpans(); + Selection.setSelection(mSearchQueryBuilder, 0); + } + @Override public void fillInLaunchSourceData(Bundle sourceData) { // Since the other cases are caught by the AllAppsRecyclerView LaunchSourceProvider, we just @@ -889,11 +732,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private View findPredictedAppAtCoordinate(int x, int y) { Rect hitRect = new Rect(); - // Ensure we aren't hitting the search bar + // Ensure that are touching in the recycler view int[] coord = {x, y}; - Utilities.mapCoordInSelfToDescendent(mHeaderView, this, coord); - mHeaderView.getHitRect(hitRect); - if (hitRect.contains(coord[0], coord[1])) { + Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, coord); + mAppsRecyclerView.getHitRect(hitRect); + if (!hitRect.contains(coord[0], coord[1])) { return null; } @@ -914,95 +757,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc return null; } - /** - * Shows the search field. - */ - private void showSearchField() { - mSearchManager.connect(); - - // Show the search bar and focus the search - final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, - getContext().getResources().getDisplayMetrics()); - mSearchBarContainerView.setVisibility(View.VISIBLE); - mSearchBarContainerView.setAlpha(0f); - mSearchBarContainerView.setTranslationX(translationX); - mSearchBarContainerView.animate() - .alpha(1f) - .translationX(0) - .setDuration(FADE_IN_DURATION) - .withLayer() - .withEndAction(new Runnable() { - @Override - public void run() { - mSearchBarEditView.requestFocus(); - getInputMethodManager().showSoftInput(mSearchBarEditView, - InputMethodManager.SHOW_IMPLICIT); - } - }); - mSearchButtonView.animate() - .alpha(0f) - .translationX(-translationX) - .setDuration(FADE_OUT_DURATION) - .withLayer(); - } - - /** - * Hides the search field. - */ - @Thunk void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { - mSearchManager.cancel(true); - - final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; - final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, - getContext().getResources().getDisplayMetrics()); - if (animated) { - // Hide the search bar and focus the recycler view - mSearchBarContainerView.animate() - .alpha(0f) - .translationX(0) - .setDuration(FADE_IN_DURATION) - .withLayer() - .withEndAction(new Runnable() { - @Override - public void run() { - mSearchBarContainerView.setVisibility(View.INVISIBLE); - if (resetTextField) { - mSearchBarEditView.setText(""); - } - mApps.setOrderedFilter(null); - if (returnFocusToRecyclerView) { - mAppsRecyclerView.requestFocus(); - } - } - }); - mSearchButtonView.setTranslationX(-translationX); - mSearchButtonView.animate() - .alpha(1f) - .translationX(0) - .setDuration(FADE_OUT_DURATION) - .withLayer(); - } else { - mSearchBarContainerView.setVisibility(View.INVISIBLE); - if (resetTextField) { - mSearchBarEditView.setText(""); - } - mApps.setOrderedFilter(null); - mSearchButtonView.setAlpha(1f); - mSearchButtonView.setTranslationX(0f); - if (returnFocusToRecyclerView) { - mAppsRecyclerView.requestFocus(); - } - } - getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0); - } - /** * Updates the visibility of the prediction bar. * @return whether the prediction bar is visible */ private boolean updatePredictionBarVisibility() { - boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() && (!mApps.hasFilter() || - mSearchBarEditView.getEditableText().toString().isEmpty()); + boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() && + (!mApps.hasFilter() || mSearchBarController.shouldShowPredictionBar()); if (showPredictionBar) { mPredictionBarView.setVisibility(View.VISIBLE); } else if (!showPredictionBar) { @@ -1010,11 +771,4 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } return showPredictionBar; } - - /** - * Returns an input method manager. - */ - @Thunk InputMethodManager getInputMethodManager() { - return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 4b8b2dfc8..dc0d27cd2 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -109,6 +109,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter mCachedSectionBounds = new HashMap<>(); @@ -121,10 +122,17 @@ class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); boolean hasDrawnPredictedAppsDivider = false; @@ -171,8 +179,8 @@ class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); - // Skip early if there are no items. - if (items.isEmpty()) { + // Skip early if there are no items or we haven't been measured + if (items.isEmpty() || mNumAppsPerRow == 0) { verticalScrollbarBounds.setEmpty(); return; } @@ -242,8 +234,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView int height = getHeight() - getPaddingTop() - getPaddingBottom(); int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight; if (totalScrollHeight > height) { - int scrollbarHeight = Math.max(mScrollbarMinHeight, - (int) (height / ((float) totalScrollHeight / height))); + int scrollbarHeight = (int) (height / ((float) totalScrollHeight / height)); // Calculate the position and size of the scroll bar if (Utilities.isRtl(getResources())) { @@ -277,8 +268,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView stateOut.rowTopOffset = -1; stateOut.rowHeight = -1; - // Return early if there are no items - if (items.isEmpty()) { + // Return early if there are no items or we haven't been measured + if (items.isEmpty() || mNumAppsPerRow == 0) { return; } diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java new file mode 100644 index 000000000..3cacd9d69 --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java @@ -0,0 +1,97 @@ +/* + * 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.allapps; + +import android.content.ComponentName; +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +/** + * An interface to a search box that AllApps can command. + */ +public abstract class AllAppsSearchBarController { + + protected AlphabeticalAppsList mApps; + protected Callbacks mCb; + + /** + * Sets the references to the apps model and the search result callback. + */ + public final void initialize(AlphabeticalAppsList apps, Callbacks cb) { + mApps = apps; + mCb = cb; + onInitialize(); + } + + /** + * To be overridden by subclasses. This method will get called when the controller is set, + * before getView(). + */ + protected abstract void onInitialize(); + + /** + * Returns the search bar view. + * @param parent the parent to attach the search bar view to. + */ + public abstract View getView(ViewGroup parent); + + /** + * Focuses the search field to handle key events. + */ + public abstract void focusSearchField(); + + /** + * Returns whether the search field is focused. + */ + public abstract boolean isSearchFieldFocused(); + + /** + * Resets the search bar state. + */ + public abstract void reset(); + + /** + * Returns whether the prediction bar should currently be visible depending on the state of + * the search bar. + */ + public abstract boolean shouldShowPredictionBar(); + + /** + * Callback for getting search results. + */ + public interface Callbacks { + + /** + * Called when the bounds of the search bar has changed. + */ + void onBoundsChanged(Rect newBounds); + + /** + * Called when the search is complete. + * + * @param apps sorted list of matching components or null if in case of failure. + */ + void onSearchResult(String query, ArrayList apps); + + /** + * Called when the search results should be cleared. + */ + void clearSearchResult(); + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index e284f77c4..a0cf5b6dc 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -235,11 +235,10 @@ public class AlphabeticalAppsList { private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; - public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) { + public AlphabeticalAppsList(Context context) { mLauncher = (Launcher) context; mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppNameComparator(context); - setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow); } /** @@ -249,10 +248,6 @@ public class AlphabeticalAppsList { mAdapterChangedCallback = cb; } - public SimpleAppSearchManagerImpl newSimpleAppSearchManager() { - return new SimpleAppSearchManagerImpl(mApps); - } - /** * Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED. */ @@ -269,7 +264,7 @@ public class AlphabeticalAppsList { mNumAppsPerRow = numAppsPerRow; mNumPredictedAppsPerRow = numPredictedAppsPerRow; - onAppsUpdated(); + updateAdapterItems(); } /** @@ -279,6 +274,13 @@ public class AlphabeticalAppsList { mAdapter = adapter; } + /** + * Returns all the apps. + */ + public List getApps() { + return mApps; + } + /** * Returns sections of all the current filtered applications. */ @@ -597,6 +599,11 @@ public class AlphabeticalAppsList { * Merges multiple sections to reduce visual raggedness. */ private void mergeSections() { + // Ignore merging until we have a valid row size + if (mNumAppsPerRow == 0) { + return; + } + // Go through each section and try and merge some of the sections if (AllAppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) { int sectionAppCount = 0; diff --git a/src/com/android/launcher3/allapps/AppSearchManager.java b/src/com/android/launcher3/allapps/AppSearchManager.java deleted file mode 100644 index b6aa22341..000000000 --- a/src/com/android/launcher3/allapps/AppSearchManager.java +++ /dev/null @@ -1,59 +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.allapps; - -import android.content.ComponentName; - -import java.util.ArrayList; - -/** - * Interface for handling app search. - */ -public interface AppSearchManager { - - /** - * Called when the search is about to be used. This method is optional for making a query but - * calling this appropriately can improve the initial response time. - */ - void connect(); - - /** - * Cancels all pending search requests. - * - * @param interruptActiveRequests if true, any active requests which are already executing will - * be invalidated, and the corresponding results will not be sent. The client should usually - * set this to true, before beginning a new search session. - */ - void cancel(boolean interruptActiveRequests); - - /** - * Performs a search - */ - void doSearch(String query, AppSearchResultCallback callback); - - /** - * Callback for getting search results. - */ - public interface AppSearchResultCallback { - - /** - * Called when the search is complete. - * - * @param apps sorted list of matching components or null if in case of failure. - */ - void onSearchResult(ArrayList apps); - } -} diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java new file mode 100644 index 000000000..28854be0e --- /dev/null +++ b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java @@ -0,0 +1,91 @@ +/* + * 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.allapps; + +import android.content.ComponentName; +import android.os.Handler; +import com.android.launcher3.AppInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * The default search implementation. + */ +public class DefaultAppSearchAlgorithm { + + private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); + + private final List mApps; + private final Handler mResultHandler; + + public DefaultAppSearchAlgorithm(List apps) { + mApps = apps; + mResultHandler = new Handler(); + } + + public void cancel(boolean interruptActiveRequests) { + if (interruptActiveRequests) { + mResultHandler.removeCallbacksAndMessages(null); + } + } + + public void doSearch(final String query, + final AllAppsSearchBarController.Callbacks callback) { + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = query.toLowerCase(); + final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); + final ArrayList result = new ArrayList<>(); + int total = mApps.size(); + + for (int i = 0; i < total; i++) { + AppInfo info = mApps.get(i); + if (!result.contains(info.componentName) && matches(info, queryWords)) { + result.add(info.componentName); + } + } + mResultHandler.post(new Runnable() { + + @Override + public void run() { + callback.onSearchResult(query, result); + } + }); + } + + private boolean matches(AppInfo info, String[] queryWords) { + String title = info.title.toString(); + String[] words = SPLIT_PATTERN.split(title.toLowerCase()); + for (int qi = 0; qi < queryWords.length; qi++) { + boolean foundMatch = false; + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(queryWords[qi])) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + // If there is a word in the query that does not match any words in this + // title, so skip it. + return false; + } + } + return true; + } + +} diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java new file mode 100644 index 000000000..1601c62df --- /dev/null +++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java @@ -0,0 +1,270 @@ +/* + * 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.allapps; + +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.TextView; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.Thunk; + +import java.util.List; + + +/** + * The default search controller. + */ +final class DefaultAppSearchController extends AllAppsSearchBarController + implements TextWatcher, TextView.OnEditorActionListener, View.OnClickListener { + + private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; + + private static final int FADE_IN_DURATION = 175; + private static final int FADE_OUT_DURATION = 100; + private static final int SEARCH_TRANSLATION_X_DP = 18; + + private final Context mContext; + @Thunk final InputMethodManager mInputMethodManager; + + private DefaultAppSearchAlgorithm mSearchManager; + + private ViewGroup mContainerView; + private View mSearchView; + @Thunk View mSearchBarContainerView; + private View mSearchButtonView; + private View mDismissSearchButtonView; + @Thunk AllAppsSearchEditView mSearchBarEditView; + @Thunk AllAppsRecyclerView mAppsRecyclerView; + private Runnable mFocusRecyclerViewRunnable = new Runnable() { + @Override + public void run() { + mAppsRecyclerView.requestFocus(); + } + }; + + public DefaultAppSearchController(Context context, ViewGroup containerView, + AllAppsRecyclerView appsRecyclerView) { + mContext = context; + mInputMethodManager = (InputMethodManager) + mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + mContainerView = containerView; + mAppsRecyclerView = appsRecyclerView; + } + + @Override + public View getView(ViewGroup parent) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false); + mSearchView.setOnClickListener(this); + + mSearchButtonView = mSearchView.findViewById(R.id.search_button); + mSearchBarContainerView = mSearchView.findViewById(R.id.search_container); + mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button); + mDismissSearchButtonView.setOnClickListener(this); + mSearchBarEditView = (AllAppsSearchEditView) + mSearchBarContainerView.findViewById(R.id.search_box); + mSearchBarEditView.addTextChangedListener(this); + mSearchBarEditView.setOnEditorActionListener(this); + mSearchBarEditView.setOnBackKeyListener( + new AllAppsSearchEditView.OnBackKeyListener() { + @Override + public void onBackKey() { + // Only hide the search field if there is no query, or if there + // are no filtered results + String query = Utilities.trim( + mSearchBarEditView.getEditableText().toString()); + if (query.isEmpty() || mApps.hasNoFilteredResults()) { + hideSearchField(true, mFocusRecyclerViewRunnable); + } + } + }); + return mSearchView; + } + + @Override + public void focusSearchField() { + mSearchBarEditView.requestFocus(); + showSearchField(); + } + + @Override + public boolean isSearchFieldFocused() { + return mSearchBarEditView.isFocused(); + } + + @Override + protected void onInitialize() { + mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps()); + } + + @Override + public void reset() { + hideSearchField(false, null); + } + + @Override + public boolean shouldShowPredictionBar() { + // Keep showing the prediction bar if the input query is empty + return mSearchBarEditView.getEditableText().toString().isEmpty(); + } + + @Override + public void onClick(View v) { + if (v == mSearchView) { + showSearchField(); + } else if (v == mDismissSearchButtonView) { + hideSearchField(true, mFocusRecyclerViewRunnable); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + @Override + public void afterTextChanged(final Editable s) { + String query = s.toString(); + if (query.isEmpty()) { + mSearchManager.cancel(true); + mCb.clearSearchResult(); + } else { + mSearchManager.cancel(false); + mSearchManager.doSearch(query, mCb); + } + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + // Skip if we disallow app-launch-on-enter + if (!ALLOW_SINGLE_APP_LAUNCH) { + return false; + } + // Skip if it's not the right action + if (actionId != EditorInfo.IME_ACTION_DONE) { + return false; + } + // Skip if there isn't exactly one item + if (mApps.getSize() != 1) { + return false; + } + // If there is exactly one icon, then quick-launch it + List items = mApps.getAdapterItems(); + for (int i = 0; i < items.size(); i++) { + AlphabeticalAppsList.AdapterItem item = items.get(i); + if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { + mAppsRecyclerView.getChildAt(i).performClick(); + mInputMethodManager.hideSoftInputFromWindow( + mContainerView.getWindowToken(), 0); + return true; + } + } + return false; + } + + /** + * Focuses the search field. + */ + private void showSearchField() { + // Show the search bar and focus the search + final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, + mContext.getResources().getDisplayMetrics()); + mSearchBarContainerView.setVisibility(View.VISIBLE); + mSearchBarContainerView.setAlpha(0f); + mSearchBarContainerView.setTranslationX(translationX); + mSearchBarContainerView.animate() + .alpha(1f) + .translationX(0) + .setDuration(FADE_IN_DURATION) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mSearchBarEditView.requestFocus(); + mInputMethodManager.showSoftInput(mSearchBarEditView, + InputMethodManager.SHOW_IMPLICIT); + } + }); + mSearchButtonView.animate() + .alpha(0f) + .translationX(-translationX) + .setDuration(FADE_OUT_DURATION) + .withLayer(); + } + + /** + * Unfocuses the search field. + */ + @Thunk void hideSearchField(boolean animated, final Runnable postAnimationRunnable) { + mSearchManager.cancel(true); + + final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; + final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP, + mContext.getResources().getDisplayMetrics()); + if (animated) { + // Hide the search bar and focus the recycler view + mSearchBarContainerView.animate() + .alpha(0f) + .translationX(0) + .setDuration(FADE_IN_DURATION) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mSearchBarContainerView.setVisibility(View.INVISIBLE); + if (resetTextField) { + mSearchBarEditView.setText(""); + } + mCb.clearSearchResult(); + if (postAnimationRunnable != null) { + postAnimationRunnable.run(); + } + } + }); + mSearchButtonView.setTranslationX(-translationX); + mSearchButtonView.animate() + .alpha(1f) + .translationX(0) + .setDuration(FADE_OUT_DURATION) + .withLayer(); + } else { + mSearchBarContainerView.setVisibility(View.INVISIBLE); + if (resetTextField) { + mSearchBarEditView.setText(""); + } + mCb.clearSearchResult(); + mSearchButtonView.setAlpha(1f); + mSearchButtonView.setTranslationX(0f); + if (postAnimationRunnable != null) { + postAnimationRunnable.run(); + } + } + mInputMethodManager.hideSoftInputFromWindow(mContainerView.getWindowToken(), 0); + } +} diff --git a/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java b/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java deleted file mode 100644 index e8a31b546..000000000 --- a/src/com/android/launcher3/allapps/SimpleAppSearchManagerImpl.java +++ /dev/null @@ -1,98 +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.allapps; - -import android.content.ComponentName; -import android.os.Handler; - -import com.android.launcher3.AppInfo; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -/** - * An {@link AppSearchManager} which does label matching on the UI thread. - */ -public class SimpleAppSearchManagerImpl implements AppSearchManager { - - private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); - - private final List mApps; - private final Handler mResultHandler; - - public SimpleAppSearchManagerImpl(List apps) { - mApps = apps; - mResultHandler = new Handler(); - } - - @Override - public void connect() { - // No op - } - - @Override - public void cancel(boolean interruptActiveRequests) { - if (interruptActiveRequests) { - mResultHandler.removeCallbacksAndMessages(null); - } - } - - @Override - public void doSearch(String query, final AppSearchResultCallback callback) { - // Do an intersection of the words in the query and each title, and filter out all the - // apps that don't match all of the words in the query. - final String queryTextLower = query.toLowerCase(); - final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); - final ArrayList result = new ArrayList(); - int total = mApps.size(); - - for (int i = 0; i < total; i++) { - AppInfo info = mApps.get(i); - if (!result.contains(info.componentName) && matches(info, queryWords)) { - result.add(info.componentName); - } - } - mResultHandler.post(new Runnable() { - - @Override - public void run() { - callback.onSearchResult(result); - } - }); - } - - private boolean matches(AppInfo info, String[] queryWords) { - String title = info.title.toString(); - String[] words = SPLIT_PATTERN.split(title.toLowerCase()); - for (int qi = 0; qi < queryWords.length; qi++) { - boolean foundMatch = false; - for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(queryWords[qi])) { - foundMatch = true; - break; - } - } - if (!foundMatch) { - // If there is a word in the query that does not match any words in this - // title, so skip it. - return false; - } - } - return true; - } - -} diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 51f2a5f05..500311add 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -66,6 +66,7 @@ public class WidgetsContainerView extends BaseContainerView private IconCache mIconCache; /* Recycler view related member variables */ + private View mContent; private WidgetsRecyclerView mView; private WidgetsListAdapter mAdapter; @@ -98,6 +99,7 @@ public class WidgetsContainerView extends BaseContainerView @Override protected void onFinishInflate() { + mContent = findViewById(R.id.content); mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view); mView.setAdapter(mAdapter); @@ -112,7 +114,6 @@ public class WidgetsContainerView extends BaseContainerView }); mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); - onUpdatePaddings(); } // @@ -335,33 +336,18 @@ public class WidgetsContainerView extends BaseContainerView // @Override - protected void onUpdatePaddings() { - if (mFixedBounds.isEmpty()) { - // If there are no fixed bounds, then use the default padding and insets - setPadding(mPadding.left + mInsets.left, mPadding.top + mInsets.top, - mPadding.right + mInsets.right, mPadding.bottom + mInsets.bottom); - } else { - // If there are fixed bounds, then we update the padding to reflect the fixed bounds. - setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, - mFixedBounds.bottom); - } - - int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset; - mView.setPadding(inset + mView.getScrollbarWidth(), inset, - inset, inset); - } - - @Override - protected void onUpdateBackgrounds() { - InsetDrawable background; - // Update the background of the reveal view and list to be inset with the fixed bound - // insets instead of the default insets - // TODO: Use quantum_panel instead of quantum_panel_shape. - int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset; - background = new InsetDrawable( - getContext().getResources().getDrawable(R.drawable.quantum_panel_shape), - inset, 0, inset, 0); - mView.updateBackgroundPadding(background); + protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) { + // Apply the top-bottom padding to the content itself so that the launcher transition is + // clipped correctly + mContent.setPadding(0, padding.top, 0, padding.bottom); + + // TODO: Use quantum_panel_dark instead of quantum_panel_shape_dark. + InsetDrawable background = new InsetDrawable( + getResources().getDrawable(R.drawable.quantum_panel_shape_dark), padding.left, 0, + padding.right, 0); + mView.setBackground(background); + getRevealView().setBackground(background.getConstantState().newDrawable()); + mView.updateBackgroundPadding(padding); } /** diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 9d265f87e..fa7e2f0a2 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -18,11 +18,9 @@ package com.android.launcher3.widget; import android.content.Context; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.util.AttributeSet; import android.view.View; - import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.Utilities; import com.android.launcher3.model.WidgetsModel; @@ -35,7 +33,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; - private Rect mBackgroundPadding = new Rect(); public WidgetsRecyclerView(Context context) { this(context, null); @@ -61,10 +58,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView { addOnItemTouchListener(this); } - public void updateBackgroundPadding(Drawable background) { - background.getPadding(mBackgroundPadding); - } - /** * Sets the widget model in this view, used to determine the fast scroll position. */ -- cgit v1.2.3 From fbc5b18626ae2e158e39c59606455124cfa8127d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 12 Jun 2015 14:18:55 -0700 Subject: Refactored section names to only draw when there is space. - This CL removes all space for section names in both phones and tablets. And when there are no section names, the layout will automatically fully merge the sections. Bug: 20222023 Change-Id: Ic7c751d86f095e5cbd690bfd4f94bb5b00ff8ae4 --- res/values/dimens.xml | 4 +- src/com/android/launcher3/BaseRecyclerView.java | 29 +++++++- .../launcher3/allapps/AllAppsContainerView.java | 84 +++++++++++++++++++-- .../launcher3/allapps/AllAppsGridAdapter.java | 26 +++---- .../launcher3/allapps/AllAppsRecyclerView.java | 12 ++- .../launcher3/allapps/AlphabeticalAppsList.java | 87 ++-------------------- 6 files changed, 134 insertions(+), 108 deletions(-) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4c9d57aec..da56d9049 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -55,7 +55,7 @@ 4dp - 56dp + 0dp 8dp 24sp 48dp @@ -65,7 +65,7 @@ 16dp 4dp - -16dp + -24dp 72dp 48dp diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 6dd029f17..140c28c0c 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; @@ -315,12 +317,37 @@ public class BaseRecyclerView extends RecyclerView /** * Animates the visibility of the fast scroller popup. */ - private void animateFastScrollerVisibility(boolean visible) { + private void animateFastScrollerVisibility(final boolean visible) { ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); anim.setDuration(visible ? 200 : 150); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (visible) { + onFastScrollingStart(); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!visible) { + onFastScrollingEnd(); + } + } + }); anim.start(); } + /** + * To be overridden by subclasses. + */ + protected void onFastScrollingStart() {} + + /** + * To be overridden by subclasses. + */ + protected void onFastScrollingEnd() {} + /** * Invalidates the fast scroller popup. */ diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index b300cae62..083e3c19e 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -58,11 +58,70 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.util.Thunk; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; import java.util.ArrayList; import java.util.List; +/** + * A merge algorithm that merges every section indiscriminately. + */ +final class FullMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm { + + @Override + public boolean continueMerging(AlphabeticalAppsList.SectionInfo section, + AlphabeticalAppsList.SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount) { + // Merge EVERYTHING + return true; + } +} + +/** + * The logic we use to merge multiple sections. We only merge sections when their final row + * contains less than a certain number of icons, and stop at a specified max number of merges. + * In addition, we will try and not merge sections that identify apps from different scripts. + */ +final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm { + + private int mMinAppsPerRow; + private int mMinRowsInMergedSection; + private int mMaxAllowableMerges; + private CharsetEncoder mAsciiEncoder; + + public SimpleSectionMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) { + mMinAppsPerRow = minAppsPerRow; + mMinRowsInMergedSection = minRowsInMergedSection; + mMaxAllowableMerges = maxNumMerges; + mAsciiEncoder = Charset.forName("US-ASCII").newEncoder(); + } + + @Override + public boolean continueMerging(AlphabeticalAppsList.SectionInfo section, + AlphabeticalAppsList.SectionInfo withSection, + int sectionAppCount, int numAppsPerRow, int mergeCount) { + // Continue merging if the number of hanging apps on the final row is less than some + // fixed number (ragged), the merged rows has yet to exceed some minimum row count, + // and while the number of merged sections is less than some fixed number of merges + int rows = sectionAppCount / numAppsPerRow; + int cols = sectionAppCount % numAppsPerRow; + + // Ensure that we do not merge across scripts, currently we only allow for english and + // native scripts so we can test if both can just be ascii encoded + boolean isCrossScript = false; + if (section.firstAppItem != null && withSection.firstAppItem != null) { + isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) != + mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName); + } + return (0 < cols && cols < mMinAppsPerRow) && + rows < mMinRowsInMergedSection && + mergeCount < mMaxAllowableMerges && + !isCrossScript; + } +} + /** * The all apps view container. */ @@ -72,7 +131,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc View.OnLongClickListener, ViewTreeObserver.OnPreDrawListener, AllAppsSearchBarController.Callbacks, Stats.LaunchSourceProvider { - public static final boolean GRID_MERGE_SECTIONS = true; + private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3; + private static final int MAX_NUM_MERGES_PHONE = 2; @Thunk Launcher mLauncher; @Thunk AlphabeticalAppsList mApps; @@ -90,6 +150,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private ViewGroup mSearchBarContainerView; private View mSearchBarView; + private int mSectionNamesMargin; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; // This coordinate is relative to this container view @@ -127,7 +188,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) + 2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) + 2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding)); - + mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); mApps = new AlphabeticalAppsList(context); mApps.setAdapterChangedCallback(this); mAdapter = new AllAppsGridAdapter(context, mApps, this, this, mLauncher, this); @@ -342,9 +403,18 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) { mNumAppsPerRow = grid.allAppsNumCols; mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols; + + // If there is a start margin to draw section names, determine how we are going to merge + // app sections + boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone; + AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ? + new FullMergeAlgorithm() : + new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f), + MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); + mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); mAdapter.setNumAppsPerRow(mNumAppsPerRow); - mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); + mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -376,14 +446,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // Pad the recycler view by the background padding plus the start margin (for the section // names) - DeviceProfile grid = mLauncher.getDeviceProfile(); - int startMargin = grid.isPhone ? getResources().getDimensionPixelSize( - R.dimen.all_apps_grid_view_start_margin) : mAppsRecyclerView.getScrollbarWidth(); + int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getScrollbarWidth()); if (isRtl) { mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getScrollbarWidth(), 0, - padding.right + startMargin, 0); + padding.right + startInset, 0); } else { - mAppsRecyclerView.setPadding(padding.left + startMargin, 0, + mAppsRecyclerView.setPadding(padding.left + startInset, 0, padding.right + mAppsRecyclerView.getScrollbarWidth(), 0); } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index dc0d27cd2..68407bdd5 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -21,7 +21,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; @@ -32,7 +31,6 @@ import android.view.ViewGroup; import android.widget.TextView; import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -114,11 +112,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter mCachedSectionBounds = new HashMap<>(); private Rect mTmpBounds = new Rect(); - private Launcher mLauncher; - - public GridItemDecoration(Context context) { - mLauncher = (Launcher) context; - } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { @@ -129,13 +122,13 @@ class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); boolean hasDrawnPredictedAppsDivider = false; + boolean showSectionNames = mSectionNamesMargin > 0; int childCount = parent.getChildCount(); int lastSectionTop = 0; int lastSectionHeight = 0; @@ -154,7 +147,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter Date: Fri, 12 Jun 2015 21:18:53 -0700 Subject: Removing ContentObserver in launcher > Using callback insted of using a content observer > Setting the listener in LauncherAppState to prevent resource leak Change-Id: Id23a4d5c8812e86178997e536226e09ec3740f84 --- src/com/android/launcher3/Launcher.java | 38 ++---------------- src/com/android/launcher3/LauncherAppState.java | 3 +- src/com/android/launcher3/LauncherProvider.java | 45 +++++++++++----------- .../launcher3/LauncherProviderChangeListener.java | 2 + src/com/android/launcher3/WidgetPreviewLoader.java | 3 +- .../allapps/DefaultAppSearchController.java | 2 +- 6 files changed, 33 insertions(+), 60 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ce0f304c1..9989abb6d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -36,7 +36,6 @@ import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -48,7 +47,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; -import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -241,7 +239,6 @@ public class Launcher extends Activity private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); - private final ContentObserver mWidgetObserver = new AppWidgetResetObserver(); private LayoutInflater mInflater; @@ -438,7 +435,6 @@ public class Launcher extends Activity LauncherAppState.setApplicationContext(getApplicationContext()); LauncherAppState app = LauncherAppState.getInstance(); - LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this); // Load configuration-specific DeviceProfile mDeviceProfile = getResources().getConfiguration().orientation @@ -478,8 +474,6 @@ public class Launcher extends Activity setupViews(); mDeviceProfile.layout(this); - registerContentObservers(); - lockAllApps(); mSavedState = savedInstanceState; @@ -2017,7 +2011,6 @@ public class Launcher extends Activity TextKeyListener.getInstance().release(); - getContentResolver().unregisterContentObserver(mWidgetObserver); unregisterReceiver(mCloseSystemDialogsReceiver); mDragLayer.clearAllResizeFrames(); @@ -2386,16 +2379,6 @@ public class Launcher extends Activity sFolders.remove(folder.id); } - /** - * Registers various content observers. The current implementation registers - * only a favorites observer to keep track of the favorites applications. - */ - private void registerContentObservers() { - ContentResolver resolver = getContentResolver(); - resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI, - true, mWidgetObserver); - } - @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { @@ -2452,9 +2435,10 @@ public class Launcher extends Activity } /** - * Re-listen when widgets are reset. + * Re-listen when widget host is reset. */ - @Thunk void onAppWidgetReset() { + @Override + public void onAppWidgetHostReset() { if (mAppWidgetHost != null) { mAppWidgetHost.startListening(); } @@ -2939,7 +2923,7 @@ public class Launcher extends Activity return false; } - private boolean startActivitySafely(View v, Intent intent, Object tag) { + @Thunk boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); @@ -3561,20 +3545,6 @@ public class Launcher extends Activity } } - /** - * Receives notifications whenever the appwidgets are reset. - */ - private class AppWidgetResetObserver extends ContentObserver { - public AppWidgetResetObserver() { - super(new Handler()); - } - - @Override - public void onChange(boolean selfChange) { - onAppWidgetReset(); - } - } - /** * If the activity is currently paused, signal that we need to run the passed Runnable * in onResume. diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 540bdf814..0565d3f4b 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -84,7 +84,7 @@ public class LauncherAppState { mInvariantDeviceProfile = new InvariantDeviceProfile(sContext); mIconCache = new IconCache(sContext, mInvariantDeviceProfile); - mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache); + mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache); mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class)); @@ -125,6 +125,7 @@ public class LauncherAppState { } LauncherModel setLauncher(Launcher launcher) { + getLauncherProvider().setLauncherProviderChangeListener(launcher); mModel.initialize(launcher); mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ? new LauncherAccessibilityDelegate(launcher) : null; diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index cb808c22b..71ddb1ab1 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -80,16 +80,7 @@ public class LauncherProvider extends ContentProvider { private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name"; - private LauncherProviderChangeListener mListener; - - /** - * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when - * {@link AppWidgetHost#deleteHost()} is called during database creation. - * Use this to recall {@link AppWidgetHost#startListening()} if needed. - */ - static final Uri CONTENT_APPWIDGET_RESET_URI = - Uri.parse("content://" + AUTHORITY + "/appWidgetReset"); - + @Thunk LauncherProviderChangeListener mListener; @Thunk DatabaseHelper mOpenHelper; @Override @@ -279,6 +270,18 @@ public class LauncherProvider extends ContentProvider { } } + @Thunk void notifyAppWidgetHostReset() { + new MainThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + if (mListener != null) { + mListener.onAppWidgetHostReset(); + } + } + }); + } + @Thunk static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -455,17 +458,6 @@ public class LauncherProvider extends ContentProvider { return mNewDbCreated; } - /** - * Send notification that we've deleted the {@link AppWidgetHost}, - * probably as part of the initial database creation. The receiver may - * want to re-call {@link AppWidgetHost#startListening()} to ensure - * callbacks are correctly set. - */ - private void sendAppWidgetResetNotify() { - final ContentResolver resolver = mContext.getContentResolver(); - resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null); - } - @Override public void onCreate(SQLiteDatabase db) { if (LOGD) Log.d(TAG, "creating new launcher database"); @@ -509,7 +501,14 @@ public class LauncherProvider extends ContentProvider { // Database was just created, so wipe any previous widgets if (mAppWidgetHost != null) { mAppWidgetHost.deleteHost(); - sendAppWidgetResetNotify(); + + /** + * Send notification that we've deleted the {@link AppWidgetHost}, + * probably as part of the initial database creation. The receiver may + * want to re-call {@link AppWidgetHost#startListening()} to ensure + * callbacks are correctly set. + */ + LauncherAppState.getLauncherProvider().notifyAppWidgetHostReset(); } // Fresh and clean launcher DB. @@ -517,7 +516,7 @@ public class LauncherProvider extends ContentProvider { setFlagEmptyDbCreated(); // When a new DB is created, remove all previously stored managed profile information. - ManagedProfileHeuristic.processAllUsers(Collections.EMPTY_LIST, mContext); + ManagedProfileHeuristic.processAllUsers(Collections.emptyList(), mContext); } private void addWorkspacesTable(SQLiteDatabase db) { diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java index 5b5c6c5ab..1b78e9c18 100644 --- a/src/com/android/launcher3/LauncherProviderChangeListener.java +++ b/src/com/android/launcher3/LauncherProviderChangeListener.java @@ -10,4 +10,6 @@ public interface LauncherProviderChangeListener { public void onLauncherProviderChange(); public void onSettingsChanged(String settings, boolean value); + + public void onAppWidgetHostReset(); } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 75952d1a6..c1cc42b2c 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -27,6 +27,7 @@ import android.os.AsyncTask; import android.os.Handler; import android.util.Log; import android.util.LongSparseArray; + import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; @@ -69,7 +70,7 @@ public class WidgetPreviewLoader { private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); @Thunk final Handler mWorkerHandler; - public WidgetPreviewLoader(Context context, InvariantDeviceProfile inv, IconCache iconCache) { + public WidgetPreviewLoader(Context context, IconCache iconCache) { mContext = context; mIconCache = iconCache; mManager = AppWidgetManagerCompat.getInstance(context); diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java index 1601c62df..a61df3a7d 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java @@ -56,7 +56,7 @@ final class DefaultAppSearchController extends AllAppsSearchBarController private View mDismissSearchButtonView; @Thunk AllAppsSearchEditView mSearchBarEditView; @Thunk AllAppsRecyclerView mAppsRecyclerView; - private Runnable mFocusRecyclerViewRunnable = new Runnable() { + @Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() { @Override public void run() { mAppsRecyclerView.requestFocus(); -- cgit v1.2.3 From e98f4a4d6dac0aa4fc6b35b46ab10aff06251ffc Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 16 Jun 2015 10:45:24 -0700 Subject: Fix last bit of WidgetTray jank issue b/21133230 Change-Id: Ic198b8d21be1b0f3465cd4efc30a240e3ec4304a --- src/com/android/launcher3/WidgetPreviewLoader.java | 28 +++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 75952d1a6..46405ada7 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -55,6 +55,8 @@ public class WidgetPreviewLoader { /** * Weak reference objects, do not prevent their referents from being made finalizable, * finalized, and then reclaimed. + * Note: synchronized block used for this variable is expensive and the block should always + * be posted to a background thread. */ @Thunk Set mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap()); @@ -554,10 +556,15 @@ public class WidgetPreviewLoader { // in the tasks's onCancelled() call, and if cancelled while the task is writing to // disk, it will be cancelled in the task's onPostExecute() call. if (mTask.mBitmapToRecycle != null) { - synchronized (mUnusedBitmaps) { - mUnusedBitmaps.add(mTask.mBitmapToRecycle); - } - mTask.mBitmapToRecycle = null; + mWorkerHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mUnusedBitmaps) { + mUnusedBitmaps.add(mTask.mBitmapToRecycle); + } + mTask.mBitmapToRecycle = null; + } + }); } } } @@ -660,14 +667,19 @@ public class WidgetPreviewLoader { } @Override - protected void onCancelled(Bitmap preview) { + protected void onCancelled(final Bitmap preview) { // If we've cancelled while the task is running, then can return the bitmap to the // recycled set immediately. Otherwise, it will be recycled after the preview is written // to disk. if (preview != null) { - synchronized (mUnusedBitmaps) { - mUnusedBitmaps.add(preview); - } + mWorkerHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mUnusedBitmaps) { + mUnusedBitmaps.add(preview); + } + } + }); } } } -- cgit v1.2.3 From 5183285847816cee9d0db6a8a7ab1a5929163e4e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 16 Jun 2015 11:36:19 -0700 Subject: Using component key for app search results Change-Id: Idc610cde340331892a5fabfa8bf952d136675f81 --- src/com/android/launcher3/AppInfo.java | 5 ++ .../launcher3/allapps/AllAppsContainerView.java | 4 +- .../allapps/AllAppsSearchBarController.java | 4 +- .../launcher3/allapps/AlphabeticalAppsList.java | 83 +++++----------------- .../allapps/DefaultAppSearchAlgorithm.java | 35 ++++----- 5 files changed, 49 insertions(+), 82 deletions(-) diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 9c87ced54..c95d5585a 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -25,6 +25,7 @@ import android.util.Log; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.ComponentKey; import java.util.ArrayList; import java.util.Arrays; @@ -137,6 +138,10 @@ public class AppInfo extends ItemInfo { return new ShortcutInfo(this); } + public ComponentKey toComponentKey() { + return new ComponentKey(componentName, user); + } + public static Intent makeLaunchIntent(Context context, LauncherActivityInfoCompat info, UserHandleCompat user) { long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 083e3c19e..0fbe8e962 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -39,6 +39,7 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.LinearLayout; + import com.android.launcher3.AppInfo; import com.android.launcher3.BaseContainerView; import com.android.launcher3.BubbleTextView; @@ -56,6 +57,7 @@ import com.android.launcher3.R; import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; import java.nio.charset.Charset; @@ -763,7 +765,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } @Override - public void onSearchResult(String query, ArrayList apps) { + public void onSearchResult(String query, ArrayList apps) { if (apps != null) { if (apps.isEmpty()) { String formatStr = getResources().getString(R.string.all_apps_no_search_results); diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java index 3cacd9d69..341539cdd 100644 --- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java @@ -20,6 +20,8 @@ import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.util.ComponentKey; + import java.util.ArrayList; /** @@ -87,7 +89,7 @@ public abstract class AllAppsSearchBarController { * * @param apps sorted list of matching components or null if in case of failure. */ - void onSearchResult(String query, ArrayList apps); + void onSearchResult(String query, ArrayList apps); /** * Called when the search results should be cleared. diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 4f29e0c7a..b7b6ed7fc 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -19,12 +19,12 @@ import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; + import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.model.AbstractUserComparator; import com.android.launcher3.model.AppNameComparator; +import com.android.launcher3.util.ComponentKey; import java.util.ArrayList; import java.util.Collections; @@ -148,6 +148,8 @@ public class AlphabeticalAppsList { // The set of apps from the system not including predictions private final List mApps = new ArrayList<>(); + private final HashMap mComponentToAppMap = new HashMap<>(); + // The set of filtered apps with the current filter private List mFilteredApps = new ArrayList<>(); // The current set of adapter items @@ -161,7 +163,7 @@ public class AlphabeticalAppsList { // The set of predicted apps resolved from the component names and the current set of apps private List mPredictedApps = new ArrayList<>(); // The of ordered component names as a result of a search query - private ArrayList mSearchResults; + private ArrayList mSearchResults; private HashMap mCachedSectionNames = new HashMap<>(); private RecyclerView.Adapter mAdapter; private AlphabeticIndexCompat mIndexer; @@ -255,7 +257,7 @@ public class AlphabeticalAppsList { /** * Sets the sorted list of filtered components. */ - public void setOrderedFilter(ArrayList f) { + public void setOrderedFilter(ArrayList f) { if (mSearchResults != f) { mSearchResults = f; updateAdapterItems(); @@ -283,33 +285,23 @@ public class AlphabeticalAppsList { * Sets the current set of apps. */ public void setApps(List apps) { - mApps.clear(); - mApps.addAll(apps); - onAppsUpdated(); + mComponentToAppMap.clear(); + addApps(apps); } /** * Adds new apps to the list. */ public void addApps(List apps) { - // We add it in place, in alphabetical order - for (AppInfo info : apps) { - mApps.add(info); - } - onAppsUpdated(); + updateApps(apps); } /** * Updates existing apps in the list */ public void updateApps(List apps) { - for (AppInfo info : apps) { - int index = mApps.indexOf(info); - if (index != -1) { - mApps.set(index, info); - } else { - mApps.add(info); - } + for (AppInfo app : apps) { + mComponentToAppMap.put(app.toComponentKey(), app); } onAppsUpdated(); } @@ -318,36 +310,19 @@ public class AlphabeticalAppsList { * Removes some apps from the list. */ public void removeApps(List apps) { - for (AppInfo info : apps) { - int removeIndex = findAppByComponent(mApps, info); - if (removeIndex != -1) { - mApps.remove(removeIndex); - } + for (AppInfo app : apps) { + mComponentToAppMap.remove(app.toComponentKey()); } onAppsUpdated(); } - /** - * Finds the index of an app given a target AppInfo. - */ - private int findAppByComponent(List apps, AppInfo targetInfo) { - ComponentName targetComponent = targetInfo.intent.getComponent(); - int length = apps.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = apps.get(i); - if (info.user.equals(targetInfo.user) - && info.intent.getComponent().equals(targetComponent)) { - return i; - } - } - return -1; - } - /** * Updates internals when the set of apps are updated. */ private void onAppsUpdated() { // Sort the list of apps + mApps.clear(); + mApps.addAll(mComponentToAppMap.values()); Collections.sort(mApps, mAppNameComparator.getAppInfoComparator()); // As a special case for some languages (currently only Simplified Chinese), we may need to @@ -494,33 +469,13 @@ public class AlphabeticalAppsList { return mApps; } - int total = mSearchResults.size(); - final HashMap sortOrder = new HashMap<>(total); - for (int i = 0; i < total; i++) { - sortOrder.put(mSearchResults.get(i), i); - } - ArrayList result = new ArrayList<>(); - for (AppInfo info : mApps) { - if (sortOrder.containsKey(info.componentName)) { - result.add(info); + for (ComponentKey key : mSearchResults) { + AppInfo match = mComponentToAppMap.get(key); + if (match != null) { + result.add(match); } } - - Collections.sort(result, new AbstractUserComparator( - LauncherAppState.getInstance().getContext()) { - - @Override - public int compare(AppInfo lhs, AppInfo rhs) { - Integer indexA = sortOrder.get(lhs.componentName); - int result = indexA.compareTo(sortOrder.get(rhs.componentName)); - if (result == 0) { - return super.compare(lhs, rhs); - } else { - return result; - } - } - }); return result; } diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java index 28854be0e..9ca5ccd06 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java @@ -15,9 +15,10 @@ */ package com.android.launcher3.allapps; -import android.content.ComponentName; import android.os.Handler; + import com.android.launcher3.AppInfo; +import com.android.launcher3.util.ComponentKey; import java.util.ArrayList; import java.util.List; @@ -46,19 +47,7 @@ public class DefaultAppSearchAlgorithm { public void doSearch(final String query, final AllAppsSearchBarController.Callbacks callback) { - // Do an intersection of the words in the query and each title, and filter out all the - // apps that don't match all of the words in the query. - final String queryTextLower = query.toLowerCase(); - final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); - final ArrayList result = new ArrayList<>(); - int total = mApps.size(); - - for (int i = 0; i < total; i++) { - AppInfo info = mApps.get(i); - if (!result.contains(info.componentName) && matches(info, queryWords)) { - result.add(info.componentName); - } - } + final ArrayList result = getTitleMatchResult(query); mResultHandler.post(new Runnable() { @Override @@ -68,7 +57,22 @@ public class DefaultAppSearchAlgorithm { }); } - private boolean matches(AppInfo info, String[] queryWords) { + protected ArrayList getTitleMatchResult(String query) { + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = query.toLowerCase(); + final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); + + final ArrayList result = new ArrayList<>(); + for (AppInfo info : mApps) { + if (matches(info, queryWords)) { + result.add(info.toComponentKey()); + } + } + return result; + } + + protected boolean matches(AppInfo info, String[] queryWords) { String title = info.title.toString(); String[] words = SPLIT_PATTERN.split(title.toLowerCase()); for (int qi = 0; qi < queryWords.length; qi++) { @@ -87,5 +91,4 @@ public class DefaultAppSearchAlgorithm { } return true; } - } -- cgit v1.2.3 From b4cbea4ad4ce06b591603a47f86cfd9df838ccb1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 16 Jun 2015 15:10:36 -0700 Subject: Fixing nullpointer when creating new DB LauncherProvider is not ready until the DBHelper is created Change-Id: Iabd61005892f15fd4a31d882100d87df2b2a7b85 --- src/com/android/launcher3/LauncherProvider.java | 26 +++++++++++----------- src/com/android/launcher3/WidgetPreviewLoader.java | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 71ddb1ab1..4df92af7c 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -99,6 +99,7 @@ public class LauncherProvider extends ContentProvider { public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) { mListener = listener; + mOpenHelper.mListener = mListener; } @Override @@ -270,18 +271,6 @@ public class LauncherProvider extends ContentProvider { } } - @Thunk void notifyAppWidgetHostReset() { - new MainThreadExecutor().execute(new Runnable() { - - @Override - public void run() { - if (mListener != null) { - mListener.onAppWidgetHostReset(); - } - } - }); - } - @Thunk static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -429,6 +418,7 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase.deleteDatabase(dbFile); } mOpenHelper = new DatabaseHelper(getContext()); + mOpenHelper.mListener = mListener; } private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback { @@ -439,6 +429,8 @@ public class LauncherProvider extends ContentProvider { private boolean mNewDbCreated = false; + @Thunk LauncherProviderChangeListener mListener; + DatabaseHelper(Context context) { super(context, LauncherFiles.LAUNCHER_DB, null, DATABASE_VERSION); mContext = context; @@ -508,7 +500,15 @@ public class LauncherProvider extends ContentProvider { * want to re-call {@link AppWidgetHost#startListening()} to ensure * callbacks are correctly set. */ - LauncherAppState.getLauncherProvider().notifyAppWidgetHostReset(); + new MainThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + if (mListener != null) { + mListener.onAppWidgetHostReset(); + } + } + }); } // Fresh and clean launcher DB. diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 5ca0ac8d6..629387ed0 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -59,7 +59,7 @@ public class WidgetPreviewLoader { * Note: synchronized block used for this variable is expensive and the block should always * be posted to a background thread. */ - @Thunk Set mUnusedBitmaps = + @Thunk final Set mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap()); private final Context mContext; @@ -540,7 +540,7 @@ public class WidgetPreviewLoader { */ public class PreviewLoadRequest { - private final PreviewLoadTask mTask; + @Thunk final PreviewLoadTask mTask; public PreviewLoadRequest(PreviewLoadTask task) { mTask = task; -- cgit v1.2.3 From d730f9d74f87b90616e0f0a9c7b6a4c78976f41a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 16 Jun 2015 15:45:49 -0700 Subject: Making resultHandler protected to make it accessible from a subClass Change-Id: Id11118b9f8b9782c44847ac82a03ea36bcb43963 --- src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java index 9ca5ccd06..10740ec77 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java @@ -32,7 +32,7 @@ public class DefaultAppSearchAlgorithm { private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+"); private final List mApps; - private final Handler mResultHandler; + protected final Handler mResultHandler; public DefaultAppSearchAlgorithm(List apps) { mApps = apps; -- cgit v1.2.3 From 92060267c4bf924be220cb69082df35e6217ddf2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 16 Jun 2015 16:57:02 -0700 Subject: Making UserHandleCompat constructor public Change-Id: Ied4c62964e608d543f0bcf3b232c0df7b57bdf5d --- src/com/android/launcher3/compat/UserHandleCompat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java index d8e60b875..ab4b7216b 100644 --- a/src/com/android/launcher3/compat/UserHandleCompat.java +++ b/src/com/android/launcher3/compat/UserHandleCompat.java @@ -41,7 +41,7 @@ public class UserHandleCompat { } } - static UserHandleCompat fromUser(UserHandle user) { + public static UserHandleCompat fromUser(UserHandle user) { if (user == null) { return null; } else { -- cgit v1.2.3 From 01d4053436a6a771b36b527a4f0ff49bbf468632 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 16 Jun 2015 17:20:56 -0700 Subject: Renaming id to prevent collision. Bug: 21878123 Change-Id: If4313d217967310ede7ed3fe11bdbe49311e1a4f --- res/layout/all_apps_search_bar.xml | 2 +- src/com/android/launcher3/allapps/DefaultAppSearchController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml index 9f4e3228c..8d75b15a6 100644 --- a/res/layout/all_apps_search_bar.xml +++ b/res/layout/all_apps_search_bar.xml @@ -40,7 +40,7 @@ android:src="@drawable/ic_arrow_back_grey" /> Date: Tue, 16 Jun 2015 18:43:58 -0700 Subject: Removing onInitialize method and creating searchManager in getView Change-Id: I3e2620a7839dfd5e6ecb76f24b384eb50e820a94 --- .../android/launcher3/allapps/AllAppsSearchBarController.java | 11 +++-------- .../android/launcher3/allapps/DefaultAppSearchController.java | 8 ++------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java index 341539cdd..9e51406f0 100644 --- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.allapps; -import android.content.ComponentName; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; @@ -35,17 +34,13 @@ public abstract class AllAppsSearchBarController { /** * Sets the references to the apps model and the search result callback. */ - public final void initialize(AlphabeticalAppsList apps, Callbacks cb) { + public void initialize(AlphabeticalAppsList apps, Callbacks cb) { mApps = apps; mCb = cb; - onInitialize(); } - /** - * To be overridden by subclasses. This method will get called when the controller is set, - * before getView(). - */ - protected abstract void onInitialize(); + @Deprecated + protected void onInitialize() { }; /** * Returns the search bar view. diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java index 20924af94..629bfeafe 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java @@ -31,7 +31,6 @@ import com.android.launcher3.util.Thunk; import java.util.List; - /** * The default search controller. */ @@ -74,6 +73,8 @@ final class DefaultAppSearchController extends AllAppsSearchBarController @Override public View getView(ViewGroup parent) { + mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps()); + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false); mSearchView.setOnClickListener(this); @@ -113,11 +114,6 @@ final class DefaultAppSearchController extends AllAppsSearchBarController return mSearchBarEditView.isFocused(); } - @Override - protected void onInitialize() { - mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps()); - } - @Override public void reset() { hideSearchField(false, null); -- cgit v1.2.3 From 4919827990b16ae22595d0b7cb123a875961d9be Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 17 Jun 2015 16:09:07 +0000 Subject: Revert "Removing onInitialize method and creating searchManager in getView" This reverts commit 6530017bb88179aeb1e3f131738da8f0d7592f36. Change-Id: I8ecfd21ec854cfe1774f16d2d50c7a0e45dd6865 --- .../android/launcher3/allapps/AllAppsSearchBarController.java | 11 ++++++++--- .../android/launcher3/allapps/DefaultAppSearchController.java | 8 ++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java index 9e51406f0..341539cdd 100644 --- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import android.content.ComponentName; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; @@ -34,13 +35,17 @@ public abstract class AllAppsSearchBarController { /** * Sets the references to the apps model and the search result callback. */ - public void initialize(AlphabeticalAppsList apps, Callbacks cb) { + public final void initialize(AlphabeticalAppsList apps, Callbacks cb) { mApps = apps; mCb = cb; + onInitialize(); } - @Deprecated - protected void onInitialize() { }; + /** + * To be overridden by subclasses. This method will get called when the controller is set, + * before getView(). + */ + protected abstract void onInitialize(); /** * Returns the search bar view. diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java index 629bfeafe..20924af94 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java @@ -31,6 +31,7 @@ import com.android.launcher3.util.Thunk; import java.util.List; + /** * The default search controller. */ @@ -73,8 +74,6 @@ final class DefaultAppSearchController extends AllAppsSearchBarController @Override public View getView(ViewGroup parent) { - mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps()); - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false); mSearchView.setOnClickListener(this); @@ -114,6 +113,11 @@ final class DefaultAppSearchController extends AllAppsSearchBarController return mSearchBarEditView.isFocused(); } + @Override + protected void onInitialize() { + mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps()); + } + @Override public void reset() { hideSearchField(false, null); -- cgit v1.2.3 From e9819e6d5df372323582cf9ae7567062ffc6fca8 Mon Sep 17 00:00:00 2001 From: Jun Mukai Date: Wed, 17 Jun 2015 10:58:59 -0700 Subject: Add null-check for workspace/hotseat. I assumed that they are non-null, but reportedly it was wrong. This method can be invoked before the views are fully initialized. Also hotseat can be null as far as I see setupViews(), therefore null-check and 'importance stored' flag check should be done separately. Bug: 21779078 Change-Id: I3f17226f887c38adf2b1fb7ee2a016e00ffc0eb4 --- src/com/android/launcher3/Launcher.java | 36 ++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9989abb6d..3596b71d2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -554,32 +554,44 @@ public class Launcher extends Activity public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { mLauncherCallbacks = callbacks; mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() { - private boolean mImportanceStored = false; + private boolean mWorkspaceImportanceStored = false; + private boolean mHotseatImportanceStored = false; private int mWorkspaceImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; @Override public void onSearchOverlayOpened() { - if (mImportanceStored) { + if (mWorkspaceImportanceStored || mHotseatImportanceStored) { return; } // The underlying workspace and hotseat are temporarily suppressed by the search // overlay. So they sholudn't be accessible. - mWorkspaceImportanceForAccessibility = mWorkspace.getImportantForAccessibility(); - mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility(); - mWorkspace.setImportantForAccessibility( - View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - mHotseat.setImportantForAccessibility( - View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - mImportanceStored = true; + if (mWorkspace != null) { + mWorkspaceImportanceForAccessibility = + mWorkspace.getImportantForAccessibility(); + mWorkspace.setImportantForAccessibility( + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + mWorkspaceImportanceStored = true; + } + if (mHotseat != null) { + mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility(); + mHotseat.setImportantForAccessibility( + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + mHotseatImportanceStored = true; + } } @Override public void onSearchOverlayClosed() { - mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility); - mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility); - mImportanceStored = false; + if (mWorkspaceImportanceStored && mWorkspace != null) { + mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility); + } + if (mHotseatImportanceStored && mHotseat != null) { + mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility); + } + mWorkspaceImportanceStored = false; + mHotseatImportanceStored = false; } }); return true; -- cgit v1.2.3 From dad45a7026868832404ecfb722407c685c0e8a13 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 17 Jun 2015 18:24:52 -0700 Subject: Removing obsolete strings Change-Id: I447d5fe819fbec64c4e873ed40dabd4f76bebb4a --- res/values-af/strings.xml | 8 -------- res/values-am/strings.xml | 8 -------- res/values-ar/strings.xml | 8 -------- res/values-bg/strings.xml | 8 -------- res/values-bn-rBD/strings.xml | 8 -------- res/values-ca/strings.xml | 8 -------- res/values-cs/strings.xml | 8 -------- res/values-da/strings.xml | 8 -------- res/values-de/strings.xml | 8 -------- res/values-el/strings.xml | 8 -------- res/values-en-rGB/strings.xml | 8 -------- res/values-en-rIN/strings.xml | 8 -------- res/values-es-rUS/strings.xml | 8 -------- res/values-es/strings.xml | 8 -------- res/values-et-rEE/strings.xml | 8 -------- res/values-et/strings.xml | 3 --- res/values-eu-rES/strings.xml | 8 -------- res/values-fa/strings.xml | 8 -------- res/values-fi/strings.xml | 8 -------- res/values-fr-rCA/strings.xml | 8 -------- res/values-fr/strings.xml | 8 -------- res/values-gl-rES/strings.xml | 8 -------- res/values-hi/strings.xml | 8 -------- res/values-hr/strings.xml | 8 -------- res/values-hu/strings.xml | 8 -------- res/values-hy-rAM/strings.xml | 8 -------- res/values-in/strings.xml | 8 -------- res/values-is-rIS/strings.xml | 8 -------- res/values-it/strings.xml | 8 -------- res/values-iw/strings.xml | 8 -------- res/values-ja/strings.xml | 8 -------- res/values-ka-rGE/strings.xml | 8 -------- res/values-kk-rKZ/strings.xml | 8 -------- res/values-km-rKH/strings.xml | 8 -------- res/values-kn-rIN/strings.xml | 8 -------- res/values-ko/strings.xml | 8 -------- res/values-ky-rKG/strings.xml | 8 -------- res/values-lo-rLA/strings.xml | 8 -------- res/values-lt/strings.xml | 8 -------- res/values-lv/strings.xml | 8 -------- res/values-mk-rMK/strings.xml | 8 -------- res/values-ml-rIN/strings.xml | 8 -------- res/values-mn-rMN/strings.xml | 8 -------- res/values-mr-rIN/strings.xml | 8 -------- res/values-ms-rMY/strings.xml | 8 -------- res/values-ms/strings.xml | 3 --- res/values-my-rMM/strings.xml | 8 -------- res/values-nb/strings.xml | 8 -------- res/values-ne-rNP/strings.xml | 8 -------- res/values-nl/strings.xml | 8 -------- res/values-pl/strings.xml | 8 -------- res/values-pt-rPT/strings.xml | 8 -------- res/values-pt/strings.xml | 8 -------- res/values-ro/strings.xml | 8 -------- res/values-ru/strings.xml | 8 -------- res/values-si-rLK/strings.xml | 8 -------- res/values-sk/strings.xml | 8 -------- res/values-sl/strings.xml | 8 -------- res/values-sr/strings.xml | 8 -------- res/values-sv/strings.xml | 8 -------- res/values-sw/strings.xml | 8 -------- res/values-ta-rIN/strings.xml | 8 -------- res/values-te-rIN/strings.xml | 8 -------- res/values-th/strings.xml | 8 -------- res/values-tl/strings.xml | 8 -------- res/values-tr/strings.xml | 8 -------- res/values-uk/strings.xml | 8 -------- res/values-ur-rPK/strings.xml | 8 -------- res/values-uz-rUZ/strings.xml | 8 -------- res/values-vi/strings.xml | 8 -------- res/values-zh-rCN/strings.xml | 8 -------- res/values-zh-rHK/strings.xml | 8 -------- res/values-zh-rTW/strings.xml | 8 -------- res/values-zu/strings.xml | 8 -------- 74 files changed, 582 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 0f90c4d82..397e759ad 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Tuis" "Program is nie geïnstalleer nie." "Program is nie beskikbaar nie" @@ -46,7 +45,6 @@ "Stel op" "Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie." "Naamlose vouer" - "Tuisskerm %1$d" "Bladsy %1$d van %2$d" "Tuisskerm %1$d van %2$d" "Welkom" @@ -57,7 +55,6 @@ "Muurpapiere, legstukke en instellings" "Raak en hou agtergrond om te pasmaak" "HET DIT" - "OK" "Vouer oopgemaak, %1$d by %2$d" "Raak om vouer toe te maak" "Raak om hernoem te stoor" @@ -67,12 +64,7 @@ "Legstukke" "Muurpapiere" "Instellings" - "Wag tans…" - "Laai tans af…" - "Installeer tans…" "Onbekend" - "Nie teruggestel nie" - "Verwyder almal" "Verwyder" "Soek" "Hierdie program is nie geïnstalleer nie" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 52fb95e6c..87aa07163 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -20,7 +20,6 @@ "ማስጀመሪያ3" - "መነሻ" "መተግበሪያ አልተጫነም።" "መተግበሪያ አይገኝም" @@ -46,7 +45,6 @@ "ማዋቀሪያ" "ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።" "ስም-አልባ አቃፊ" - "መነሻ ማያ ገጽ %1$d" "ገጽ %1$d ከ%2$d" "መነሻ ማያ ገጽ %1$d ከ%2$d" "እንኳን በደህና መጡ" @@ -57,7 +55,6 @@ "የግድግዳ ወረቀቶች፣ ንዑስ ፕሮግራሞች እና ቅንብሮች" "ለማበጀት ጀርባውን ነክተው ይያዙት" "ገባኝ" - "እሺ" "አቃፊ ተከፍቷል፣ %1$d%2$d" "አቃፊን ለመዝጋት ይንኩ" "ዳግም የተሰየመውን ለማስቀመጥ ይንኩ" @@ -67,12 +64,7 @@ "ፍርግሞች" "የግድግዳ ወረቀቶች" "ቅንብሮች" - "በመጠበቅ ላይ" - "በማውረድ ላይ" - "በመጫን ላይ" "የማይታወቅ" - "ወደነበረበት አልተመለሰም" - "ሁሉንም አስወግድ" "አስወግድ" "ፈልግ" "ይህ መተግበሪያ አልተጫነም" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 02e4ee862..eee003df6 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "الرئيسية" "لم يتم تثبيت التطبيق." "التطبيق ليس متاحًا" @@ -46,7 +45,6 @@ "الإعداد" "هذا تطبيق نظام وتتعذر إزالته." "مجلد بدون اسم" - "‏الشاشة الرئيسية %1$d" "‏الصفحة %1$d من %2$d" "‏الشاشة الرئيسية %1$d من %2$d" "مرحبًا" @@ -57,7 +55,6 @@ "الخلفيات والأدوات والإعدادات" "المس مع الاستمرار الخلفية لتخصيصها" "حسنًا" - "موافق" "تم فتح المجلد، بمقاس %1$d في %2$d" "المس لإغلاق المجلد" "المس لحفظ إعادة التسمية" @@ -67,12 +64,7 @@ "الأدوات" "الخلفيات" "الإعدادات" - "انتظار" - "جارٍ التنزيل" - "جارٍ التثبيت" "غير معروفة" - "استعادة مخفقة" - "إزالة الكل" "إزالة" "بحث" "لم يتم تثبيت هذا التطبيق" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index cff5c783f..b918603ee 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Начало" "Приложението не е инсталирано." "Приложението не е налично" @@ -46,7 +45,6 @@ "Настройване" "Това е системно приложение и не може да се деинсталира." "Папка без име" - "Начален екран %1$d" "Страница %1$d от %2$d" "Начален екран %1$d от %2$d" "Добре дошли" @@ -57,7 +55,6 @@ "Тапети, приспособления и настройки" "Докоснете и задръжте фона за персонализиране" "РАЗБРАХ" - "ОK" "Папката е отворена – %1$d на %2$d" "Докоснете, за да затворите папката" "Докоснете, за да запазите новото име" @@ -67,12 +64,7 @@ "Приспособления" "Тапети" "Настройки" - "Изчаква" - "Изтегля се" - "Инсталира се" "Няма информация" - "Не е възстановено" - "Премахване на всички" "Премахване" "Търсене" "Това приложение не е инсталирано" diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index 14a93a21b..8387932c1 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -20,7 +20,6 @@ "লঞ্চার৩" - "হোম" "অ্যাপ্লিকেশান ইনস্টল করা নেই৷" "অ্যাপ্লিকেশান অনুপলব্ধ" @@ -46,7 +45,6 @@ "সেটআপ" "এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷" "নামবিহীন ফোল্ডার" - "%1$d নম্বর হোম স্ক্রীন" "%2$dটির মধ্যে %1$dটি পৃষ্ঠা" "%2$dটির %1$d নম্বর হোম স্ক্রীন" "স্বাগতম" @@ -57,7 +55,6 @@ "ওয়ালপেপার, উইজেট এবং সেটিংস" "কাস্টমাইজ করার জন্য পটভূমি স্পর্শ করে ধরে থাকুন" "বুঝেছি" - "ঠিক আছে" "ফোল্ডার খোলা হয়েছে, %1$d বাই %2$d" "ফোল্ডার বন্ধ করতে স্পর্শ করুন" "পুনঃনামকরণ সংরক্ষণ করতে স্পর্শ করুন" @@ -67,12 +64,7 @@ "উইজেটগুলি" "ওয়ালপেপারগুলি" "সেটিংস" - "প্রতীক্ষা" - "ডাউনলোড হচ্ছে" - "ইনস্টল করা হচ্ছে" "অজানা" - "পুনঃস্থাপন করা যায়নি" - "সবগুলি সরান" "সরান" "অনুসন্ধান" "এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 4d38c8164..69ace8ec7 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Inici" "L\'aplicació no s\'ha instal·lat." "L\'aplicació no està disponible." @@ -46,7 +45,6 @@ "Configuració" "Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar." "Carpeta sense nom" - "Pantalla d\'inici %1$d" "Pàgina %1$d de %2$d" "Pantalla d\'inici %1$d de %2$d" "Us donem la benvinguda" @@ -57,7 +55,6 @@ "Fons de pantalla, widgets i configuració" "Mantén premut el fons per fer personalitzacions." "D\'ACORD" - "D\'acord" "S\'ha obert la carpeta, %1$d per %2$d" "Toca per tancar la carpeta" "Toca per desar el canvi de nom" @@ -67,12 +64,7 @@ "Widgets" "Fons de pantalla" "Configuració" - "En espera" - "S\'està baixant" - "Instal·lant" "Desconegut" - "No restaurat" - "Suprimeix-ho tot" "Suprimeix" "Cerca" "Aquesta aplicació no està instal·lada" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 57598f70f..7606a4a6b 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Plocha" "Aplikace není nainstalována." "Aplikace není k dispozici." @@ -46,7 +45,6 @@ "Nastavení" "Toto je systémová aplikace a nelze ji odinstalovat." "Složka bez názvu" - "Plocha %1$d" "Strana %1$d z %2$d" "Plocha %1$d z %2$d" "Vítejte" @@ -57,7 +55,6 @@ "Tapety, widgety a nastavení" "Pozadí můžete přizpůsobit klepnutím a podržením" "ROZUMÍM" - "OK" "Složka otevřena, rozměry %1$d x %2$d" "Dotykem složku zavřete" "Dotykem uložíte změnu názvu" @@ -67,12 +64,7 @@ "Widgety" "Tapety" "Nastavení" - "Čekání" - "Stahování" - "Instalace" "Neznámé" - "Nebylo obnoveno" - "Odstranit vše" "Odstranit" "Hledat" "Tato aplikace není nainstalována" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 36f345526..b64d20fc4 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Startskærm" "Appen er ikke installeret." "Appen er ikke tilgængelig" @@ -46,7 +45,6 @@ "Konfigurer" "Dette er en systemapp, som ikke kan afinstalleres." "Unavngiven mappe" - "Startskærm %1$d" "Side %1$d ud af %2$d" "Startskærm %1$d ud af %2$d" "Velkommen" @@ -57,7 +55,6 @@ "Baggrunde, widgets og indstillinger" "Tryk på baggrunden, og hold fingeren nede for at tilpasse den" "OK, FORSTÅET" - "OK" "Mappen er åben, %1$d gange %2$d" "Tryk for at lukke mappen" "Tryk for at gemme det nye navn" @@ -67,12 +64,7 @@ "Widgets" "Baggrunde" "Indstillinger" - "Afventer" - "Downloader" - "Installerer" "Ukendt" - "Ikke gendannet" - "Slet alle" "Fjern" "Søg" "Denne app er ikke installeret" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 1b9bc9e1b..d64ae45d0 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Startseite" "App ist nicht installiert." "App nicht verfügbar" @@ -46,7 +45,6 @@ "Einrichten" "Dies ist eine Systemanwendung, die nicht deinstalliert werden kann." "Unbenannter Ordner" - "Startbildschirm %1$d" "Seite %1$d von %2$d" "Startbildschirm %1$d von %2$d" "Hallo!" @@ -57,7 +55,6 @@ "Hintergründe, Widgets & Einstellungen" "Berühren und halten Sie den Hintergrund, um ihn anzupassen." "OK" - "OK" "Ordner geöffnet, %1$d x %2$d" "Ordner durch Berühren schließen" "Umbenennung durch Berühren speichern" @@ -67,12 +64,7 @@ "Widgets" "Hintergründe" "Einstellungen" - "Warten" - "Download läuft" - "Installation" "Unbekannt" - "Nicht wiederhergestellt" - "Alle entfernen" "Entfernen" "Suchen" "Diese App ist nicht installiert" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 69a1e7794..8cd2ac900 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Αρχική σελίδα" "Η εφαρμογή δεν έχει εγκατασταθεί." "Η εφαρμογή δεν είναι διαθέσιμη" @@ -46,7 +45,6 @@ "Ρύθμιση" "Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της." "Φάκελος χωρίς όνομα" - "Αρχική οθόνη %1$d" "Σελίδα %1$d από %2$d" "Αρχική οθόνη %1$d από %2$d" "Καλώς ορίσατε" @@ -57,7 +55,6 @@ "Ταπετσαρίες, γραφικά στοιχεία και ρυθμίσεις" "Αγγίξτε παρατεταμένα το παρασκήνιο για προσαρμογή" "ΕΓΙΝΕ" - "OK" "Άνοιγμα φακέλου, %1$d επί %2$d" "Αγγίξτε για να κλείσετε τον φάκελο" "Αγγίξτε για να αποθηκεύσετε το νέο όνομα" @@ -67,12 +64,7 @@ "Γραφικά στοιχεία" "Ταπετσαρίες" "Ρυθμίσεις" - "Αναμονή" - "Λήψη " - "Εγκατάσταση" "Άγνωστο" - "Δεν ανακτήθηκε" - "Κατάργηση όλων" "Κατάργηση" "Αναζήτηση" "Αυτή η εφαρμογή δεν είναι εγκατεστημένη" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index cb25fd9be..30d626d8c 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Home" "App isn\'t installed." "App isn\'t available" @@ -46,7 +45,6 @@ "Setup" "This is a system app and can\'t be uninstalled." "Unnamed Folder" - "Home screen %1$d" "Page %1$d of %2$d" "Home screen %1$d of %2$d" "Welcome" @@ -57,7 +55,6 @@ "Wallpapers, widgets, & settings" "Touch & hold background to customise" "GOT IT" - "OK" "Folder opened, %1$d by %2$d" "Touch to close folder" "Touch to save rename" @@ -67,12 +64,7 @@ "Widgets" "Wallpapers" "Settings" - "Waiting" - "Downloading" - "Installing" "Unknown" - "Not restored" - "Remove All" "Remove" "Search" "This app is not installed" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index cb25fd9be..30d626d8c 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Home" "App isn\'t installed." "App isn\'t available" @@ -46,7 +45,6 @@ "Setup" "This is a system app and can\'t be uninstalled." "Unnamed Folder" - "Home screen %1$d" "Page %1$d of %2$d" "Home screen %1$d of %2$d" "Welcome" @@ -57,7 +55,6 @@ "Wallpapers, widgets, & settings" "Touch & hold background to customise" "GOT IT" - "OK" "Folder opened, %1$d by %2$d" "Touch to close folder" "Touch to save rename" @@ -67,12 +64,7 @@ "Widgets" "Wallpapers" "Settings" - "Waiting" - "Downloading" - "Installing" "Unknown" - "Not restored" - "Remove All" "Remove" "Search" "This app is not installed" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 36f32a722..5e2d87c83 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Pantalla principal" "No se instaló la aplicación." "La aplicación no está disponible." @@ -46,7 +45,6 @@ "Configuración" "Esta es una aplicación del sistema y no se puede desinstalar." "Carpeta sin nombre" - "Pantalla principal %1$d" "Página %1$d de %2$d" "Pantalla principal %1$d de %2$d" "Bienvenido" @@ -57,7 +55,6 @@ "Fondos, widgets y configuración" "Mantén presionado el fondo para personalizarlo" "ENTENDIDO" - "Aceptar" "Carpeta abierta, %1$d por %2$d" "Toca para cerrar la carpeta." "Toca para guardar el nuevo nombre." @@ -67,12 +64,7 @@ "Widgets" "Fondos de pantalla" "Configuración" - "Pendiente" - "Descargando" - "Instalando" "Desconocido" - "No restaurado" - "Eliminar todo" "Eliminar" "Buscar" "Esta aplicación no está instalada" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 6dcfba4a4..1bf1d769f 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Home" "La aplicación no está instalada." "La aplicación no está disponible" @@ -46,7 +45,6 @@ "Configuración" "Esta aplicación es del sistema y no se puede desinstalar." "Carpeta sin nombre" - "Pantalla de inicio %1$d" "Página %1$d de %2$d" "Pantalla de inicio %1$d de %2$d" "Te damos la bienvenida" @@ -57,7 +55,6 @@ "Fondos de pantalla, widgets y ajustes" "Mantén pulsado el fondo para personalizarlo" "ENTENDIDO" - "Aceptar" "Carpeta abierta, %1$d por %2$d" "Toca para cerrar la carpeta" "Toca para cambiar el nuevo nombre" @@ -67,12 +64,7 @@ "Widgets" "Fondos de pantalla" "Ajustes" - "Esperando" - "Descargando" - "Instalando" "Desconocido" - "No restaurado" - "Eliminar todo" "Eliminar" "Buscar" "Esta aplicación no está instalada" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index c56bc6986..b1c13d76b 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Avaekraan" "Rakendus pole installitud." "Rakendus ei ole saadaval" @@ -46,7 +45,6 @@ "Seadistamine" "See on süsteemirakendus ja seda ei saa desinstallida." "Nimetu kaust" - "Avaekraan %1$d" "Leht %1$d/%2$d" "Avaekraan %1$d/%2$d" "Tere tulemast" @@ -57,7 +55,6 @@ "Taustapildid, vidinad ja seaded" "Kohandamiseks puudutage ja hoidke tausta all" "SELGE" - "OK" "Kaust on avatud, %1$d x %2$d" "Puudutage kausta sulgemiseks" "Puudutage uue nime salvestamiseks" @@ -67,12 +64,7 @@ "Vidinad" "Taustapildid" "Seaded" - "Ootamine" - "Allalaadimine" - "Installimine" "Teadmata" - "Ei taastatud" - "Eemalda kõik" "Eemalda" "Otsing" "See rakendus ei ole installitud" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index bc9520675..afed3b548 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -20,7 +20,6 @@ "Käivitaja" - "Kodu" "Määra taustapilt" "Taustapildid" @@ -43,10 +42,8 @@ "Probleem vidina laadimisel" "See on süsteemirakendus ja seda ei saa desinstallida." "Nimeta kaust" - "Avakuva %1$d" "Leht %1$d/%2$d" "Avakuva %1$d/%2$d" - "OK" "Kaust on avatud, %1$d x %2$d" "Puudutage kausta sulgemiseks" "Puudutage uue nime salvestamiseks" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 781f262ea..51ff5083f 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -20,7 +20,6 @@ "Abiarazlea3" - "Hasiera" "Aplikazioa instalatu gabe dago." "Ez dago erabilgarri aplikazioa" @@ -46,7 +45,6 @@ "Konfigurazioa" "Sistema-aplikazioa da hau eta ezin da desinstalatu." "Izenik gabeko karpeta" - "%1$d hasierako pantaila" "%1$d/%2$d orria" "%1$d/%2$d hasierako pantaila" "Ongi etorri" @@ -57,7 +55,6 @@ "Horma-paperak, widgetak eta ezarpenak" "Pertsonalizatzeko, eduki ukituta atzeko planoa" "ADOS" - "Ados" "Karpeta ireki da: %1$d x %2$d" "Karpeta ixteko, uki ezazu" "Karpetaren izen berria gordetzeko, uki ezazu" @@ -67,12 +64,7 @@ "Widgetak" "Horma-paperak" "Ezarpenak" - "Zain" - "Deskargatzen" - "Instalatzen" "Ezezaguna" - "Ez da leheneratu" - "Kendu guztiak" "Kendu" "Bilatu" "Aplikazio hau ez dago instalatuta" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index bf3a04f5c..76ffc9d19 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "صفحه اصلی" "برنامه نصب نشده است." "برنامه در دسترس نیست" @@ -46,7 +45,6 @@ "تنظیم" "این برنامه سیستمی است و حذف نصب نمی‌شود." "پوشه بی‌نام" - "‏صفحه اصلی %1$d" "‏صفحه %1$d از %2$d" "‏صفحه اصلی %1$d از %2$d" "خوش آمدید" @@ -57,7 +55,6 @@ "کاغذدیواری‌ها، ابزارک‌ها و تنظیمات" "برای سفارشی کردن، پس‌زمینه را لمس کنید و نگه‌دارید" "متوجه شدم" - "تأیید" "پوشه باز شده، %1$d در %2$d" "برای بستن پوشه لمس کنید" "برای ذخیره تغییر نام لمس کنید" @@ -67,12 +64,7 @@ "ابزارک‌ها" "کاغذدیواری‌ها" "تنظیمات" - "در حال انتظار" - "در حال دانلود" - "در حال نصب" "نامشخص" - "بازیابی نشد" - "حذف همه" "حذف" "جستجو" "این برنامه نصب نشده است." diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index da298be8c..44630c8ab 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Aloitusruutu" "Sovellusta ei ole asennettu." "Sovellus ei ole käytettävissä" @@ -46,7 +45,6 @@ "Asetus" "Tämä on järjestelmäsovellus, eikä sitä voi poistaa." "Nimetön kansio" - "Aloitusruutu %1$d" "Sivu %1$d / %2$d" "Aloitusruutu %1$d/%2$d" "Tervetuloa" @@ -57,7 +55,6 @@ "Taustakuvat, widgetit ja asetukset" "Muokkaa taustaa koskettamalla ja painamalla pitkään" "SELVÄ" - "OK" "Kansio avattu, koko %1$d x %2$d" "Sulje kansio koskettamalla" "Tallenna uudella nimellä koskettamalla" @@ -67,12 +64,7 @@ "Widgetit" "Taustakuvat" "Asetukset" - "Odottaa" - "Ladataan" - "Asennetaan" "Tuntematon" - "Ei palautettu" - "Poista kaikki" "Poista" "Haku" "Sovellusta ei ole asennettu" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index cc4d2a958..f11b34da7 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -20,7 +20,6 @@ "Lanceur3" - "Accueil" "L\'application n\'est pas installée." "Application indisponible" @@ -46,7 +45,6 @@ "Configuration" "Impossible de désinstaller cette application, car il s\'agit d\'une application système." "Dossier sans nom" - "Écran d\'accueil %1$d" "Page %1$d sur %2$d" "Écran d\'accueil %1$d sur %2$d" "Bienvenue" @@ -57,7 +55,6 @@ "Fonds d\'écran, widgets et paramètres" "Maintenez le doigt sur le fond d\'écran pour personnaliser" "J\'ai compris" - "OK" "Dossier ouvert, %1$d par %2$d" "Toucher pour fermer le dossier" "Toucher pour enregistrer le nouveau nom" @@ -67,12 +64,7 @@ "Widgets" "Fonds d\'écran" "Paramètres" - "En attente" - "Téléchargement..." - "Installation…" "Inconnu" - "Non restauré" - "Tout supprimer" "Supprimer" "Rechercher" "Cette application n\'est pas installée" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 826e17251..692c2ba29 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Accueil" "L\'application n\'est pas installée." "Application indisponible" @@ -46,7 +45,6 @@ "Configuration" "Impossible de désinstaller cette application, car il s\'agit d\'une application système." "Dossier sans nom" - "Écran d\'accueil %1$d" "Page %1$d sur %2$d" "Écran d\'accueil %1$d sur %2$d" "Bienvenue" @@ -57,7 +55,6 @@ "Fonds d\'écran, widgets et paramètres" "Appuyez de manière prolongée sur l\'arrière-plan pour le personnaliser." "OK" - "OK" "Dossier ouvert, %1$d par %2$d" "Appuyez pour fermer le dossier." "Appuyez pour enregistrer le nouveau nom." @@ -67,12 +64,7 @@ "Widgets" "Fonds d\'écran" "Paramètres" - "En attente" - "Téléchargement…" - "Installation…" "Inconnu" - "Non restauré" - "Tout supprimer" "Supprimer" "Rechercher" "Cette application n\'est pas installée" diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index 8cd568654..0684bdc22 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Inicio" "A aplicación non está instalada" "A aplicación non está dispoñible" @@ -46,7 +45,6 @@ "Configuración" "Esta aplicación é do sistema e non se pode desinstalar." "Cartafol sen nome" - "Pantalla de inicio %1$d" "Páxina %1$d de %2$d" "Pantalla de inicio %1$d de %2$d" "Dámosche a benvida" @@ -57,7 +55,6 @@ "Fondos pantalla, widgets e configuración" "Mantén tocado o segundo plano para personalizar" "DE ACORDO" - "Aceptar" "Abriuse o cartafol, %1$d por %2$d" "Toca para pechar o cartafol" "Toca para gardar o cambio de nome" @@ -67,12 +64,7 @@ "Widgets" "Fondos de pantalla" "Configuración" - "En espera" - "Descargando" - "Instalando" "Descoñecido" - "Non restaurado" - "Eliminar todas" "Eliminar" "Buscar" "Esta aplicación non está instalada" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 49feb66cb..c36cc7462 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "होम" "एप्‍लिकेशन इंस्‍टॉल नहीं है." "ऐप्स उपलब्ध नहीं है" @@ -46,7 +45,6 @@ "सेटअप" "यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता." "अनामित फ़ोल्डर" - "होम स्‍क्रीन %1$d" "पृष्ठ %2$d में से %1$d" "होम स्क्रीन %2$d में से %1$d" "स्वागत है" @@ -57,7 +55,6 @@ "वॉलपेपर, शॉर्टकट और सेटिंग" "पृष्ठभूमि कस्टमाइज़ करने के लिए स्पर्श करके रखें" "समझ लिया" - "ठीक" "फ़ोल्डर खोला गया, %1$d गुणा %2$d" "फ़ोल्‍डर बंद करने के लिए स्‍पर्श करें" "बदला गया नाम सहेजने के लिए स्पर्श करें" @@ -67,12 +64,7 @@ "शॉर्टकट" "वॉलपेपर" "सेटिंग" - "प्रतीक्षा में" - "डाउनलोड हो रहा है" - "इंस्टॉल हो रहा है" "अज्ञात" - "पुन:स्थापित नहीं हुआ" - "सभी निकालें" "निकालें" "खोजें" "यह ऐप्स इंस्टॉल नहीं है" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index c43d2f40b..016d7eb2d 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -20,7 +20,6 @@ "Pokretač3" - "Početna" "Aplikacija nije instalirana." "Aplikacija nije dostupna" @@ -46,7 +45,6 @@ "Postavljanje" "Ovo je aplikacija sustava i ne može se ukloniti." "Neimenovana mapa" - "Početni zaslon %1$d" "Stranica %1$d od %2$d" "Početni zaslon %1$d od %2$d" "Dobro došli" @@ -57,7 +55,6 @@ "Pozadinske slike, widgeti i postavke" "Dodirnite i zadržite pozadinu radi prilagodbe" "SHVAĆAM" - "U redu" "Mapa je otvorena, %1$d x %2$d" "Dodirnite da biste zatvorili mapu" "Dodirnite da biste spremili preimenovanje" @@ -67,12 +64,7 @@ "Widgeti" "Pozadinske slike" "Postavke" - "Čekanje" - "Preuzimanje" - "Instaliranje" "Nepoznato" - "Nije vraćeno" - "Ukloni sve" "Ukloni" "Traži" "Ta aplikacija nije instalirana" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index ef804a4d8..9074c176f 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Főoldal" "Az alkalmazás nincs telepítve." "Az alkalmazás nem érhető el" @@ -46,7 +45,6 @@ "Beállítás" "Ez egy rendszeralkalmazás, és nem lehet eltávolítani." "Névtelen mappa" - "%1$d. kezdőképernyő" "%2$d/%1$d. oldal" "%2$d/%1$d. kezdőképernyő" "Üdvözöljük!" @@ -57,7 +55,6 @@ "Háttérképek, modulok és beállítások" "Érintse meg és tartsa lenyomva a személyre szabáshoz" "MEGÉRTETTEM" - "OK" "Mappa megnyitva – szélesség: %1$d; magasság: %2$d" "Érintse meg a mappa bezárásához" "Érintse meg az átnevezés mentéséhez" @@ -67,12 +64,7 @@ "Modulok" "Háttérképek" "Beállítások" - "Várakozik" - "Letöltés alatt" - "Települ" "Ismeretlen" - "Nincs visszaállítva" - "Az összes eltávolítása" "Eltávolítás" "Keresés" "Az alkalmazás nincs telepítve" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index cbc5585da..ea4b2a1ed 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Հիմնական" "Ծրագիրը տեղադրված չէ:" "Հավելվածը հասանելի չէ" @@ -46,7 +45,6 @@ "Կարգավորում" "Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:" "Անանուն թղթապանակ" - "Հիմնական էկրան %1$d" "Էջ %1$d՝ %2$d-ից" "Հիմնական էկրան %1$d` %2$d-ից" "Բարի գալուստ" @@ -57,7 +55,6 @@ "Պաստառներ, վիջեթներ և կարգավորումներ" "Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա" "ՀԱՍԿԱՆԱԼԻ Է" - "Լավ" "Թղթապանակը բաց է, %1$d-ից %2$d" "Հպեք՝ թղթապանակը փակելու համար" "Հպեք՝ վերանվանումը պահելու համար" @@ -67,12 +64,7 @@ "Վիջեթներ" "Պաստառներ" "Կարգավորումներ" - "Առկախ է" - "Ներբեռնվում է" - "Տեղադրվում է" "Անհայտ է" - "Չի վերականգնվել" - "Հեռացնել բոլորը" "Հեռացնել" "Գտնել" "Այս ծրագիրը տեղադրված չէ:" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index ea9ebb594..edefc4435 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Layar Utama" "Aplikasi tidak dipasang." "Aplikasi tidak tersedia" @@ -46,7 +45,6 @@ "Siapkan" "Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya." "Folder Tanpa Nama" - "Layar utama %1$d" "Laman %1$d dari %2$d" "Layar utama %1$d dari %2$d" "Selamat Datang" @@ -57,7 +55,6 @@ "Wallpaper, widget, & setelan" "Sentuh & tahan latar belakang untuk menyesuaikan" "MENGERTI" - "Oke" "Folder dibuka, %1$d x %2$d" "Sentuh untuk menutup folder" "Sentuh untuk menyimpan ganti nama" @@ -67,12 +64,7 @@ "Widget" "Wallpaper" "Setelan" - "Menunggu" - "Mengunduh" - "Memasang" "Tidak dikenal" - "Tak dipulihkan" - "Buang Semua" "Buang" "Telusuri" "Aplikasi ini belum terpasang" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index 41e43b597..70f87e46f 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Heim" "Forritið er ekki uppsett." "Forritið er ekki í boði" @@ -46,7 +45,6 @@ "Uppsetning" "Þetta er kerfisforrit sem ekki er hægt að fjarlægja." "Ónefnd mappa" - "Heimaskjár %1$d" "Síða %1$d af %2$d" "Heimaskjár %1$d af %2$d" "Komdu fagnandi" @@ -57,7 +55,6 @@ "Veggfóður, græjur og stillingar" "Haltu fingri á bakgrunninum til að sérsníða hann" "ÉG SKIL" - "Í lagi" "Mappa opnuð, %1$d sinnum %2$d" "Snertu til að loka möppunni" "Snertu til að staðfesta nýtt heiti" @@ -67,12 +64,7 @@ "Græjur" "Veggfóður" "Stillingar" - "Bíður" - "Sækir" - "Setur upp" "Óþekkt" - "Ekki endurheimt" - "Fjarlægja öll" "Fjarlægja" "Leita" "Þetta forrit er ekki uppsett" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 6307fff8f..6ad1ed4d2 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Home page" "App non installata." "App non disponibile" @@ -46,7 +45,6 @@ "Configurazione" "Questa è un\'app di sistema e non può essere disinstallata." "Cartella senza nome" - "Schermata Home %1$d" "Pagina %1$d di %2$d" "Schermata Home %1$d di %2$d" "Benvenuto" @@ -57,7 +55,6 @@ "Sfondi, widget e impostazioni" "Tocca lo sfondo e tieni premuto per personalizzare" "OK" - "OK" "Cartella aperta, %1$d per %2$d" "Tocca per chiudere la cartella" "Tocca per salvare nuovo nome" @@ -67,12 +64,7 @@ "Widget" "Sfondi" "Impostazioni" - "In attesa" - "Download..." - "Installazione..." "Sconosciuto" - "Non ripristinato" - "Rimuovi tutto" "Rimuovi" "Cerca" "L\'app non è installata" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 479be106c..3e3fe3391 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "דף הבית" "האפליקציה לא מותקנת." "האפליקציה אינה זמינה" @@ -46,7 +45,6 @@ "הגדר" "זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה." "תיקיה ללא שם" - "‏מסך דף הבית %1$d" "‏דף %1$d מתוך %2$d" "‏מסך דף הבית %1$d מתוך %2$d" "ברוכים הבאים" @@ -57,7 +55,6 @@ "טפטים, ווידג\'טים והגדרות" "גע והחזק ברקע לביצוע התאמה אישית" "הבנתי" - "אישור" "תיקיה פתוחה, %1$d על %2$d" "גע כדי לסגור את התיקיה" "גע כדי לשמור את שינוי השם" @@ -67,12 +64,7 @@ "רכיבי ווידג\'ט" "טפטים" "הגדרות" - "ממתין" - "מוריד" - "מתקין" "לא ידוע" - "לא שוחזרה" - "הסר את הכל" "הסר" "חפש" "אפליקציה זו אינה מותקנת" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index aa700aa54..1fc9aca00 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ホーム" "このアプリはインストールされていません。" "このアプリは使用できません" @@ -46,7 +45,6 @@ "セットアップ" "このシステムアプリはアンインストールできません。" "名前のないフォルダ" - "ホーム画面: %1$d" "%1$d/%2$dページ" "ホーム画面: %1$d/%2$d" "ようこそ" @@ -57,7 +55,6 @@ "壁紙、ウィジェット、設定" "カスタマイズするにはバックグラウンドを押し続けます" "OK" - "OK" "フォルダが開いています。%1$dx%2$dの大きさです" "タップしてフォルダを閉じます" "タップして名前の変更を保存します" @@ -67,12 +64,7 @@ "ウィジェット" "壁紙" "設定" - "待機中" - "ダウンロード中" - "インストール中" "不明" - "復元失敗" - "すべて削除" "削除" "検索" "このアプリはインストールされていません" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 79434c0f1..4a93b9a16 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "მთავარი" "აპი არ არის დაყენებული." "აპი მიუწვდომელია" @@ -46,7 +45,6 @@ "დაყენება" "ეს სისტემური აპია და მისი წაშლა შეუძლებელია." "უსახელო საქაღალდე" - "მთავარი ეკრანი %1$d" "გვერდი %1$d %2$d-დან" "მთავარი ეკრანი %1$d, %2$d-დან" "მოგესალმებით" @@ -57,7 +55,6 @@ "ფონები, ვიჯეტები, & პარამეტრები" "მოსარგებად შეეხეთ & დააყოვნეთ ფონზე" "გასაგებია" - "კარგი" "საქაღალდე გახსნილია, %1$d x %2$d" "შეეხეთ საქაღალდის დასახურად" "შეეხეთ ახალი სახელის შესანახად" @@ -67,12 +64,7 @@ "ვიჯეტები" "ფონები" "პარამეტრები" - "მოცდა..." - "ჩამოტვირთვა..." - "ინსტალაცია..." "უცნობი" - "არ აღდგა" - "ყველას ამოშლა" "ამოშლა" "ძიება" "ეს აპი დაყენებული არ არის" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index 8a0c6277f..40de75307 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Негізгі" "Қолданба орнатылмаған." "Қолданба қол жетімді емес" @@ -46,7 +45,6 @@ "Орнату" "Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес." "Атауы жоқ қалта" - "%1$d негізгі экран" "%1$d бет, барлығы %2$d" "%1$d негізгі экран, барлығы %2$d" "Қош келдіңіз" @@ -57,7 +55,6 @@ "Тұсқағаздар, виджеттер және параметрлер" "Теңшеу үшін фонды түртіп, ұстап тұрыңыз" "ТҮСІНДІМ" - "Жарайды" "Қалта ашылды, %1$d және %2$d" "Қалтаны жабу үшін түртіңіз" "Өзгертілген атауын сақтау үшін түртіңіз" @@ -67,12 +64,7 @@ "Виджеттер" "Артқы фондар" "Параметрлер" - "Күтілуде" - "Жүктелуде" - "Орнатылуда" "Белгісіз" - "Қалп. кел-меді" - "Барлығын алып тастау" "Алып тастау" "Іздеу" "Бұл қолданба орнатылмаған" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index a61be73cd..1cd8fe29b 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ដើម" "មិន​បាន​ដំឡើង​កម្មវិធី។" "មិន​មាន​កម្មវិធី" @@ -46,7 +45,6 @@ "រៀបចំ" "នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។" "ថត​គ្មាន​ឈ្មោះ" - "អេក្រង់​ដើម %1$d" "ទំព័រ %1$d នៃ %2$d" "អេក្រង់​ដើម %1$d នៃ %2$d" "សូម​ស្វាគមន៍​" @@ -57,7 +55,6 @@ "ផ្ទាំងរូបភាព,ធាតុក្រាហ្វិក & ការកំណត់" "ប៉ះ & សង្កត់​ផ្ទៃ​ខាង​ក្រោយ​ដើម្បី​ប្ដូរ​តាម​​តម្រូវ​ការ" "យល់​ហើយ" - "យល់ព្រម" "បាន​បើក​ថត %1$d ដោយ %2$d" "ប៉ះ ដើម្បី​បិទ​ថត" "ប៉ះ ដើម្បី​រក្សាទុក​ការ​ប្ដូរ​ឈ្មោះ" @@ -67,12 +64,7 @@ "ធាតុ​ក្រាហ្វិក" "ផ្ទាំង​រូបភាព" "ការកំណត់" - "រង់ចាំ" - "​ទាញ​យក" - "ដំឡើង" "មិន​ស្គាល់" - "មិនបាន​​ស្តា​រ" - "លុបចេញ​​​ទាំងអស់" "លុបចេញ" "ស្វែងរក" "មិន​បាន​ដំឡើង​កម្មវិធី​នេះ" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index 9680cb694..a823528df 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -20,7 +20,6 @@ "ಲಾಂಚರ್3" - "ಮುಖಪುಟ" "ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ" @@ -46,7 +45,6 @@ "ಸೆಟಪ್" "ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ." "ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್" - "ಮುಖಪುಟದ ಪರದೆ %1$d" "%2$d ರಲ್ಲಿ %1$d ಪುಟ" "%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ" "ಸುಸ್ವಾಗತ" @@ -57,7 +55,6 @@ "ವಾಲ್‌ಪೇಪರ್‌ಗಳು, ವಿಜೆಟ್‌ಗಳು, & ಸೆಟ್ಟಿಂಗ್‌ಗಳು" "ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ & ಒತ್ತಿ ಹಿಡಿಯಿರಿ" "ಅರ್ಥವಾಯಿತು" - "ಸರಿ" "ಫೋಲ್ಡರ್ ತೆರೆಯಲಾಗಿದೆ, %1$d ಬೈ %2$d" "ಫೋಲ್ಡರ್‌ ಮುಚ್ಚಲು ಸ್ಪರ್ಶಿಸಿ" "ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಸ್ಪರ್ಶಿಸಿ" @@ -67,12 +64,7 @@ "ವಿಜೆಟ್‌ಗಳು" "ವಾಲ್‌ಪೇಪರ್‌ಗಳು" "ಸೆಟ್ಟಿಂಗ್‌ಗಳು" - "ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ" - "ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ" - "ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" "ಅಜ್ಞಾತ" - "ಇನ್ನೂ ಪುನಃಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ" - "ಎಲ್ಲವನ್ನೂ ತೆಗೆದುಹಾಕಿ" "ತೆಗೆದುಹಾಕಿ" "ಹುಡುಕು" "ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 7e924cb19..628900aba 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "홈" "앱이 설치되지 않았습니다." "앱을 사용할 수 없음" @@ -46,7 +45,6 @@ "설정" "시스템 앱은 제거할 수 없습니다." "이름이 없는 폴더" - "홈 화면 %1$d" "페이지 %1$d/%2$d" "홈 화면 %1$d/%2$d" "환영합니다." @@ -57,7 +55,6 @@ "배경화면, 위젯, 설정" "백그라운드를 길게 터치하여 맞춤설정합니다." "확인" - "확인" "폴더 열림(%1$dX%2$d)" "터치하여 폴더를 닫음" "터치하여 바꾼 이름을 저장" @@ -67,12 +64,7 @@ "위젯" "배경화면" "설정" - "대기 중" - "다운로드 중" - "설치 중" "알 수 없음" - "복원되지 않음" - "모두 삭제" "삭제" "검색" "이 앱이 설치되어 있지 않음" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index 39cbee2a6..865db3b3a 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Үйгө" "Колдонмо орнотулган эмес." "Колдонмо жеткиликтүү эмес" @@ -46,7 +45,6 @@ "Орнотуу" "Бул системдик колдонмо жана аны чечкенге болбойт." "Аты жок фолдер" - "Үй экраны %1$d" "%2$d ичинен %1$d барак" "Үй экраны %2$d ичинен %1$d" "Кош келиңиз" @@ -57,7 +55,6 @@ "Тушкагаздар, виджеттер & жөндөөлөр" "Өзгөчөлөштүрүү үчүн фонго тийип & коё бербей туруңуз" "ТҮШҮНДҮМ" - "OK" "Фолдер ачылды, туурасы %1$d, бийиктиги %2$d" "Фолдерди жабыш үчүн тийиңиз" "Тийип, аттын өзгөртүлүшүн сактаңыз" @@ -67,12 +64,7 @@ "Виджеттер" "Тушкагаздар" "Тууралоолор" - "Күтүүдө" - "Жүктөлп алнууда" - "Орнотулууда" "Белгисиз" - "Калыбн келт. жок" - "Баарын алып салуу" "Алып салуу" "Издөө" "Бул колдонмо орнотулган эмес" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index aa45950f0..b3c467693 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ໜ້າຫຼັກ" "ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ." "ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້" @@ -46,7 +45,6 @@ "ຕິດຕັ້ງ" "ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້." "ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່" - "ໜ້າຈໍຫຼັກ %1$d" "ໜ້າ %1$d ຈາກ %2$d" "ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d" "ຍິນດີຕ້ອນຮັບ" @@ -57,7 +55,6 @@ "​ຮູບ​ພື້ນຫຼັງ, ວິດເຈັດ, & ​ການ​ຕັ້ງ​ຄ່າ" "ແຕະທີ່​ພາບ​ພື້ນ​ຫລັງ​ຄ້າງ​ໄວ້​ເພື່ອ​ປັບ​ແຕ່ງ" "ເຂົ້າໃຈແລ້ວ" - "ຕົກລົງ" "ເປີດໂຟນເດີແລ້ວ, %1$d ຄູນ %2$d" "ສຳພັດເພື່ອປິດໂຟນເດີ" "ສຳພັດເພື່ອບັນທຶກການປ່ຽນຊື່" @@ -67,12 +64,7 @@ "ວິດເຈັດ" "ພາບພື້ນຫຼັງ" "ການຕັ້ງຄ່າ" - "ກຳ​ລັງ​ລໍ​ຖ້າ" - "ກຳລັງດາວໂຫລດ" - "​ກຳ​ລັງ​ຕິດ​ຕັ້ງ" "​ບໍ່​ຮູ້​ຈັກ" - "ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ" - "ລຶບ​ທັງ​ໝົດ" "ລຶບ​" "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 3dd9fc9ec..a6d5edc6b 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Pagrindinis" "Programa neįdiegta." "Programa nepasiekiama" @@ -46,7 +45,6 @@ "Sąranka" "Tai sistemos programa ir jos negalima pašalinti." "Aplankas be pavadinimo" - "%1$d pagrindinis ekranas" "%1$d psl. iš %2$d" "%1$d pagrindinis ekranas iš %2$d" "Sveiki" @@ -57,7 +55,6 @@ "Ekrano fonai, valdikliai ir nustatymai" "Jei norite tinkinti, palieskite ir palaikykite foną" "SUPRATAU" - "Gerai" "Atidarytas aplankas, %1$d ir %2$d" "Palieskite, kad uždarytumėte aplanką" "Palieskite, kad išsaugotumėte naują pavadinimą" @@ -67,12 +64,7 @@ "Valdikliai" "Ekrano fonai" "Nustatymai" - "Laukiama" - "Atsisiunčiama" - "Diegiama" "Nežinoma" - "Neatkurta" - "Pašalinti viską" "Pašalinti" "Ieškoti" "Ši programa neįdiegta" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 07ed9ece4..fdbee78ed 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Sākums" "Lietotne nav instalēta." "Lietotne nav pieejama." @@ -46,7 +45,6 @@ "Notiek iestatīšana" "Šī ir sistēmas lietotne, un to nevar atinstalēt." "Mape bez nosaukuma" - "Sākuma ekrāns: %1$d" "%1$d. lapa no %2$d" "Sākuma ekrāns: %1$d no %2$d" "Laipni lūdzam!" @@ -57,7 +55,6 @@ "Fona tapetes, logrīki un iestatījumi" "Lai pielāgotu, pieskarieties fonam un turiet to nospiestu." "SAPRATU!" - "Labi" "Atvērta mape: %1$d x %2$d" "Pieskarieties, lai aizvērtu mapi." "Pieskarieties, lai saglabātu pārdēvēto nosaukumu." @@ -67,12 +64,7 @@ "Logrīki" "Fona tapetes" "Iestatījumi" - "Gaida" - "Lejupielādē" - "Instalē" "Nezināma" - "Nav atjaunota" - "Noņemt visas" "Noņemt" "Meklēt" "Šī lietotne nav instalēta" diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index 4e7c52573..f6df54dd3 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Почетна страница" "Апликацијата не е инсталирана." "Апликацијата не е достапна" @@ -46,7 +45,6 @@ "Поставување" "Ова е системска апликација и не може да се деинсталира." "Неименувана папка" - "Екран на почетна страница %1$d" "Страница %1$d од %2$d" "Екран на почетна страница %1$d од %2$d" "Добредојдовте" @@ -57,7 +55,6 @@ "Тапети, додатоци и поставки" "Допрете и задржете на заднината за да приспособите" "СФАТИВ" - "Во ред" "Отворена е папка, %1$d на %2$d" "Допри за да се затвори папката" "Допри за да се зачува преименувањето" @@ -67,12 +64,7 @@ "Виџети" "Тапети" "Подесувања" - "На чекање" - "Се презема" - "Се инсталира" "Непознато" - "Не е обновено" - "Отстрани ги сите" "Отстрани" "Барај" "Апликацијата не е инсталирана" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index d1338b6b5..de37f47c4 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ഹോം" "അപ്ലിക്കേഷൻ ഇൻസ്‌റ്റാളുചെ‌യ്‌തിട്ടില്ല." "അപ്ലിക്കേഷൻ ലഭ്യമല്ല" @@ -46,7 +45,6 @@ "സജ്ജീകരിക്കുക" "ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല." "പേരുനൽകാത്ത ഫോൾഡർ" - "ഹോം സ്‌ക്രീൻ %1$d" "പേജ് %1$d / %2$d" "ഹോം സ്‌ക്രീൻ %1$d / %2$d" "സ്വാഗതം" @@ -57,7 +55,6 @@ "വാൾപേപ്പറുകൾ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ" "ഇഷ്‌ടാനുസൃതമാക്കുന്നതിന് പശ്‌ചാത്തലം സ്‌പർശിച്ചുപിടിക്കുക" "മനസ്സിലായി" - "ശരി" "ഫോൾഡർ തുറന്നു, %1$d / %2$d" "ഫോൾഡർ അടയ്ക്കാൻ സ്‌പർശിക്കുക" "പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് സ്‌പർശിക്കുക" @@ -67,12 +64,7 @@ "വിജറ്റുകൾ" "വാൾപേപ്പറുകൾ" "ക്രമീകരണങ്ങൾ" - "കാത്തിരിക്കുന്നു" - "ഡൗൺലോഡുചെയ്യുന്നു" - "ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു" "അജ്ഞാതം" - "പുനഃസ്ഥാപിച്ചിട്ടില്ല" - "എല്ലാം നീക്കം ചെയ്യുക" "നീക്കംചെയ്യുക" "തിരയുക" "ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 6639e6df1..27b166d3b 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Нүүр" "Апп суугаагүй байна." "Апп-г ашиглах боломжгүй" @@ -46,7 +45,6 @@ "Тохируулга" "Энэ апп нь системийн апп ба устгах боломжгүй." "Нэргүй фолдер" - "Нүүр дэлгэц %1$d" "%2$d-н %1$d хуудас" "%2$d-н Нүүр дэлгэц %1$d" "Тавтай морилно уу" @@ -57,7 +55,6 @@ "Дэвсгэр зураг, виджет, & тохиргоо" "Тааруулахын тулд арын дэлгэцэнд хүрээд & барина уу" "Ойлголоо" - "Тийм" "%1$d %2$d фолдер нээгдэв" "Фолдер хаах бол хүрнэ үү" "Шинэ нэрийг хадгалах бол хүрнэ үү" @@ -67,12 +64,7 @@ "Виджет" "Ханын зураг" "Тохиргоо" - "Хүлээж байна" - "Татаж авч байна" - "Суулгаж байна" "Тодорхойгүй" - "Сэргээгээгүй" - "Бүгдийг устгах" "Устгах" "Хайх" "Энэ апп-г суулгаагүй байна" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index 0d61ff3ca..ad75e8842 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "मुख्‍यपृष्‍ठ" "अॅप स्थापित केलेला नाही." "अॅप उपलब्ध नाही" @@ -46,7 +45,6 @@ "सेटअप" "हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही." "अनामित फोल्डर" - "मुख्य स्क्रीन %1$d" "%2$d पैकी %1$d पृष्ठ" "%2$d पैकी %1$d मुख्य स्क्रीन" "सुस्वागतम" @@ -57,7 +55,6 @@ "वॉलपेपर, विजेट आणि सेटिंग्ज" "सानुकूल करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरुन ठेवा" "समजले" - "ठीक" "फोल्डर उघडले, %1$d बाय %2$d" "फोल्डर बंद करण्यासाठी स्पर्श करा" "नवे नाव जतन करण्यासाठी स्पर्श करा" @@ -67,12 +64,7 @@ "विजेट" "वॉलपेपर" "सेटिंग्ज" - "प्रतीक्षारत" - "डाउनलोड करत आहे" - "स्थापित करत आहे" "अज्ञात" - "पुनर्स्थापित झाले नाही" - "सर्व काढा" "काढा" "शोधा" "हा अॅप स्थापित केलेला नाही" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index a9344f5b5..e2b8e162c 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Laman Utama" "Apl tidak dipasang." "Apl tidak tersedia" @@ -46,7 +45,6 @@ "Persediaan" "Ini ialah apl sistem dan tidak boleh dinyahpasang." "Folder Tanpa Nama" - "Skrin Laman Utama %1$d" "Halaman %1$d daripada %2$d" "Skrin Laman Utama %1$d daripada %2$d" "Selamat datang" @@ -57,7 +55,6 @@ "Kertas dinding, widget & tetapan" "Sentuh & tahan latar belakang untuk memperibadikan" "FAHAM" - "OK" "Folder dibuka, %1$d kali %2$d" "Sentuh untuk menutup folder" "Sentuh untuk menyimpan penamaan semula" @@ -67,12 +64,7 @@ "Widget" "Kertas dinding" "Tetapan" - "Menunggu" - "Memuat turun" - "Memasang" "Tidak diketahui" - "Tak dipulihkan" - "Buang Semua" "Alih keluar" "Carian" "Apl ini tidak dipasang" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index de274d7d7..7335605c8 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -20,7 +20,6 @@ "Pelancar" - "Laman Utama" "Tetapkan kertas dinding" "Kertas dinding" @@ -43,10 +42,8 @@ "Masalah memuatkan widget" "Ini adalah aplikasi sistem dan tidak boleh dinyahpasang." "Folder Tanpa Nama" - "Skrin utama %1$d" "Halaman %1$d dari %2$d" "Skrin utama %1$d dari %2$d" - "OK" "Folder dibuka, %1$d kali %2$d" "Sentuh untuk menutup folder" "Sentuh untuk menyimpan penamaan semula" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index fc3f4a4d1..7589f75c1 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -20,7 +20,6 @@ "Launcher၃" - "ပင်မစာမျက်နှာ" "အပ်ပလီကေးရှင်း မထည့်သွင်းထားပါ" "App လက်လှမ်း မမှီပါ" @@ -46,7 +45,6 @@ "စဖွင့်သတ်မှတ်ရန်" "ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ" "အမည်မရှိအကန့်" - "ပင်မစာမျက်နှာ %1$d" "စာမျက်နှာ %1$d မှ %2$d" "ပင်မစာမျက်နှာ %1$d မှ %2$d" "မင်္ဂလာပါ" @@ -57,7 +55,6 @@ "နောက်ခံများ၊ ဝီဂျက်များ& ဆက်တင်များ" "နောက်ခံကို စိတ်တိုင်းကျ ပြုလုပ်ရန် ထိလျက် & ကိုင်ထားပါ" "ရပြီ" - "ကောင်းပြီ" "ဖွင့်ထားသောအကန့်, %1$d နှင့် %2$d" "အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ" "အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ" @@ -67,12 +64,7 @@ "ဝဒ်ဂျက်များ" "နောက်ခံများ" "အပြင်အဆင်များ" - "စောင့်နေ" - "ဒေါင်းလုဒ် လုပ်နေ" - "တပ်ဆင်နေ" "မသိရ" - "ပြန်မဖေါ်ခဲ့" - "အားလုံး ဖယ်ရှားရန်" "ဖယ်ရှားရန်" "ရှာဖွေရန်" "App မတပ်ဆင်ရသေးပါ" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 386019ce6..df9ec93e5 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Startside" "Appen er ikke installert." "Appen er ikke tilgjengelig" @@ -46,7 +45,6 @@ "Konfigurering" "Dette er en systemapp som ikke kan avinstalleres." "Mappe uten navn" - "Startside %1$d" "Side %1$d av %2$d" "Startside %1$d av %2$d" "Velkommen" @@ -57,7 +55,6 @@ "Bakgrunner, moduler og innstillinger" "Trykk og hold på bakgrunnen for å tilpasse den" "SKJØNNER" - "OK" "Mappen er åpnet – %1$d ganger %2$d" "Trykk for å lukke mappen" "Trykk for å lagre det nye navnet" @@ -67,12 +64,7 @@ "Moduler" "Bakgrunner" "Innstillinger" - "Venter …" - "Laster ned …" - "Installerer …" "Ukjent" - "Ikke gjenoppr." - "Fjern alle" "Fjern" "Søk" "Denne appen er ikke installert" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 9efc04fd6..4398334cf 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "गृह" "अनुप्रयोग स्थापित छैन।" "अनुप्रयोग उपलब्ध छैन" @@ -46,7 +45,6 @@ "सेटअप" "यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।" "बेनाम फोल्डर" - "गृह स्क्रिन %1$d" "पृष्ठ %2$d को %1$d" "गृह स्क्रिन %2$d को %2$d" @@ -60,7 +58,6 @@ "वालपेपरहरू, विजेट; सेटिङहरू" "छुनुहोस् ; अनुकूलन पृष्ठभूमि होल्ड गर्नुहोस्" "बुझियो" - "ठिक छ" "फोल्डर खुल्यो %1$d बाट %2$d" "फोल्डर बन्द गर्नको लागि टच गर्नुहोस्" "पुन: नामाकरण बचत गर्न टच गर्नुहोस्।" @@ -70,12 +67,7 @@ "विजेटहरू" "वालपेपरहरु" "सेटिंङहरू" - "पर्खँदै" - "डाउनलोड हुँदै" - "स्थापना गर्दै" "अज्ञात" - "पुनर्स्थापित भएन" - "सबै हटाउनुहोस्" "हटाउनुहोस्" "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index d9b6aea2e..8a82624b3 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Startpagina" "App is niet geïnstalleerd." "App is niet beschikbaar" @@ -46,7 +45,6 @@ "Configuratie" "Dit is een systeemapp die niet kan worden verwijderd." "Naamloze map" - "Startscherm %1$d" "Pagina %1$d van %2$d" "Startscherm %1$d van %2$d" "Welkom" @@ -57,7 +55,6 @@ "Achtergronden, widgets en instellingen" "Blijf de achtergrond aanraken om deze aan te passen" "OK" - "OK" "Map geopend, %1$d bij %2$d" "Raak dit aan om de map te sluiten" "Raak dit aan om de gewijzigde naam op te slaan" @@ -67,12 +64,7 @@ "Widgets" "Achtergronden" "Instellingen" - "Wachten" - "Downloaden" - "Installeren" "Onbekend" - "Niet hersteld" - "Alles verwijderen" "Verwijderen" "Zoeken" "Deze app is niet geïnstalleerd" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 5506781bb..94da2a52c 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Ekran główny" "Aplikacja nie jest zainstalowana." "Aplikacja niedostępna" @@ -46,7 +45,6 @@ "Konfiguracja" "To aplikacja systemowa i nie można jej odinstalować." "Folder bez nazwy" - "Ekran główny %1$d" "Strona %1$d z %2$d" "Ekran główny %1$d z %2$d" "Witamy" @@ -57,7 +55,6 @@ "Tapety, widżety i ustawienia" "Kliknij i przytrzymaj tło, by dostosować" "OK" - "OK" "Folder otwarty, %1$d na %2$d" "Kliknij, by zamknąć folder" "Kliknij, by zapisać zmianę nazwy" @@ -67,12 +64,7 @@ "Widżety" "Tapety" "Ustawienia" - "Oczekiwanie" - "Pobieranie" - "Instalowanie" "Brak informacji" - "Nie przywrócono" - "Usuń wszystkie" "Usuń" "Szukaj" "Ta aplikacja nie jest zainstalowana" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index db2f793ae..ef1fa6f4e 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -20,7 +20,6 @@ "Iniciador3" - "Ecrã principal" "A aplicação não está instalada." "A aplicação não está disponível" @@ -46,7 +45,6 @@ "Configuração" "É uma aplicação de sistema e não pode ser desinstalada." "Pasta sem nome" - "Ecrã principal %1$d" "Página %1$d de %2$d" "Ecrã principal %1$d de %2$d" "Bem-vindo(a)" @@ -57,7 +55,6 @@ "Imagens de fundo, widgets e definições" "Toque sem soltar no fundo para personalizar" "COMPREENDI" - "OK" "Pasta aberta, %1$d por %2$d" "Toque para fechar a pasta" "Toque para guardar o nome novo" @@ -67,12 +64,7 @@ "Widgets" "Imagens de fundo" "Definições" - "A aguardar" - "A transferir " - "A instalar" "Desconhecido" - "Não restaurado" - "Remover todos" "Remover" "Pesquisar" "Esta aplicação não está instalada" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 7172b24e7..8c592c191 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Início" "O app não está instalado." "O app não está disponível" @@ -46,7 +45,6 @@ "Configuração" "Este é um app do sistema e não pode ser desinstalado." "Pasta sem nome" - "Tela inicial %1$d" "Página %1$d de %2$d" "Tela inicial %1$d de %2$d" "Bem-vindo" @@ -57,7 +55,6 @@ "Plano de fundo, widgets e configurações" "Toque e mantenha pressionado o segundo plano para personalizar" "ENTENDI" - "Ok" "Pasta aberta, %1$d por %2$d" "Toque para fechar a pasta" "Toque para salvar o novo nome" @@ -67,12 +64,7 @@ "Widgets" "Planos de fundo" "Configurações" - "Aguardando" - "Transferindo" - "Instalando" "Desconhecido" - "Não restaurado" - "Remover tudo" "Remover" "Pesquisar" "Este app não está instalado" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index b2da9fba4..1bcaa5a82 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Ecran de pornire" "Aplicația nu este instalată." "Aplicația nu este disponibilă" @@ -46,7 +45,6 @@ "Configurați" "Aceasta este o aplicație de sistem și nu poate fi dezinstalată." "Dosar fără nume" - "Ecran de pornire %1$d" "Pagina %1$d din %2$d" "Ecranul de pornire %1$d din %2$d" "Bun venit" @@ -57,7 +55,6 @@ "Imagini de fundal, widgeturi și setări" "Atingeți lung fundalul pentru a-l personaliza" "AM ÎNȚELES" - "OK" "Dosar deschis, %1$d pe %2$d" "Atingeți pentru a închide dosarul" "Atingeți pentru a salva redenumirea" @@ -67,12 +64,7 @@ "Widgeturi" "Imagini de fundal" "Setări" - "În așteptare" - "Se descarcă" - "Se instalează" "Necunoscut" - "Nerestabilit" - "Eliminați-le pe toate" "Eliminați" "Căutați" "Aplicația nu este instalată" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index c0a8d023b..c5eb1c27d 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Главный экран" "Приложение удалено" "Приложение недоступно" @@ -46,7 +45,6 @@ "Настройка" "Это системное приложение, его нельзя удалить." "Папка без названия" - "Главный экран %1$d" "Стр. %1$d из %2$d" "Главные экран %1$d из %2$d" "Добро пожаловать!" @@ -57,7 +55,6 @@ "Обои, виджеты и настройки" "Чтобы выполнить настройку, коснитесь фона и удерживайте его" "ОК" - "ОК" "Папка открыта, %1$d x %2$d" "Нажмите, чтобы закрыть папку" "Нажмите, чтобы подтвердить переименование" @@ -67,12 +64,7 @@ "Виджеты" "Обои" "Настройки" - "Ожидается" - "Скачивается" - "Устанавливается" "Неизвестно" - "Не восстановлен" - "Удалить все" "Удалить" "Найти" "Приложение не установлено" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index 3b8d81134..fcfa71739 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "මුල් පිටුව" "යෙදුම ස්ථාපනය කර නැත." "යෙදුම නොතිබේ" @@ -46,7 +45,6 @@ "ස්ථාපනය කරන්න" "මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක." "නම් නොකළ ෆෝල්ඩරය" - "මුල් පිටු තිරය %1$d" "%2$d හි %1$d පිටුව" "මුල් පිටු තිරය %2$d හි %1$d" "සාදරයෙන් පිළිගනිමු" @@ -57,7 +55,6 @@ "වෝල්පේපර, විජට්, සහ සැකසීම්" "පසුබිම අභිරුචිකරණය කිරීමට ස්පර්ශ කර අල්ලා සිටින්න" "තේරුණා" - "හරි" "ෆෝල්ඩරය විවෘත විය, %1$d හි %2$d" "ෆෝල්ඩරය වැසීමට ස්පර්ශ කරන්න" "නැවත නම් කිරීම සුරැකීමට ස්පර්ශ කරන්න" @@ -67,12 +64,7 @@ "විජට්" "වෝල්පේපර" "සැකසීම්" - "රැඳී සිටිමින්" - "බාගනිමින්" - "ස්ථාපනය කරමින්" "නොදනී" - "ප්‍රතිස්ථාපනය කළේ නැත" - "සියල්ල ඉවත් කරන්න" "ඉවත් කරන්න" "සොයන්න" "මෙම යෙදුම ස්ථාපනය කර නොමැත" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 8cfb99227..7b20ba41a 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Plocha" "Aplikácia nie je nainštalovaná." "Aplikácia nie je k dispozícii" @@ -46,7 +45,6 @@ "Nastavenie" "Toto je systémová aplikácia a nedá sa odinštalovať." "Nepomenovaný priečinok" - "Plocha %1$d" "Stránka %1$d z %2$d" "Plocha %1$d z %2$d" "Vitajte!" @@ -57,7 +55,6 @@ "Pozadia, miniaplikácie a nastavenia" "Ak si chcete pozadie prispôsobiť, klepnite naň a podržte ho" "ROZUMIEM" - "OK" "Otvorený priečinok, %1$d x %2$d" "Dotykom zavriete priečinok" "Dotykom premenovanie uložíte" @@ -67,12 +64,7 @@ "Miniaplikácie" "Tapety" "Nastavenia" - "Čaká sa" - "Sťahovanie" - "Inštalácia" "Neznáme" - "Nebolo obnovené" - "Odstrániť všetky" "Odstrániť" "Vyhľadať" "Táto aplikácia nie je nainštalovaná" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 63a4bac03..ae8c071fa 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -20,7 +20,6 @@ "Zaganjalnik3" - "Začetni zaslon" "Aplikacija ni nameščena." "Aplikacija ni na voljo" @@ -46,7 +45,6 @@ "Nastavitev" "To je sistemska aplikacija in je ni mogoče odstraniti." "Neimenovana mapa" - "Začetni zaslon %1$d" "Stran %1$d od %2$d" "Začetni zaslon %1$d od %2$d" "Pozdravljeni" @@ -57,7 +55,6 @@ "Ozadja, pripomočki in nastavitve" "Za prilagajanje se dotaknite ozadja in ga pridržite" "V REDU" - "V redu" "Mapa je odprta, %1$d krat %2$d" "Dotaknite se, da zaprete mapo" "Dotaknite se, da shranite preimenovanje" @@ -67,12 +64,7 @@ "Pripomočki" "Ozadja" "Nastavitve" - "Čakanje" - "Prenašanje" - "Nameščanje" "Neznano" - "Ni obnovljen" - "Odstrani vse" "Odstrani" "Iskanje" "Ta aplikacija ni nameščena." diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 955c44cc9..744fe37a1 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Почетна" "Апликација није инсталирана." "Апликација није доступна" @@ -46,7 +45,6 @@ "Подешавање" "Ово је системска апликација и не може да се деинсталира." "Неименовани директоријум" - "Почетни екран %1$d" "%1$d. страница од %2$d" "%1$d. почетни екран од %2$d" "Добро дошли" @@ -57,7 +55,6 @@ "Позадине, виџети и подешавања" "Додирните и задржите позадину да бисте прилагодили" "ВАЖИ" - "Потврди" "Директоријум је отворен, %1$d пута %2$d" "Додирните да бисте затворили директоријум" "Додирните да бисте сачували промену имена" @@ -67,12 +64,7 @@ "Виџети" "Позадине" "Подешавања" - "Чека се" - "Преузима се" - "Инсталира се" "Непознато" - "Није враћено" - "Уклони све" "Уклони" "Претражи" "Ова апликација није инсталирана" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 636cabc1f..cb1a42986 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Startskärm" "Appen är inte installerad." "Appen är inte tillgänglig" @@ -46,7 +45,6 @@ "Konfiguration" "Det här är en systemapp som inte kan avinstalleras." "Namnlös mapp" - "Startskärmen %1$d" "Sidan %1$d av %2$d" "Startskärmen %1$d av %2$d" "Välkommen" @@ -57,7 +55,6 @@ "Bakgrunder, widgetar och inställningar" "Tryck länge på bakgrunden om du vill anpassa den" "OK" - "OK" "Mappen är öppen, %1$d gånger %2$d" "Tryck om du vill stänga mappen" "Tryck om du vill spara det nya namnet" @@ -67,12 +64,7 @@ "Widgetar" "Bakgrunder" "Inställningar" - "Väntar" - "Hämtas" - "Installerar" "Okänt" - "Inte återställt" - "Ta bort alla" "Ta bort" "Sök" "Appen är inte installerad" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 6a2abcc5f..5283e47c9 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -20,7 +20,6 @@ "Kizindua3" - "Mwanzo" "Programu haijasakinishwa." "Programu haipatikani" @@ -46,7 +45,6 @@ "Sanidi" "Hii ni programu ya mfumo na haiwezi kuondolewa." "Folda isiyo na jina" - "Skrini ya mwazo %1$d" "Ukurasa%1$d wa %2$d" @@ -59,7 +57,6 @@ "Mandhari, wijeti, na mipangilio" "Gusa na ushikilie mandhari ili uweke mapendeleo" "NIMEELEWA" - "SAWA" "Folda imefunguliwa, %1$d kwa %2$d" "Gusa ili ufunge folda" "Gusa ili uhifadhi jina jipya" @@ -69,12 +66,7 @@ "Wijeti" "Mandhari" "Mipangilio" - "Inasubiri" - "Inapakua" - "Inasakinisha" "Yasiyojulikana" - "Haijarejeshwa" - "Ondoa Zote" "Ondoa" "Tafuta" "Programu hii haijasakinishwa" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index 9c7e699df..a610bf82d 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -20,7 +20,6 @@ "லாஞ்சர்3" - "முகப்பு" "பயன்பாடு நிறுவப்படவில்லை." "பயன்பாடு இல்லை" @@ -46,7 +45,6 @@ "அமைவு" "இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது." "பெயரிடப்படாத கோப்புறை" - "முகப்புத் திரை %1$d" "பக்கம் %1$d / %2$d" "முகப்புத் திரை %1$d of %2$d" "வரவேற்கிறோம்" @@ -57,7 +55,6 @@ "வால்பேப்பர்கள், விட்ஜெட்கள் & அமைப்புகள்" "தனிப்பயனாக்க, பின்னணியைத் தொட்டுப் பிடிக்கவும்" "புரிந்தது" - "சரி" "திறக்கப்பட்டக் கோப்புறை, %1$d x %2$d" "கோப்புறையை மூட, தொடவும்" "மறுபெயரிட்டதைச் சேமிக்க, தொடவும்" @@ -67,12 +64,7 @@ "ஷார்ட்கட்ஸ்" "வால்பேப்பர்கள்" "அமைப்பு" - "காத்திருக்கிறது" - "பதிவிறக்குகிறது" - "நிறுவுகிறது" "தெரியாதது" - "மீட்டெடுக்க முடியாது" - "அனைத்தையும் அகற்று" "அகற்று" "தேடு" "பயன்பாடு நிறுவப்படவில்லை" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index 19e45e799..e3fd8e405 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -20,7 +20,6 @@ "లాంచర్3" - "హోమ్" "అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు." "అనువర్తనం అందుబాటులో లేదు" @@ -46,7 +45,6 @@ "సెటప్ చేయి" "ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." "పేరు లేని ఫోల్డర్" - "హోమ్ స్క్రీన్ %1$d" "%2$dలో %1$dవ పేజీ" "%2$dలో %1$dవ హోమ్ స్క్రీన్" "స్వాగతం" @@ -57,7 +55,6 @@ "వాల్‌పేపర్‌లు, విడ్జెట్‌లు & సెట్టింగ్‌లు" "అనుకూలీకరించడానికి నేపథ్యాన్ని నొక్కి & ఉంచండి" "అర్థమైంది" - "సరే" "ఫోల్డర్ తెరవబడింది, %1$d X %2$d" "ఫోల్డర్‌ను మూసివేయడానికి తాకండి" "పేరు మార్పును సేవ్ చేయడానికి తాకండి" @@ -67,12 +64,7 @@ "విడ్జెట్‌లు" "వాల్‌పేపర్‌లు" "సెట్టింగ్‌లు" - "వేచి ఉంది" - "డౌన్‌లోడ్ చేస్తోంది" - "ఇన్‌స్టాల్ చేస్తోంది" "తెలియదు" - "పునరుద్ధరించబడలేదు" - "అన్నీ తీసివేయి" "తీసివేయి" "శోధించు" "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 262788544..72d2d4bad 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "หน้าแรก" "ไม่ได้ติดตั้งแอป" "แอปไม่พร้อมใช้งาน" @@ -46,7 +45,6 @@ "ตั้งค่า" "นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้" "โฟลเดอร์ที่ไม่มีชื่อ" - "หน้าจอหลัก %1$d" "หน้า %1$d จาก %2$d" "หน้าจอหลัก %1$d จาก %2$d" "ยินดีต้อนรับ" @@ -57,7 +55,6 @@ "วอลเปเปอร์ วิดเจ็ต และการตั้งค่า" "แตะพื้นหลังค้างไว้เพื่อกำหนดค่า" "รับทราบ" - "ตกลง" "เปิดโฟลเดอร์ %1$d x %2$d" "แตะเพื่อปิดโฟลเดอร์" "แตะเพื่อบันทึกการเปลี่ยนชื่อ" @@ -67,12 +64,7 @@ "วิดเจ็ต" "วอลเปเปอร์" "การตั้งค่า" - "กำลังรอ" - "กำลังดาวน์โหลด" - "กำลังติดตั้ง" "ไม่รู้จัก" - "ไม่ได้คืนค่า" - "ลบทั้งหมด" "ลบ" "ค้นหา" "ไม่ได้ติดตั้งแอปนี้" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 5356aa596..948139bda 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Home" "Hindi naka-install ang app." "Hindi available ang app" @@ -46,7 +45,6 @@ "I-setup" "Isa itong app ng system at hindi maaaring i-uninstall." "Walang Pangalang Folder" - "Home screen %1$d" "Pahina %1$d ng %2$d" "Home screen %1$d ng %2$d" "Maligayang Pagdating" @@ -57,7 +55,6 @@ "Mga wallpaper, widget at setting" "Pindutin nang matagal ang background upang i-customize" "NAKUHA KO" - "OK" "Binuksan ang folder, %1$d by %2$d" "Pindutin upang isara ang folder" "Pindutin upang i-save ang pagpapalit ng pangalan" @@ -67,12 +64,7 @@ "Mga Widget" "Mga Wallpaper" "Mga Setting" - "Naghihintay" - "Nagda-download" - "Nag-i-install" "Hindi kilala" - "Hindi naibalik" - "Alisin Lahat" "Alisin" "Maghanap" "Hindi naka-install ang app na ito" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 24e7815d5..b9add4e20 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Ana ekran" "Uygulama yüklü değil." "Uygulama kullanılamıyor" @@ -46,7 +45,6 @@ "Kurulum" "Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz." "Adsız Klasör" - "Ana ekran %1$d" "Sayfa %1$d / %2$d" "Ana ekran %1$d / %2$d" "Hoş geldiniz" @@ -57,7 +55,6 @@ "Duvar kağıtları, widget\'lar ve ayarlar" "Özelleştirmek için arka plana dokunun ve basılı tutun" "TAMAM" - "Tamam" "Klasör açıldı, %1$d x %2$d" "Klasörü kapatmak için dokunun" "Yeni adı kaydetmek için dokunun" @@ -67,12 +64,7 @@ "Widget\'lar" "Duvar Kağıtları" "Ayarlar" - "Bekliyor" - "İndiriliyor" - "Yükleniyor" "Bilinmiyor" - "Geri yüklenmedi" - "Tümünü Kaldır" "Kaldır" "Ara" "Bu uygulama yüklü değil" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 75a2a6091..a1eead8b8 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Головний екран" "Додаток видалено." "Додаток недоступний" @@ -46,7 +45,6 @@ "Налаштування" "Це системна програма, її неможливо видалити." "Папка без назви" - "Головний екран %1$d" "Сторінка %1$d з %2$d" "Головний екран %1$d з %2$d" "Вітаємо" @@ -57,7 +55,6 @@ "Фонові малюнки, віджети й налаштування" "Натисніть і втримуйте фон, щоб налаштувати робочу область" "ЗРОЗУМІЛО" - "OК" "Папку відкрито (%1$d х %2$d)" "Торкніться, щоб закрити папку" "Торкніться, щоб зберегти нову назву" @@ -67,12 +64,7 @@ "Віджети" "Фонові малюнки" "Налаштування" - "Очікування" - "Завантаження" - "Встановлення" "Невідомо" - "Не відновлено" - "Видалити всі" "Видалити" "Шукати" "Цей додаток не встановлено" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index 70549246e..efad22861 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ہوم" "ایپ انسٹال نہیں ہے۔" "ایپ دستیاب نہیں ہے" @@ -46,7 +45,6 @@ "ترتیب دیں" "یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔" "بلا نام فولڈر" - "‏ہوم اسکرین ‎%1$d" "‏صفحہ ‎%1$d از ‎%2$d" "‏ہوم اسکرین ‎%1$d از ‎%2$d" "خوش آمدید" @@ -57,7 +55,6 @@ "وال پیپرز، ویجیٹس اور ترتیبات" "حسب ضرورت بنانے کیلئے پس منظر کو ٹچ کریں اور دبائے رکھیں" "سمجھ آ گئی" - "ٹھیک ہے" "فولڈر کھولا گیا، %1$d × %2$d" "فولڈر بند کرنے کیلئے ٹچ کریں" "نام کی تبدیلی محفوظ کرنے کیلئے ٹچ کریں" @@ -67,12 +64,7 @@ "ویجیٹس" "وال پیپرز" "ترتیبات" - "منتظر" - "ڈاؤن لوڈ کیا جا رہا ہے" - "انسٹال کیا جا رہا ہے" "نامعلوم" - "بحال نہیں ہوا" - "سبھی کو ہٹا دیں" "ہٹائیں" "تلاش کریں" "یہ ایپ انسٹال کردہ نہیں ہے" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index a5c461321..b594e7db2 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -20,7 +20,6 @@ "Ishga tushirgich3" - "Uy" "Ilova o‘rnatilmadi." "Ilova mavjud emas" @@ -46,7 +45,6 @@ "Sozlash" "Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi." "Nomsiz jild" - "Uy ekrani %1$d" "%2$ddan %1$d ta sahifa" "Uy ekrani %2$ddan %1$d" "Xush kelibsiz" @@ -57,7 +55,6 @@ "Orqa fon rasmlari, vidjet va sozlamalar" "Orqa fonni moslashtirish uchun uni bosing va ushlab turing" "OK" - "OK" "Jild ochildi, %1$d ga %2$d" "Jildni yopish uchun bosing" "O‘zgartirilgan nomni saqlash uchun bosing" @@ -67,12 +64,7 @@ "Vidjetlar" "Orqa fon rasmlari" "Sozlamalar" - "Kutilmoqda" - "Yuklab olinmoqda" - "O‘rnatilmoqda" "Noma’lum" - "Qayta tiklanmadi" - "Barchasini o‘chirish" "O‘chirish" "Qidirish" "Ushbu ilova o‘rnatilmagan" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 45bfa4cd2..278bb5484 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Màn hình chính" "Ứng dụng chưa được cài đặt." "Ứng dụng không có sẵn" @@ -46,7 +45,6 @@ "Thiết lập" "Đây là ứng dụng hệ thống và không thể gỡ cài đặt." "Thư mục chưa đặt tên" - "Màn hình chính %1$d" "Trang %1$d / %2$d" "Màn hình chính %1$d / %2$d" "Chào mừng" @@ -57,7 +55,6 @@ "Hình nền, tiện ích và cài đặt" "Chạm và giữ nền để tùy chỉnh" "OK" - "OK" "Đã mở thư mục, %1$d x %2$d" "Chạm để đóng thư mục" "Chạm để lưu tên mới" @@ -67,12 +64,7 @@ "Tiện ích con" "Hình nền" "Cài đặt" - "Đang đợi" - "Đang tải xuống" - "Đang cài đặt" "Không xác định" - "Không được khôi phục" - "Xóa tất cả" "Xóa" "Tìm kiếm" "Ứng dụng này chưa được cài đặt" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index c8ca12d08..9cd26b946 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "主屏" "未安装该应用。" "应用不可用" @@ -46,7 +45,6 @@ "设置" "这是系统应用,无法卸载。" "未命名文件夹" - "主屏幕%1$d" "第%1$d页,共%2$d页" "主屏幕:第%1$d屏,共%2$d屏" "欢迎使用" @@ -57,7 +55,6 @@ "壁纸、小部件和设置" "触摸并按住背景,即可进行个性化设置" "知道了" - "确定" "文件夹已打开,大小为%1$d×%2$d" "触摸可关闭文件夹" "触摸可保存新名称" @@ -67,12 +64,7 @@ "小部件" "壁纸" "设置" - "正在等待" - "正在下载" - "正在安装" "未知" - "无法还原" - "全部移除" "移除" "搜索" "未安装此应用" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 132df1417..052e8a0a1 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "主畫面" "尚未安裝應用程式。" "目前無法使用這個應用程式" @@ -46,7 +45,6 @@ "設定" "這是系統應用程式,無法將其解除安裝。" "未命名的資料夾" - "主畫面 %1$d" "第 %1$d 頁,共 %2$d 頁" "主畫面 %1$d,共 %2$d 個" "歡迎" @@ -57,7 +55,6 @@ "桌布、小工具和設定" "輕觸並按住背景即可自訂" "知道了" - "確定" "資料夾已開啟 (%1$d x %2$d)" "輕觸即可關閉資料夾" "輕觸即可儲存新改的名稱" @@ -67,12 +64,7 @@ "小工具" "桌布" "設定" - "等候中" - "下載中" - "安裝中" "不明" - "無法還原" - "全部移除" "移除" "搜尋" "尚未安裝這個應用程式" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index d1e41fb6b..b6b5c4eb5 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "主螢幕" "應用程式未安裝。" "應用程式目前無法使用" @@ -46,7 +45,6 @@ "設定" "這是系統應用程式,不可解除安裝。" "未命名的資料夾" - "主螢幕 %1$d" "第 %1$d 頁,共 %2$d 頁" "主螢幕:第 %1$d 頁,共 %2$d 頁" "歡迎使用" @@ -57,7 +55,6 @@ "桌布、小工具和設定" "輕觸並按住背景即可自訂" "知道了" - "確定" "資料夾已開啟 (%1$d x %2$d)" "輕觸即可關閉資料夾" "輕觸即可儲存新名稱" @@ -67,12 +64,7 @@ "小工具" "桌布" "設定" - "等待中" - "下載中…" - "安裝中" "不明" - "無法還原" - "全部移除" "移除" "搜尋" "尚未安裝這個應用程式" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 73852bfc6..2dbd72237 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -20,7 +20,6 @@ "Isiqalisi3" - "Ikhaya" "Uhlelo lokusebenza alufakiwe." "Uhlelo lokusebenza alutholakali" @@ -46,7 +45,6 @@ "Ukumisa" "Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa." "Ifolda engenagama" - "Isikrini sasekhaya esingu-%1$d" "Ikhasi elingu-%1$d kwangu-%2$d" "Isikrini sasekhaya esingu-%1$d se-%2$d" "Siyakwamukela" @@ -57,7 +55,6 @@ "Izithombe zangemuva, amawijethi, nezilungiselelo" "Thinta uphinde ubambe ingemuva ukuze wenze ngokwezifiso" "NGIYITHOLILE" - "KULUNGILE" "Ifolda ivuliwe, %1$d nge-%2$d" "Thinta ukuze uvale ifolda" "Thinta ukuze ulondoloze ukuqamba kabusha" @@ -67,12 +64,7 @@ "Amawijethi" "Izithombe zangemuva" "Izilungiselelo" - "Ilindile" - "Iyalanda" - "Iyafaka" "Akwaziwa" - "Ayibuyiselwe" - "Susa konke" "Susa" "Sesha" "Lolu hlelo lokusebenza alifakiwe" -- cgit v1.2.3 From 170ca1ccd81a1a73bec94fa468a8b54d7d25717b Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 17 Jun 2015 20:18:23 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I8c0c8c4e5f5e09c5852d31429c8f668d4229cc37 Auto-generated-cl: translation import --- WallpaperPicker/res/values-en-rAU/strings.xml | 36 ++++++++++ WallpaperPicker/res/values-fr/strings.xml | 2 +- WallpaperPicker/res/values-hr/strings.xml | 2 +- WallpaperPicker/res/values-kk-rKZ/strings.xml | 2 +- WallpaperPicker/res/values-uz-rUZ/strings.xml | 6 +- res/values-af/strings.xml | 29 +++++++- res/values-am/strings.xml | 29 +++++++- res/values-ar/strings.xml | 29 +++++++- res/values-bg/strings.xml | 29 +++++++- res/values-bn-rBD/strings.xml | 29 +++++++- res/values-ca/strings.xml | 29 +++++++- res/values-cs/strings.xml | 29 +++++++- res/values-da/strings.xml | 29 +++++++- res/values-de/strings.xml | 29 +++++++- res/values-el/strings.xml | 29 +++++++- res/values-en-rAU/strings.xml | 100 ++++++++++++++++++++++++++ res/values-en-rGB/strings.xml | 29 +++++++- res/values-en-rIN/strings.xml | 29 +++++++- res/values-es-rUS/strings.xml | 29 +++++++- res/values-es/strings.xml | 29 +++++++- res/values-et-rEE/strings.xml | 29 +++++++- res/values-eu-rES/strings.xml | 29 +++++++- res/values-fa/strings.xml | 29 +++++++- res/values-fi/strings.xml | 29 +++++++- res/values-fr-rCA/strings.xml | 29 +++++++- res/values-fr/strings.xml | 29 +++++++- res/values-gl-rES/strings.xml | 29 +++++++- res/values-hi/strings.xml | 29 +++++++- res/values-hr/strings.xml | 29 +++++++- res/values-hu/strings.xml | 29 +++++++- res/values-hy-rAM/strings.xml | 35 +++++++-- res/values-in/strings.xml | 29 +++++++- res/values-is-rIS/strings.xml | 29 +++++++- res/values-it/strings.xml | 29 +++++++- res/values-iw/strings.xml | 29 +++++++- res/values-ja/strings.xml | 29 +++++++- res/values-ka-rGE/strings.xml | 29 +++++++- res/values-kk-rKZ/strings.xml | 29 +++++++- res/values-km-rKH/strings.xml | 29 +++++++- res/values-kn-rIN/strings.xml | 29 +++++++- res/values-ko/strings.xml | 29 +++++++- res/values-ky-rKG/strings.xml | 29 +++++++- res/values-lo-rLA/strings.xml | 29 +++++++- res/values-lt/strings.xml | 29 +++++++- res/values-lv/strings.xml | 29 +++++++- res/values-mk-rMK/strings.xml | 29 +++++++- res/values-ml-rIN/strings.xml | 29 +++++++- res/values-mn-rMN/strings.xml | 29 +++++++- res/values-mr-rIN/strings.xml | 29 +++++++- res/values-ms-rMY/strings.xml | 29 +++++++- res/values-my-rMM/strings.xml | 29 +++++++- res/values-nb/strings.xml | 29 +++++++- res/values-ne-rNP/strings.xml | 34 +++++++-- res/values-nl/strings.xml | 31 +++++++- res/values-pl/strings.xml | 29 +++++++- res/values-pt-rPT/strings.xml | 29 +++++++- res/values-pt/strings.xml | 29 +++++++- res/values-ro/strings.xml | 29 +++++++- res/values-ru/strings.xml | 29 +++++++- res/values-si-rLK/strings.xml | 29 +++++++- res/values-sk/strings.xml | 33 ++++++++- res/values-sl/strings.xml | 29 +++++++- res/values-sr/strings.xml | 29 +++++++- res/values-sv/strings.xml | 29 +++++++- res/values-sw/strings.xml | 29 +++++++- res/values-ta-rIN/strings.xml | 29 +++++++- res/values-te-rIN/strings.xml | 29 +++++++- res/values-th/strings.xml | 29 +++++++- res/values-tl/strings.xml | 29 +++++++- res/values-tr/strings.xml | 29 +++++++- res/values-uk/strings.xml | 31 +++++++- res/values-ur-rPK/strings.xml | 29 +++++++- res/values-uz-rUZ/strings.xml | 33 ++++++++- res/values-vi/strings.xml | 29 +++++++- res/values-zh-rCN/strings.xml | 29 +++++++- res/values-zh-rHK/strings.xml | 29 +++++++- res/values-zh-rTW/strings.xml | 29 +++++++- res/values-zu/strings.xml | 29 +++++++- 78 files changed, 2168 insertions(+), 91 deletions(-) create mode 100644 WallpaperPicker/res/values-en-rAU/strings.xml create mode 100644 res/values-en-rAU/strings.xml diff --git a/WallpaperPicker/res/values-en-rAU/strings.xml b/WallpaperPicker/res/values-en-rAU/strings.xml new file mode 100644 index 000000000..c057c0cb3 --- /dev/null +++ b/WallpaperPicker/res/values-en-rAU/strings.xml @@ -0,0 +1,36 @@ + + + + + "Set wallpaper" + "Couldn\'t load image" + "Couldn\'t load image as wallpaper" + + "%1$d selected" + "%1$d selected" + "%1$d selected" + + "Wallpaper %1$d of %2$d" + "Selected %1$s" + "Delete" + "Pick image" + "Wallpapers" + "Crop wallpaper" + diff --git a/WallpaperPicker/res/values-fr/strings.xml b/WallpaperPicker/res/values-fr/strings.xml index ea07db1e5..37846e0f5 100644 --- a/WallpaperPicker/res/values-fr/strings.xml +++ b/WallpaperPicker/res/values-fr/strings.xml @@ -32,5 +32,5 @@ "Supprimer" "Sélectionner une image" "Fonds d\'écran" - "Rogner le fond d\'écran" + "Recadrer le fond d\'écran" diff --git a/WallpaperPicker/res/values-hr/strings.xml b/WallpaperPicker/res/values-hr/strings.xml index ff2eed2cc..5e8322f9b 100644 --- a/WallpaperPicker/res/values-hr/strings.xml +++ b/WallpaperPicker/res/values-hr/strings.xml @@ -19,7 +19,7 @@ - "Postavi pozadinsku sliku" + "Postavi pozadinu" "Nije moguće učitati sliku" "Nije moguće učitati sliku kao pozadinsku sliku" diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml index b75562ec9..dcf372cf5 100644 --- a/WallpaperPicker/res/values-kk-rKZ/strings.xml +++ b/WallpaperPicker/res/values-kk-rKZ/strings.xml @@ -19,7 +19,7 @@ - "Артқы фонды орнату" + "Тұсқағаз орнату" "Суретті жүктей алмады" "Суретті артқы фон ретінде жүктей алмады" diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml index 73d7a0d1e..751f457e7 100644 --- a/WallpaperPicker/res/values-uz-rUZ/strings.xml +++ b/WallpaperPicker/res/values-uz-rUZ/strings.xml @@ -24,13 +24,13 @@ "Fon rasmi sifatida rasm yuklanmadi" "%1$d ta tanlandi" - "%1$d tanlandi" + "%1$d ta tanlandi" "%1$d ta tanlandi" "Fon rasmi %2$ddan %1$d" "%1$s tanlandi" "O‘chirish" - "Rasmni tanlang" - "Fon rasmlari" + "Rasm tanlang" + "Orqa fon rasmlari" "Fon rasmini kesish" diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 397e759ad..102a70d22 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Werk" "Program is nie geïnstalleer nie." "Program is nie beskikbaar nie" "Afgelaaide program in veiligmodus gedeaktiveer" @@ -28,6 +29,9 @@ "Wys Mem" "Raak en hou om \'n legstuk op te tel." "%1$d × %2$d" + "Deursoek programme" + "Laai tans programme …" + "Geen programme gevind wat met \"%1$s\" ooreenstem nie" "Niks meer spasie op die tuisskerm nie." "Geen plek meer in die Gunstelinge-laai nie" "Programme" @@ -69,5 +73,28 @@ "Soek" "Hierdie program is nie geïnstalleer nie" "Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer." - "Voeg by werkspasie" + "Voeg by tuisskerm" + "Skuif item hierheen" + "Item is by tuisskerm gevoeg" + "Item is verwyder" + "Skuif item" + "Skuif na ry %1$s kolom %2$s" + "Skuif na posisie %1$s" + "Skuif na gunstelingposisie %1$s" + "Item geskuif" + "Voeg by vouer: %1$s" + "Voeg by vouer met %1$s" + "Item by vouer gevoeg" + "Skep vouer met: %1$s" + "Vouer geskep" + "Skuif na tuisskerm" + "Skuif skerm na links" + "Skuif skerm na regs" + "Skerm is geskuif" + "Verander grootte" + "Vermeerder breedte" + "Vermeerder hoogte" + "Verminder breedte" + "Verminder hoogte" + "Legstukgrootte is verander na breedte %1$s hoogte %2$s" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 87aa07163..9a910f679 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "ማስጀመሪያ3" + "ስራ" "መተግበሪያ አልተጫነም።" "መተግበሪያ አይገኝም" "የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል" @@ -28,6 +29,9 @@ "ማህደረ ማስታወሻ አሳይ" "ፍርግም ለማንሳት ይንኩ እና ይያዙት" "%1$d × %2$d" + "መተግበሪያዎችን ይፈልጉ" + "መተግበሪያዎችን በመጫን ላይ..." + "ከ«%1$s» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም" "በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።" "በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም" "መተግበሪያዎች" @@ -69,5 +73,28 @@ "ፈልግ" "ይህ መተግበሪያ አልተጫነም" "የዚህ አዶ መተግበሪያ አልተጫነም። ማስወገድ ወይም መተግበሪያውን መፈለግና ራስዎ መጫን ይችላሉ።" - "ወደ ስራ ቦታ ያክሉ" + "ወደ መነሻ ማያ ገጽ ያክሉ" + "ንጥልን ወደዚህ ውሰድ" + "ወደ መነሻ ማያ ገጽ ንጥል ታክሏል" + "ንጥል ነገር ተንቀሳቅሷል" + "ንጥልን አንቀሳቅስ" + "ወደ ረድፍ %1$s ዓምድ %2$s አንቀሳቅስ" + "ወደ አቀማመጥ %1$s አንቀሳቅስ" + "ወደ ተወዳጆች አቀማመጥ %1$s አንቀሳቅስ" + "ንጥል ተንቀሳቅሷል" + "ወደ አቃፊ አክል፦ %1$s" + "ወደ አቃፊ አክል ከ%1$s" + "ንጥል ወደ አቃፊ ታክሏል" + "አቃፊ ፍጠር ከዚህ ጋር፦ %1$s" + "አቃፊ ተፈጥሮዋል" + "ወደ መነሻ ማያ ገጽ አንቀሳቅስ" + "ማያ ገጽን ወደ ግራ አንቀሳቅስ" + "ማያ ገጽን ወደ ቀኝ አንቀሳቅስ" + "ማያ ገጽ ተንቀሳቅሷል" + "መጠን ቀይር" + "ስፋት ጨምር" + "ቁመት ጨምር" + "ስፋት ይቀንሱ" + "ቁመት ይቀንሱ" + "የመግብር መጠን ወደ ስፋት %1$s ቁመት %2$s ተለውጧል" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index eee003df6..84f55686b 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "العمل" "لم يتم تثبيت التطبيق." "التطبيق ليس متاحًا" "تم تعطيل التطبيق الذي تم تنزيله في الوضع الآمن" @@ -28,6 +29,9 @@ "عرض الذاكرة" "المس مع الاستمرار لاختيار إحدى الأدوات." "%1$d × %2$d" + "البحث في التطبيقات" + "جارٍ تحميل التطبيقات…" + "لم يتم العثور على أية تطبيقات تتطابق مع \"%1$s\"" "ليس هناك مساحة أخرى في هذه الشاشة الرئيسية." "لا يوجد المزيد من الحقول في علبة المفضلة" "التطبيقات" @@ -69,5 +73,28 @@ "بحث" "لم يتم تثبيت هذا التطبيق" "لم يتم تثبيت تطبيق لهذا الرمز. يمكنك إزالته أو البحث عن التطبيق وتثبيته يدويًا." - "إضافة إلى مساحة العمل" + "إضافة إلى الشاشة الرئيسية" + "نقل العنصر إلى هنا" + "تمت إضافة العنصر إلى الشاشة الرئيسية" + "تم حذف العنصر" + "نقل العنصر" + "نقل إلى الصف %1$s العمود %2$s" + "نقل إلى الموضع %1$s" + "نقل إلى الموضع المفضل %1$s" + "تم نقل العنصر" + "إضافة إلى المجلد: %1$s" + "إضافة إلى المجلد الذي به %1$s" + "تمت إضافة العنصر إلى المجلد" + "إنشاء مجلد يتضمن: %1$s" + "تم إنشاء المجلد" + "نقل إلى الشاشة الرئيسية" + "نقل الشاشة إلى اليسار" + "نقل الشاشة إلى اليمين" + "تم نقل الشاشة" + "تغيير حجم" + "زيادة العرض" + "زيادة الارتفاع" + "تقليل العرض" + "تقليل الارتفاع" + "تم تغيير حجم الأداة إلى العرض %1$s والارتفاع %2$s" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index b918603ee..f6ce8481f 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Работа" "Приложението не е инсталирано." "Приложението не е налично" "Изтегленото приложение е деактивирано в безопасния режим" @@ -28,6 +29,9 @@ "Показване на паметта" "Докоснете и задръжте за избор на приспособление." "%1$d × %2$d" + "Търсене в приложенията" + "Приложенията се зареждат…" + "Няма намерени приложения, съответстващи на „%1$s“" "На този начален екран няма повече място." "Няма повече място в областта с любимите" "Приложения" @@ -69,5 +73,28 @@ "Търсене" "Това приложение не е инсталирано" "Приложението за тази икона не е инсталирано. Можете да я премахнете или да потърсите приложението и да го инсталирате ръчно." - "Добавяне към раб. пространство" + "Добавяне към началния екран" + "Преместване на елемента тук" + "Елементът е добавен към началния екран" + "Елементът е премахнат" + "Преместване на елемента" + "Преместване към ред %1$s, колона %2$s" + "Преместване към позиция %1$s" + "Преместване към позиция %1$s в любимите" + "Елементът е преместен" + "Добавяне към папката „%1$s“" + "Добавяне към папката, съдържаща %1$s" + "Елементът е добавен към папката" + "Създаване на папка с елемента „%1$s“" + "Папката е създадена" + "Преместване към началния екран" + "Преместване на екрана наляво" + "Преместване на екрана надясно" + "Екранът е преместен" + "Преоразмеряване" + "Увеличаване на ширината" + "Увеличаване на височината" + "Намаляване на ширината" + "Намаляване на височината" + "Приспособлението е преоразмерено към ширина %1$s и височина %2$s" diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index 8387932c1..8391ea5e5 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "লঞ্চার৩" + "কাজ" "অ্যাপ্লিকেশান ইনস্টল করা নেই৷" "অ্যাপ্লিকেশান অনুপলব্ধ" "ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে" @@ -28,6 +29,9 @@ "মেম দেখান" "একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷" "%1$d × %2$d" + "অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন" + "অ্যাপ্লিকেশানগুলি লোড হচ্ছে..." + "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" "পছন্দসই ট্রে-তে আর কোনো জায়গা নেই" "অ্যাপ্লিকেশানগুলি" @@ -69,5 +73,28 @@ "অনুসন্ধান" "এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই" "এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।" - "ওয়ার্কস্পেস যোগ করুন" + "হোম স্ক্রীনে যোগ করুন" + "এখানে আইটেম সরান" + "হোম স্ক্রীনে আইটেম যোগ করা হয়েছে" + "আইটেম সরানো হয়েছে" + "আইটেম সরান" + "সারি %1$s কলাম %2$s এ সরান" + "অবস্থানে সরান %1$s" + "পছন্দসই অবস্থানে সরান %1$s" + "আইটেম সরানো হয়েছে" + "ফোল্ডারে যোগ করুন: %1$s" + "%1$s সহ ফোল্ডারে যোগ করা হয়েছে" + "আইটেম ফোল্ডারে যোগ করা হয়েছে" + "এর সাথে ফোল্ডার তৈরি করুন: %1$s" + "ফোল্ডার তৈরি করা হয়েছে" + "হোম স্ক্রীনে সরান" + "স্ক্রীন বাম দিকে সরান" + "স্ক্রীন ডান দিকে সরান" + "স্ক্রীন সরানো হয়েছে" + "পুনরায় আকার দিন" + "প্রস্থ বাড়ান" + "উচ্চতা বাড়ান" + "প্রস্থ কমান" + "উচ্চতা কমান" + "উইজেটের আকার প্রস্থ %1$s উচ্চতা %2$s তে পরিবর্তন করা হয়েছে" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 69ace8ec7..70002585a 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Feina" "L\'aplicació no s\'ha instal·lat." "L\'aplicació no està disponible." "L\'aplicació que has baixat està desactivada al mode segur." @@ -28,6 +29,9 @@ "Mostra la memòria" "Mantén premut un widget per triar-lo." "%1$d × %2$d" + "Cerca a les aplicacions" + "S\'estan carregant les aplicacions..." + "No s\'ha trobat cap aplicació que coincideixi amb %1$s" "Ja no queda espai en aquesta pantalla d\'inici." "No hi ha més espai a la safata Preferits." "Aplicacions" @@ -69,5 +73,28 @@ "Cerca" "Aquesta aplicació no està instal·lada" "L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment." - "Afegeix a l\'espai de treball" + "Afegeix a la pantalla d\'inici" + "Mou l\'element aquí" + "S\'ha afegit l\'element a la pantalla d\'inici" + "S\'ha suprimit l\'element" + "Desplaça l\'element" + "Desplaça l\'element a la fila %1$s i la columna %2$s" + "Desplaça l\'element a la posició %1$s" + "Desplaça l\'element a la posició de preferits %1$s" + "Element desplaçat" + "Afegeix l\'element a la carpeta: %1$s" + "Afegeix l\'element a la carpeta amb %1$s" + "Element afegit a la carpeta" + "Crea una carpeta amb: %1$s" + "Carpeta creada" + "Desplaça a la pantalla d\'inici" + "Desplaça pantalla a l\'esquerra" + "Desplaça pantalla a la dreta" + "S\'ha desplaçat la pantalla" + "Canvia la mida" + "Augmenta l\'amplada" + "Augmenta l\'alçada" + "Redueix l\'amplada" + "Redueix l\'alçada" + "S\'ha canviat la mida del widget a l\'amplada %1$s i l\'alçada %2$s" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 7606a4a6b..189d40486 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Práce" "Aplikace není nainstalována." "Aplikace není k dispozici." "Stažená aplikace je v nouzovém režimu zakázána" @@ -28,6 +29,9 @@ "Zobrazit Mem" "Widget vyberete dotykem a podržením." "%1$d × %2$d" + "Hledat aplikace" + "Načítání aplikací…" + "Dotazu „%1$s“ neodpovídají žádné aplikace" "Na této ploše již není místo." "Na panelu Oblíbené položky již není místo." "Aplikace" @@ -69,5 +73,28 @@ "Hledat" "Tato aplikace není nainstalována" "Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně." - "Přidat do pracovního prostoru" + "Přidat na plochu" + "Přesunout položku sem" + "Položka byla přidána na plochu" + "Položka byla odstraněna" + "Přesunout položku" + "Přesunout na řádek %1$s do sloupce %2$s" + "Přesunout na pozici %1$s" + "Přesunout do oblíbených položek na pozici %1$s" + "Položka byla přesunuta" + "Přidat do složky: %1$s" + "Přidat do složky s aplikací %1$s" + "Položka byla přidána do složky" + "Vytvořit složku s položkou %1$s" + "Složka byla vytvořena" + "Přesunout na plochu" + "Přesunout obrazovku doleva" + "Přesunout obrazovku doprava" + "Obrazovka byla přesunuta" + "Změnit velikost" + "Zvýšit šířku" + "Zvýšit výšku" + "Snížit šířku" + "Snížit výšku" + "Velikost widgetu upravena: šířka %1$s, výška %2$s" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index b64d20fc4..c922edefe 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Arbejde" "Appen er ikke installeret." "Appen er ikke tilgængelig" "Downloadet app er deaktiveret i sikker tilstand" @@ -28,6 +29,9 @@ "Vis Mem" "Tryk på en widget, og hold den nede for at vælge." "%1$d × %2$d" + "Søg i Apps" + "Indlæser apps…" + "Der blev ikke fundet nogen apps, som matcher \"%1$s\"" "Der er ikke mere plads på denne startskærm." "Der er ikke mere plads i bakken Foretrukne" "Apps" @@ -69,5 +73,28 @@ "Søg" "Denne app er ikke installeret" "Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt." - "Føj til arbejdsområdet" + "Føj til startskærm" + "Flyt elementet hertil" + "Elementet er føjet til startskærmen" + "Elementet er fjernet" + "Flyt element" + "Flyt til række %1$s, kolonne %2$s" + "Flyt til position %1$s" + "Flyt til foretrukne position %1$s" + "Elementet blev flyttet" + "Føj til mappen: %1$s" + "Føj til mappen, der indeholder %1$s" + "Elementet blev føjet til mappen" + "Opret mappe med: %1$s" + "Mappen blev oprettet" + "Flyt til startskærmen" + "Flyt skærmen til venstre" + "Flyt skærmen til højre" + "Skærmen er flyttet" + "Tilpas størrelse" + "Øg bredden" + "Øg højden" + "Reducer bredden" + "Reducer højden" + "Størrelsen for widgetten er ændret til bredde %1$s og højde %2$s" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index d64ae45d0..9408212a1 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Arbeit" "App ist nicht installiert." "App nicht verfügbar" "Heruntergeladene App im abgesicherten Modus deaktiviert" @@ -28,6 +29,9 @@ "Speicher anzeigen" "Zum Hinzufügen Widget berühren und halten" "%1$d × %2$d" + "In Apps suchen" + "Apps werden geladen..." + "Keine Apps für \"%1$s\" gefunden" "Auf diesem Startbildschirm ist kein Platz mehr vorhanden." "Ablage \"Favoriten\" ist voll." "Apps" @@ -69,5 +73,28 @@ "Suchen" "Diese App ist nicht installiert" "Die App für dieses Symbol ist nicht installiert. Sie können das Symbol entfernen oder nach der App suchen und sie manuell installieren." - "Zum Arbeitsbereich hinzufügen" + "Zum Startbildschirm hinzufügen" + "Element hierhin verschieben" + "Element zum Startbildschirm hinzugefügt" + "Element entfernt" + "Element verschieben" + "In Zeile %1$s, Spalte %2$s verschoben" + "Auf Position %1$s verschoben" + "Auf Favoritenposition %1$s verschoben" + "Element verschoben" + "Zum Ordner \"%1$s\" hinzufügen" + "Zum Ordner hinzufügen, in dem sich %1$s befindet" + "Element zum Ordner hinzugefügt" + "Anhand von %1$s Ordner erstellen" + "Ordner erstellt" + "Auf Startbildschirm verschieben" + "Bildschirm nach links" + "Bildschirm nach rechts" + "Bildschirm verschoben" + "Größe anpassen" + "Breite vergrößern" + "Höhe vergrößern" + "Breite verringern" + "Höhe verringern" + "Größe des Widgets zu Breite %1$s und Höhe %2$s geändert" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 8cd2ac900..2e67562c0 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Εργασία" "Η εφαρμογή δεν έχει εγκατασταθεί." "Η εφαρμογή δεν είναι διαθέσιμη" "Η λήψη εφαρμογών απενεργοποήθηκε στην Ασφαλή λειτουργία" @@ -28,6 +29,9 @@ "Εμφάνιση Mem" "Αγγίξτε παρατεταμένα για να πάρετε ένα γραφ.στοιχ." "%1$d × %2$d" + "Αναζήτηση εφαρμογών" + "Φόρτωση εφαρμογών…" + "Δεν βρέθηκαν εφαρμογές για το ερώτημα \"%1$s\"" "Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη." "Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα" "Εφαρμογές" @@ -69,5 +73,28 @@ "Αναζήτηση" "Αυτή η εφαρμογή δεν είναι εγκατεστημένη" "Η εφαρμογή γι\' αυτό το εικονίδιο δεν είναι εγκατεστημένη. Μπορείτε να το καταργήσετε ή να αναζητήσετε την εφαρμογή και να την εγκαταστήσετε με μη αυτόματο τρόπο." - "Προσθήκη στο χώρο εργασίας" + "Προσθήκη στην αρχική οθόνη" + "Μετακίνηση στοιχείου εδώ" + "Το στοιχείο προστέθηκε στην αρχική οθόνη" + "Το στοιχείο καταργήθηκε" + "Μετακίνηση στοιχείου" + "Μετακίνηση στη σειρά %1$s, στήλη %2$s" + "Μετακίνηση στη θέση %1$s" + "Μετακίνηση στη θέση %1$s στα αγαπημένα" + "Το στοιχείο καταργήθηκε" + "Προσθήκη στο φάκελο: %1$s" + "Προσθήκη στο φάκελο με την εφαρμογή %1$s" + "Το στοιχείο προστέθηκε στο φάκελο" + "Δημιουργία φακέλου με: %1$s" + "Δημιουργήθηκε φάκελος" + "Μετακίνηση Αρχικής οθόνης" + "Μετακίνηση οθόνης αριστερά" + "Μετακίνηση οθόνης δεξιά" + "Η οθόνη μετακινήθηκε" + "Προσαρμογή μεγέθους" + "Αύξηση του πλάτους" + "Αύξηση του ύψους" + "μείωση του πλάτους" + "Μείωση του ύψους" + "Έγινε προσαρμογή του μεγέθους του γραφικού στοιχείου σε %1$s πλάτος και %2$s ύψος" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml new file mode 100644 index 000000000..b44ac8d64 --- /dev/null +++ b/res/values-en-rAU/strings.xml @@ -0,0 +1,100 @@ + + + + + "Launcher3" + + "Work" + "App isn\'t installed." + "App isn\'t available" + "Downloaded app disabled in Safe mode" + "Widgets disabled in Safe mode" + "Show Mem" + "Touch & hold to pick up a widget." + "%1$d × %2$d" + "Search Apps" + "Loading Apps…" + "No Apps found matching \"%1$s\"" + "No more room on this Home screen." + "No more room in the Favourites tray" + "Apps" + "Home" + "Remove" + "Uninstall" + "App info" + "install shortcuts" + "Allows an app to add shortcuts without user intervention." + "read Home settings and shortcuts" + "Allows the app to read the settings and shortcuts in Home." + "write Home settings and shortcuts" + "Allows the app to change the settings and shortcuts in Home." + "Problem loading widget" + "Setup" + "This is a system app and can\'t be uninstalled." + "Unnamed Folder" + "Page %1$d of %2$d" + "Home screen %1$d of %2$d" + "Welcome" + "Copy your app icons" + "Import icons and folders from your old Home screens?" + "COPY ICONS" + "START AFRESH" + "Wallpapers, widgets, & settings" + "Touch & hold background to customise" + "GOT IT" + "Folder opened, %1$d by %2$d" + "Touch to close folder" + "Touch to save rename" + "Folder closed" + "Folder renamed to %1$s" + "Folder: %1$s" + "Widgets" + "Wallpapers" + "Settings" + "Unknown" + "Remove" + "Search" + "This app is not installed" + "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." + "Add to Home screen" + "Move item here" + "Item added to home screen" + "Item removed" + "Move item" + "Move to row %1$s column %2$s" + "Move to position %1$s" + "Move to favourites position %1$s" + "Item moved" + "Add to folder: %1$s" + "Add to folder with %1$s" + "Item added to folder" + "Create folder with: %1$s" + "Folder created" + "Move to Home screen" + "Move screen to left" + "Move screen to right" + "Screen moved" + "Re-size" + "Increase width" + "Increase height" + "Decrease width" + "Decrease height" + "Widget re-sized to width %1$s height %2$s" + diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 30d626d8c..b44ac8d64 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Work" "App isn\'t installed." "App isn\'t available" "Downloaded app disabled in Safe mode" @@ -28,6 +29,9 @@ "Show Mem" "Touch & hold to pick up a widget." "%1$d × %2$d" + "Search Apps" + "Loading Apps…" + "No Apps found matching \"%1$s\"" "No more room on this Home screen." "No more room in the Favourites tray" "Apps" @@ -69,5 +73,28 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." - "Add To Workspace" + "Add to Home screen" + "Move item here" + "Item added to home screen" + "Item removed" + "Move item" + "Move to row %1$s column %2$s" + "Move to position %1$s" + "Move to favourites position %1$s" + "Item moved" + "Add to folder: %1$s" + "Add to folder with %1$s" + "Item added to folder" + "Create folder with: %1$s" + "Folder created" + "Move to Home screen" + "Move screen to left" + "Move screen to right" + "Screen moved" + "Re-size" + "Increase width" + "Increase height" + "Decrease width" + "Decrease height" + "Widget re-sized to width %1$s height %2$s" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 30d626d8c..b44ac8d64 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Work" "App isn\'t installed." "App isn\'t available" "Downloaded app disabled in Safe mode" @@ -28,6 +29,9 @@ "Show Mem" "Touch & hold to pick up a widget." "%1$d × %2$d" + "Search Apps" + "Loading Apps…" + "No Apps found matching \"%1$s\"" "No more room on this Home screen." "No more room in the Favourites tray" "Apps" @@ -69,5 +73,28 @@ "Search" "This app is not installed" "The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually." - "Add To Workspace" + "Add to Home screen" + "Move item here" + "Item added to home screen" + "Item removed" + "Move item" + "Move to row %1$s column %2$s" + "Move to position %1$s" + "Move to favourites position %1$s" + "Item moved" + "Add to folder: %1$s" + "Add to folder with %1$s" + "Item added to folder" + "Create folder with: %1$s" + "Folder created" + "Move to Home screen" + "Move screen to left" + "Move screen to right" + "Screen moved" + "Re-size" + "Increase width" + "Increase height" + "Decrease width" + "Decrease height" + "Widget re-sized to width %1$s height %2$s" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 5e2d87c83..20db97502 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Trabajo" "No se instaló la aplicación." "La aplicación no está disponible." "Aplicación descargada inhabilitada en modo seguro" @@ -28,6 +29,9 @@ "Mostrar memoria" "Mantén presionado el widget que desees elegir." "%1$d × %2$d" + "Buscar aplicaciones" + "Cargando aplicaciones…" + "No hay aplicaciones que coincidan con %1$s." "No hay más espacio en esta pantalla principal." "La bandeja de favoritos está llena." "Aplicaciones" @@ -69,5 +73,28 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente." - "Agregar al espacio de trabajo" + "Agregar a la pantalla principal" + "Mover elemento aquí" + "Se agregó el elemento a la pantalla principal." + "Se eliminó el elemento." + "Mover elemento" + "Mover a fila %1$s, columna %2$s" + "Mover a la posición número %1$s" + "Mover a la posición de favoritos número %1$s" + "Elemento movido" + "Agregar a carpeta: %1$s" + "Agregar a la carpeta que contiene %1$s" + "Elemento agregado a la carpeta" + "Crear carpeta con: %1$s" + "Carpeta creada" + "Mover a la pantalla principal" + "Mover pantalla a la izquierda" + "Mover pantalla a la derecha" + "Se movió la pantalla." + "Ajustar tamaño" + "Aumentar el ancho" + "Aumentar la altura" + "Reducir el ancho" + "Reducir la altura" + "Se cambió la dimensión del widget a %1$s de ancho y %2$s de alto." diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 1bf1d769f..0a82554f2 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Trabajo" "La aplicación no está instalada." "La aplicación no está disponible" "Aplicación descargada inhabilitada en modo seguro" @@ -28,6 +29,9 @@ "Mostrar memoria" "Mantén pulsado el widget que quieras seleccionar." "%1$d × %2$d" + "Busca aplicaciones" + "Cargando aplicaciones…" + "No se han encontrado aplicaciones que contengan \"%1$s\"" "No queda espacio en la pantalla de inicio." "La bandeja de favoritos está completa" "Aplicaciones" @@ -69,5 +73,28 @@ "Buscar" "Esta aplicación no está instalada" "La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente." - "Añadir a espacio de trabajo" + "Añadir a la pantalla de inicio" + "Mover elemento aquí" + "Elemento añadido a la pantalla de inicio" + "Elemento eliminado" + "Mover elemento" + "Mover a la fila %1$s, columna %2$s" + "Mover a la posición número %1$s" + "Mover a la posición número %1$s de favoritos" + "Elemento movido" + "Añadir a carpeta: %1$s" + "Añadir a la carpeta con %1$s" + "Elemento añadido a carpeta" + "Crear carpeta con: %1$s" + "Carpeta creada" + "Mover a la pantalla de inicio" + "Mover pantalla a la izquierda" + "Mover pantalla a la derecha" + "Pantalla movida" + "Modificar tamaño" + "Aumentar ancho" + "Aumentar altura" + "Reducir ancho" + "Reducir altura" + "Se ha modificado el tamaño del widget a %1$s de ancho y %2$s de alto" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index b1c13d76b..cf5d0239c 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Töö" "Rakendus pole installitud." "Rakendus ei ole saadaval" "Allalaetud rakendus on turvarežiimis keelatud" @@ -28,6 +29,9 @@ "Mälu kuvamine" "Vidina valimiseks vajutage ja hoidke seda all." "%1$d × %2$d" + "Otsige rakendustest" + "Rakenduste laadimine ..." + "Päringule „%1$s” ei vastanud ükski rakendus" "Sellel avaekraanil pole enam ruumi." "Salves Lemmikud pole rohkem ruumi" "Rakendused" @@ -69,5 +73,28 @@ "Otsing" "See rakendus ei ole installitud" "Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida." - "Tööruumi lisamine" + "Lisa avaekraanile" + "Teisalda üksus siia" + "Üksus lisati avaekraanile" + "Üksus eemaldati" + "Teisalda üksus" + "Teisaldamine %1$s. rea %2$s. veergu" + "Teisaldamine %1$s. positsioonile" + "Teisaldamine lemmikute %1$s. positsioonile" + "Üksus teisaldati" + "Lisamine kausta: %1$s" + "Lisamine kausta nimega %1$s" + "Üksus lisati kausta" + "Kausta loomine nimega %1$s" + "Kaust on loodud" + "Teisalda avaekraanile" + "Teisalda ekraan vasakule" + "Teisalda ekraan paremale" + "Ekraan teisaldati" + "Muuda suurust" + "Suurenda laiust" + "Suurenda kõrgust" + "Vähenda laiust" + "Vähenda kõrgust" + "Vidina suurust muudeti. Laius: %1$s. Kõrgus: %2$s" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 51ff5083f..390e82a88 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Abiarazlea3" + "Lana" "Aplikazioa instalatu gabe dago." "Ez dago erabilgarri aplikazioa" "Deskargatutako aplikazioa modu seguruan desgaitu da" @@ -28,6 +29,9 @@ "Erakutsi memoria" "Eduki ukituta widgeta aukeratzeko." "%1$d × %2$d" + "Bilatu aplikazioetan" + "Aplikazioak kargatzen…" + "Ez da aurkitu \"%1$s\" bilaketarekin bat datorren aplikaziorik" "Hasierako pantaila honetan ez dago toki gehiago." "Ez dago toki gehiago Gogokoak erretiluan" "Aplikazioak" @@ -69,5 +73,28 @@ "Bilatu" "Aplikazio hau ez dago instalatuta" "Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu." - "Gehitu lan-eremuan" + "Gehitu hasierako pantailan" + "Ekarri elementua hona" + "Gehitu da elementua hasierako pantailan" + "Kendu da elementua" + "Mugitu elementua" + "Eraman %1$s. errenkadara, %2$s. zutabera" + "Eraman %1$s. postura" + "Eraman gogokoen %1$s. postura" + "Elementua mugitu da" + "Gehitu %1$s karpetan" + "Gehitu %1$s duen karpetan" + "Elementua karpetan gehitu da" + "Sortu karpeta %1$s elementuarekin" + "Karpeta sortu da" + "Eraman hasierako pantailara" + "Eraman pantaila ezkerrera" + "Eraman pantaila eskuinera" + "Mugitu da pantaila" + "Aldatu tamaina" + "Handitu zabalera" + "Handitu altuera" + "Txikitu zabalera" + "Txikitu altuera" + "Aldatu da widgetaren tamaina. Zabalera: %1$s. Altuera: %2$s." diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 76ffc9d19..1053fdd01 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "کاری" "برنامه نصب نشده است." "برنامه در دسترس نیست" "برنامه دانلود شده در حالت ایمن غیرفعال شد" @@ -28,6 +29,9 @@ "‏نمایش Mem" "برای انتخاب ابزارک لمس کنید و نگه دارید." "%1$d × %2$d" + "جستجوی برنامه‌ها" + "در حال بارگیری برنامه‌ها..." + "هیچ برنامه‌ای مطابق با «%1$s» پیدا نشد" "فضای بیشتری در این صفحه اصلی موجود نیست." "فضای بیشتری در سینی موارد دلخواه وجود ندارد" "برنامه‌ها" @@ -69,5 +73,28 @@ "جستجو" "این برنامه نصب نشده است." "برنامه برای این نماد نصب نشده است. می‌توانید آن را حذف کنید یا سعی کنید برنامه را جستجو کنید و آن را به صورت دستی نصب کنید." - "افزودن به فضای کاری" + "افزودن به صفحه اصلی" + "انتقال مورد به اینجا" + "مورد به صفحه اصلی اضافه شد" + "مورد حذف شد" + "انتقال مورد" + "انتقال به سطر %1$s ستون %2$s" + "انتقال به موقعیت %1$s" + "انتقال به موقعیت دلخواه %1$s" + "مورد منتقل شد" + "افزودن به پوشه: %1$s" + "افزودن به پوشه حاوی %1$s" + "مورد به پوشه اضافه شد" + "ایجاد پوشه با: %1$s" + "پوشه ایجاد شد" + "انتقال به صفحه اصلی" + "انتقال صفحه به چپ" + "انتقال صفحه به راست" + "صفحه منتقل شد" + "تغییر اندازه" + "افزایش عرض" + "افزایش ارتفاع" + "کاهش عرض" + "کاهش ارتفاع" + "اندازه ابزارک به عرض %1$s ارتفاع %2$s تغییر کرد" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 44630c8ab..54f793325 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Työ" "Sovellusta ei ole asennettu." "Sovellus ei ole käytettävissä" "Ladattu sovellus poistettiin käytöstä suojatussa tilassa" @@ -28,6 +29,9 @@ "Näytä muisti" "Valitse widget painamalla sitä pitkään." "%1$d × %2$d" + "Sovellushaku" + "Ladataan sovelluksia…" + "”%1$s” ei palauttanut sovelluksia." "Tässä aloitusruudussa ei ole enää tilaa." "Suosikit-valikossa ei ole enää tilaa" "Sovellukset" @@ -69,5 +73,28 @@ "Haku" "Sovellusta ei ole asennettu" "Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti." - "Lisää työtilaan" + "Lisää aloitusnäytölle" + "Siirrä kohde tänne" + "Kohde lisättiin aloitusnäytölle." + "Kohde poistettiin." + "Siirrä kohde" + "Siirrä rivin %1$s sarakkeeseen %2$s." + "Siirrä kohtaan %1$s." + "Siirrä suosikkien kohtaan %1$s." + "Kohde on siirretty." + "Lisää kansioon %1$s." + "Lisää samaan kansioon sovelluksen %1$s kanssa." + "Kohde on lisätty kansioon." + "Luo kansio, jossa on %1$s." + "Kansio on luotu." + "Siirrä aloitusnäytölle" + "Siirrä näyttöä vasemmalle" + "Siirrä näyttöä oikealle" + "Näyttö siirrettiin." + "Muuta kokoa" + "Lisää leveyttä" + "Lisää korkeutta" + "Vähennä leveyttä" + "Vähennä korkeutta" + "Widgetin kokoa muutettiin. Sen leveys on nyt %1$s ja korkeus %2$s." diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index f11b34da7..cf42badc6 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Lanceur3" + "Travail" "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." @@ -28,6 +29,9 @@ "Afficher la mémoire" "Maintenez un doigt sur le widget pour l\'ajouter." "%1$d × %2$d" + "Rechercher des applications" + "Chargement des applications en cours..." + "Aucune application trouvée correspondant à « %1$s »" "Pas d\'espace libre sur l\'écran d\'accueil." "Il n\'y a plus d\'espace dans la zone des favoris" "Applications" @@ -69,5 +73,28 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement." - "Ajouter à l\'espace de travail" + "Ajouter à l\'écran d\'accueil" + "Déplacer l\'élément ici" + "Élément ajouté à l\'écran d\'accueil" + "Élément supprimé" + "Déplacer l\'élément" + "Déplacer vers rangée %1$s colonne %2$s" + "Déplacer vers la position %1$s" + "Déplacer vers la position %1$s dans les favoris" + "Élément déplacé" + "Ajouter au dossier : %1$s" + "Ajouter au dossier contenant %1$s" + "Élément ajouté au dossier" + "Créer un dossier avec : %1$s" + "Dossier créé" + "Déplacer sur l\'écran d\'accueil" + "Déplacer l\'écran à gauche" + "Déplacer l\'écran à droite" + "Écran déplacé" + "Redimensionner" + "Augmenter la largeur" + "Augmenter la hauteur" + "Diminuer la largeur" + "Diminuer la hauteur" + "Le widget a été redimensionné (largeur : %1$s, hauteur : %2$s)" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 692c2ba29..27ca28b51 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Android Work" "L\'application n\'est pas installée." "Application indisponible" "L\'application téléchargée est désactivée en mode sécurisé." @@ -28,6 +29,9 @@ "Afficher la mémoire" "App. de manière prolongée pour sélectionner widget." "%1$d x %2$d" + "Rechercher dans les applications" + "Chargement des applications en cours…" + "Aucune application ne correspond à la requête \"%1$s\"." "Pas d\'espace libre sur cet écran d\'accueil." "Plus d\'espace disponible dans la zone de favoris." "Applications" @@ -69,5 +73,28 @@ "Rechercher" "Cette application n\'est pas installée" "L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement." - "Ajouter à l\'espace de travail" + "Ajouter à l\'écran d\'accueil" + "Déplacer l\'élément ici" + "L\'élément a bien été ajouté à l\'écran d\'accueil." + "L\'élément a bien été supprimé." + "Déplacer l\'élément" + "Déplacer vers la ligne %1$s, colonne %2$s" + "Déplacer vers la position %1$s" + "Déplacer vers la position %1$s dans les favoris" + "Élément déplacé" + "Ajouter au dossier \"%1$s\"" + "Ajouter au dossier contenant \"%1$s\"" + "Élément ajouté au dossier" + "Créer un dossier avec \"%1$s\"" + "Dossier créé" + "Déplacer vers l\'écran d\'accueil" + "Déplacer l\'écran vers la gauche" + "Déplacer l\'écran vers la droite" + "L\'écran a bien été déplacé." + "Redimensionner" + "Augmenter la largeur" + "Augmenter la hauteur" + "Diminuer la largeur" + "Diminuer la hauteur" + "Le widget a bien été redimensionné (largeur : %1$s, hauteur : %2$s)." diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index 0684bdc22..a9aa9875e 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Traballo" "A aplicación non está instalada" "A aplicación non está dispoñible" "A aplicación que descargaches está desactivada no modo seguro" @@ -28,6 +29,9 @@ "Mostrar memoria" "Mantén premido un widget para seleccionalo." "%1$d × %2$d" + "Aplicacións de busca" + "Cargando aplicacións..." + "Non se atoparon aplicacións que coincidan con \"%1$s\"" "Non hai máis espazo nesta pantalla de inicio." "Non hai máis espazo na bandexa de favoritos" "Aplicacións" @@ -69,5 +73,28 @@ "Buscar" "Esta aplicación non está instalada" "A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente." - "Engadir a espazo de traballo" + "Engadir á pantalla de inicio" + "Mover elemento aquí" + "Engadiuse o elemento á pantalla de inicio" + "Eliminouse o elemento" + "Mover elemento" + "Mover á fila %1$s columna %2$s" + "Mover á posición %1$s" + "Mover á posición dos favoritos %1$s" + "Moveuse o elemento" + "Engadir ao cartafol: %1$s" + "Engadir ao cartafol con %1$s" + "Engadiuse o elemento ao cartafol" + "Crear cartafol con: %1$s" + "Creouse o cartafol" + "Mover á pantalla de inicio" + "Mover pantalla á esquerda" + "Mover pantalla á dereita" + "Moveuse a pantalla" + "Cambiar tamaño" + "Aumentar ancho" + "Aumentar altura" + "Reducir ancho" + "Reducir altura" + "Cambiouse o tamaño do widget polo ancho %1$s e a altura %2$s" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index c36cc7462..82484e468 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "कार्यस्‍थल" "एप्‍लिकेशन इंस्‍टॉल नहीं है." "ऐप्स उपलब्ध नहीं है" "डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है" @@ -28,6 +29,9 @@ "मेमोरी दिखाएं" "विजेट को चुनने के लिए स्‍पर्श करके रखें." "%1$d × %2$d" + "ऐप्‍स खोजें" + "ऐप्स लोड हो रहे हैं..." + "\"%1$s\" से मिलान करने वाला कोई ऐप नहीं मिला" "इस होम स्‍क्रीन पर स्थान शेष नहीं है." "पसंदीदा ट्रे में और स्थान नहीं है" "ऐप्लिकेशन" @@ -69,5 +73,28 @@ "खोजें" "यह ऐप्स इंस्टॉल नहीं है" "इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." - "कार्यस्‍थल में जोड़ें" + "होम स्‍क्रीन में जोड़ें" + "आइटम यहां ले जाएं" + "होम स्क्रीन में आइटम जोड़ा गया" + "आइटम निकाला गया" + "आइटम ले जाएं" + "पंक्ति %1$s स्तंभ %2$s पर ले जाएं" + "%1$s स्थिति पर ले जाएं" + "%1$s की पसंदीदा स्थिति पर ले जाएं" + "आइटम ले जाया गया" + "फ़ोल्डर में जोड़ें: %1$s" + "%1$s के साथ फ़ोल्डर में जोड़ें" + "आइटम फ़ोल्डर में जोड़ा गया" + "इसके साथ फ़ोल्डर बनाएं: %1$s" + "फ़ोल्डर बनाया गया" + "होम स्क्रीन पर ले जाएं" + "स्क्रीन को बाएं ले जाएं" + "स्क्रीन को दाएं ले जाएं" + "स्क्रीन ले जाई गई" + "आकार बदलें" + "चौड़ाई बढ़ाएं" + "ऊंचाई बढ़ाएं" + "चौड़ाई घटाएं" + "ऊंचाई घटाएं" + "विजेट का आकार बदलकर उसकी चौड़ाई %1$s और ऊंचाई %2$s कर दी गई" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 016d7eb2d..64d43bf4e 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Pokretač3" + "Posao" "Aplikacija nije instalirana." "Aplikacija nije dostupna" "Preuzeta aplikacija onemogućena je u Sigurnom načinu rada" @@ -28,6 +29,9 @@ "Prikaži mem" "Dodirnite i držite kako biste podigli widget." "%1$d × %2$d" + "Pretraži aplikacije" + "Učitavanje aplikacija…" + "Nema aplikacija podudarnih s upitom \"%1$s\"" "Na ovom početnom zaslonu više nema mjesta." "Nema više prostora na traci Favoriti" "Aplikacije" @@ -69,5 +73,28 @@ "Traži" "Ta aplikacija nije instalirana" "Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno." - "Dodavanje u radni prostor" + "Dodavanje na početni zaslon" + "Premjesti stavku ovdje" + "Stavka je dodana na početni zaslon" + "Stavka je uklonjena" + "Premještanje stavke" + "Premještanje u redak %1$s, stupac %2$s" + "Premještanje na položaj %1$s" + "Premještanje na položaj favorita %1$s" + "Stavka premještena" + "Dodavanje u mapu: %1$s" + "Dodavanje u mapu s aplikacijom %1$s" + "Stavka dodana u mapu" + "Izrada mape pomoću stavke: %1$s" + "Mapa izrađena" + "Premještanje na početni zaslon" + "Premještanje zaslona ulijevo" + "Premještanje zaslona udesno" + "Zaslon je premješten" + "Promjena veličine" + "Povećanje širine" + "Povećanje visine" + "Smanjenje širine" + "Smanjenje visine" + "Širina widgeta promijenjena je na %1$s, a visina na %2$s" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 9074c176f..53ef28eec 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Munka" "Az alkalmazás nincs telepítve." "Az alkalmazás nem érhető el" "A letöltött alkalmazás Csökkentett módban ki van kapcsolva" @@ -28,6 +29,9 @@ "Mem. megjelenítése" "Modul felvételéhez érintse meg, és tartsa lenyomva" "%1$d × %2$d" + "Alkalmazások keresése" + "Alkalmazások betöltése…" + "Egy alkalmazás sem található a(z) „%1$s” lekérdezésre." "Nincs több hely ezen a kezdőképernyőn." "Nincs több hely a Kedvencek tálcán" "Alkalmazások" @@ -69,5 +73,28 @@ "Keresés" "Az alkalmazás nincs telepítve" "Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt." - "Hozzáadás a munkaterülethez" + "Hozzáadás a kezdőképernyőhöz" + "Elem áthelyezése ide" + "Elem hozzáadva a kezdőképernyőhöz" + "Elem eltávolítva" + "Elem mozgatása" + "Áthelyezés ide: %1$s. sor, %2$s. oszlop" + "Áthelyezés a(z) %1$s. pozícióba" + "Áthelyezés a kedvencek %1$s. pozíciójába" + "Elem áthelyezve" + "Hozzáadás a(z) %1$s mappához" + "Hozzáadás a mappához a következővel: %1$s" + "Elem hozzáadva a mappához" + "Mappa létrehozása a következővel: %1$s" + "Mappa létrehozva" + "Áthelyezés a kezdőképernyőre" + "Képernyő mozgatása balra" + "Képernyő mozgatása jobbra" + "Képernyő áthelyezve" + "Átméretezés" + "Szélesség növelése" + "Magasság növelése" + "Szélesség csökkentése" + "Magasság csökkentése" + "Modul átméretezve %1$s szélességre és %2$s magasságra" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index ea4b2a1ed..3a7ee0e0e 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Աշխատանքային" "Ծրագիրը տեղադրված չէ:" "Հավելվածը հասանելի չէ" "Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում" @@ -28,6 +29,9 @@ "Ցուցադրել մեմը" "Հպեք և պահեք՝ վիջեթն ընտրելու համար:" "%1$d × %2$d" + "Հավելվածների որոնում" + "Հավելվածների բեռնում…" + %1$s» հարցմանը համապատասխանող հավելվածներ չեն գտնվել" "Այլևս տեղ չկա այս հիմնական էկրանին:" "Ընտրյալների ցուցակում այլևս ազատ տեղ չկա" "Ծրագրեր" @@ -44,19 +48,19 @@ "Վիջեթի բեռնման խնդիր կա" "Կարգավորում" "Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:" - "Անանուն թղթապանակ" + "Անանուն պանակ" "Էջ %1$d՝ %2$d-ից" "Հիմնական էկրան %1$d` %2$d-ից" "Բարի գալուստ" "Պատճենել ձեր ծրագրի պատկերակները" - "Ներմուծե՞լ պատկերակները և թղթապանակները ձեր նախկին Հիմնական էկրանից" + "Ներմուծե՞լ պատկերակները և պանակները ձեր նախկին Հիմնական էկրանից" "ՊԱՏՃԵՆԵԼ ՊԱՏԿԵՐԱԿՆԵՐԸ" "ՄԵԿՆԱՐԿԵԼ ԸՍՏ ԿԱՆԽԱԴՐՎԱԾԻ" "Պաստառներ, վիջեթներ և կարգավորումներ" "Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա" "ՀԱՍԿԱՆԱԼԻ Է" "Թղթապանակը բաց է, %1$d-ից %2$d" - "Հպեք՝ թղթապանակը փակելու համար" + "Հպեք՝ պանակը փակելու համար" "Հպեք՝ վերանվանումը պահելու համար" "Թղթապանակը փակ է" "Թղթապանակը վերանվանվեց %1$s" @@ -69,5 +73,28 @@ "Գտնել" "Այս ծրագիրը տեղադրված չէ:" "Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:" - "Ավելացնել աշխատատարածքին" + "Ավելացնել Հիմնական էկրանին" + "Տեղափոխել տարրն այստեղ" + "Տարրն ավելացվեց հիմնական էկրանին" + "Տարրը հեռացվեց" + "Տեղափոխել տարրը" + "Տեղափոխել տող %1$s սյունակ %2$s" + "Տեղափոխել դիրք %1$s" + "Տեղափոխել նախընտրած դիրք՝ %1$s" + "Տարրը տեղափոխվեց" + "Ավելացնել թղթապանակում՝ %1$s" + "Ավելացնել «%1$s» պանակին" + "Տարրն ավելացվեց թղթապանակում" + "Ստեղծել թղթապանակ, օգտագործելով՝ %1$s" + "Թղթապանակը ստեղծվեց" + "Տեղափոխել Հիմնական էկրան" + "Տեղափոխել էկրանը ձախ" + "Տեղափոխել էկրանը աջ" + "Էկրանը տեղափոխվեց" + "Չափափոխել" + "Ավելացնել լայնությունը" + "Ավելացնել բարձրությունը" + "Նվազեցնել լայնությունը" + "Նվազեցնել բարձրությունը" + "Վիջեթի լայնությունը փոխվել է %1$s-ի, իսկ բարձրությունը՝ %2$s-ի" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index edefc4435..d63968987 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Kantor" "Aplikasi tidak dipasang." "Aplikasi tidak tersedia" "Aplikasi yang diunduh dinonaktifkan dalam mode Aman" @@ -28,6 +29,9 @@ "Tampilkan Memori" "Sentuh lama untuk memilih widget." "%1$d × %2$d" + "Telusuri Apps" + "Memuat Aplikasi..." + "Tidak ditemukan Aplikasi yang cocok dengan \"%1$s\"" "Tidak ada ruang lagi pada layar Utama ini." "Tidak ada ruang tersisa di baki Favorit" "Aplikasi" @@ -69,5 +73,28 @@ "Telusuri" "Aplikasi ini belum terpasang" "Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual." - "Tambahkan Ke Ruang Kerja" + "Tambahkan ke layar Utama" + "Pindahkan item ke sini" + "Item ditambahkan ke layar utama" + "Item dihapus" + "Pindahkan item" + "Pindahkan ke baris %1$s kolom %2$s" + "PIndahkan ke posisi %1$s" + "Pindahkan ke posisi favorit %1$s" + "Item dipindahkan" + "Tambahkan ke folder: %1$s" + "Tambahkan ke folder dengan %1$s" + "Item ditambahkan ke folder" + "Buat folder dengan: %1$s" + "Folder dibuat" + "Pindahkan ke layar Utama" + "Pindahkan layar ke kiri" + "Pindahkan layar ke kanan" + "Layar dipindahkan" + "Ubah ukuran" + "Tambahi lebar" + "Tambahi tinggi" + "Kurangi lebar" + "Kurangi tinggi" + "Widget diubah ukurannya menjadi lebar %1$s tinggi %2$s" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index 70f87e46f..61f5e8528 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Vinna" "Forritið er ekki uppsett." "Forritið er ekki í boði" "Sótt forrit er óvirkt í öryggisstillingu" @@ -28,6 +29,9 @@ "Sýna minni" "Haltu fingri á græju til að grípa hana." "%1$d × %2$d" + "Leita í forritum" + "Hleður forrit…" + "Ekki fundust forrit sem samsvara „%1$s“" "Ekki meira pláss á þessum heimaskjá." "Ekki meira pláss í bakka fyrir uppáhald" "Forrit" @@ -69,5 +73,28 @@ "Leita" "Þetta forrit er ekki uppsett" "Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt." - "Bæta við vinnusvæði" + "Bæta á heimaskjá" + "Færa atriði hingað" + "Atriði bætt á heimaskjáinn" + "Atriði fjarlægt" + "Færa atriði" + "Færa í línu %1$s, dálk %2$s" + "Færa í stöðu %1$s" + "Færa í stöðu %1$s á festisvæði" + "Atriði fært" + "Setja í möppu: %1$s" + "Setja í möppu með %1$s" + "Atriði sett í möppu" + "Búa til möppu með: %1$s" + "Mappa búin til" + "Færa á heimaskjá" + "Færa skjá til vinstri" + "Færa skjá til hægri" + "Skjár færður" + "Breyta stærð" + "Auka breidd" + "Auka hæð" + "Minnka breidd" + "Minnka hæð" + "Stærð græju breytt í %1$s á breidd og %2$s á hæð" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 6ad1ed4d2..62a3d3031 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Lavoro" "App non installata." "App non disponibile" "L\'app scaricata è stata disattivata in modalità provvisoria" @@ -28,6 +29,9 @@ "Mostra Mem" "Tocca e tieni premuto per scegliere un widget." "%1$d × %2$d" + "Cerca app" + "Caricamento di app…" + "Nessuna app trovata corrispondente a \"%1$s\"" "Spazio nella schermata Home esaurito." "Spazio esaurito nella barra dei Preferiti" "App" @@ -69,5 +73,28 @@ "Cerca" "L\'app non è installata" "L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente." - "Aggiunta all\'area di lavoro" + "Aggiungi a schermata Home" + "Sposta elemento qui" + "Elemento aggiunto alla schermata Home" + "Elemento rimosso" + "Sposta elemento" + "Sposta a riga %1$s, colonna %2$s" + "Sposta nella posizione %1$s" + "Sposta nella posizione %1$s dei preferiti" + "Elemento spostato" + "Aggiungi alla cartella: %1$s" + "Aggiungi a cartella con %1$s" + "Elemento aggiunto alla cartella" + "Crea cartella con: %1$s" + "Cartella creata" + "Sposta nella schermata Home" + "Sposta schermata a sinistra" + "Sposta schermata a destra" + "Schermata spostata" + "Ridimensiona" + "Aumenta larghezza" + "Aumenta altezza" + "Riduci larghezza" + "Riduci altezza" + "Widget ridimensionato a larghezza %1$s, altezza %2$s" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 3e3fe3391..327081842 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "עבודה" "האפליקציה לא מותקנת." "האפליקציה אינה זמינה" "אפליקציה שהורדת הושבתה במצב בטוח" @@ -28,6 +29,9 @@ "הצג זכרון" "גע נגיעה רציפה בווידג\'ט כדי לבחור בו." "%1$d × %2$d" + "חפש אפליקציות" + "טוען אפליקציות…" + "לא נמצאו אפליקציות התואמות ל-\"%1$s\"" "אין עוד מקום במסך דף הבית הזה." "אין עוד מקום במגש המועדפים" "אפליקציות" @@ -69,5 +73,28 @@ "חפש" "אפליקציה זו אינה מותקנת" "האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית." - "הוסף לסביבת העבודה" + "הוסף למסך דף הבית" + "העבר את הפריט לכאן" + "הפריט הועבר אל מסך דף הבית" + "הפריט הוסר" + "העבר את הפריט" + "העבר אל שורה %1$s עמודה %2$s" + "העבר אל מיקום %1$s" + "העבר אל מיקום %1$s במועדפים" + "הפריט הועבר" + "הוסף לתיקייה: %1$s" + "העבר אל התיקייה עם %1$s" + "הפריט נוסף לתיקייה" + "צור תיקייה עם: %1$s" + "התיקייה נוצרה" + "העבר אל מסך דף הבית" + "הזז את המסך שמאלה" + "הזז את המסך ימינה" + "המסך הועבר" + "שנה גודל" + "הגדל רוחב" + "הגדל גובה" + "הקטן רוחב" + "הקטן גובה" + "גודל הווידג\'ט שונה - רוחב %1$s גובה %2$s" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 1fc9aca00..44f3e185f 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "仕事用" "このアプリはインストールされていません。" "このアプリは使用できません" "ダウンロードしたアプリは、セーフモードでは無効です" @@ -28,6 +29,9 @@ "メモリーを表示" "ウィジェットを追加するには押し続けます。" "%1$dx%2$d" + "アプリを検索" + "アプリを読み込んでいます…" + "「%1$s」に一致するアプリは見つかりませんでした" "このホーム画面に空きスペースがありません。" "お気に入りトレイに空きスペースがありません" "アプリ" @@ -69,5 +73,28 @@ "検索" "このアプリはインストールされていません" "このアイコンのアプリはインストールされていません。このアイコンは削除できます。または、手動でアプリを検索してインストールしください。" - "作業スペースに追加" + "ホーム画面に追加" + "アイテムをここに移動" + "アイテムをホーム画面に追加しました" + "アイテムを削除しました" + "アイテムを移動" + "行%1$s、列%2$sに移動" + "位置%1$sに移動" + "お気に入りの位置%1$sに移動" + "アイテムを移動しました" + "フォルダ「%1$s」に追加" + "%1$sのあるフォルダに追加" + "アイテムをフォルダに追加しました" + "「%1$s」フォルダを作成" + "フォルダを作成しました" + "ホーム画面に移動" + "画面を左に移動" + "画面を右に移動" + "画面を移動しました" + "サイズを変更" + "幅を広くする" + "高さを高くする" + "幅を狭くする" + "高さを低くする" + "ウィジェットのサイズを幅%1$s、高さ%2$sに変更しました" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 4a93b9a16..952209d78 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "სამუშაო" "აპი არ არის დაყენებული." "აპი მიუწვდომელია" "უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია" @@ -28,6 +29,9 @@ "Mem-ის ჩვენება" "შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად." "%1$d × %2$d" + "აპების ძიება" + "აპები იტვირთება..." + "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" "ამ მთავარ ეკრანზე ადგილი აღარ არის." "რჩეულების თაროზე ადგილი არ არის" "აპები" @@ -69,5 +73,28 @@ "ძიება" "ეს აპი დაყენებული არ არის" "ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია." - "სამუშაო სივრცეში დამატება" + "მთავარ ეკრანზე დამატება" + "ერთეულის გადაადგილება აქ" + "ერთეული დაემატა მთავარ ეკრანს" + "ერთეული წაიშალა" + "ერთეულის გადაადგილება" + "გადატანა რიგში %1$s სვეტში %2$s" + "გადატანა %1$s პოზიციაზე" + "გადატანა რჩეულთა პოზიციაზე %1$s" + "ერთეული გადაადგილდა" + "საქაღალდეში დამატება: %1$s" + "საქაღალდეში დამატება %1$s-ით" + "ერთეული დაემატა საქაღალდეს" + "საქაღალდის შექმნა ერთეულით: %1$s" + "საქაღალდე შექმნილია" + "მთავარ ეკრანზე გადატანა" + "ეკრანის გადატანა მარცხნივ" + "ეკრანის გადატანა მარჯვნით" + "ეკრანი გადაადგილდა" + "ზომის შეცვლა" + "სიგანის გაზრდა" + "სიმაღლის გაზრდა" + "სიგანის შემცირება" + "სიმაღლის შემცირება" + "ვიჯეტის ზომები შეიცვალა: სიგანე %1$s სიმაღლე %2$s" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index 40de75307..cc1a38ba9 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Жұмыс" "Қолданба орнатылмаған." "Қолданба қол жетімді емес" "Жүктелген қолданба қауіпсіз режимде өшірілген" @@ -28,6 +29,9 @@ "Жадты көрсету" "Виджетті таңдау үшін түртіп, мықтап ұстаңыз." "%1$d × %2$d" + "Қолданбаларды іздеу" + "Қолданбалар жүктелуде…" + %1$s» сұрауына сәйкес келетін қолданбалар жоқ" "Бұл Негізгі экранда орын қалмады." "Қалаулылар науасында орын қалмады" "Қолданбалар" @@ -69,5 +73,28 @@ "Іздеу" "Бұл қолданба орнатылмаған" "Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады." - "Жұмыс кеңістігіне қосу" + "Негізгі экранға қосу" + "Элементті мұнда жылжыту" + "Элемент негізгі экранға қосылды" + "Элемент жойылды" + "Элементті жылжыту" + "%1$s-жол, %2$s-бағанға жылжыту" + "%1$s-орынға жылжыту" + "%1$s нөмірлі таңдаулы орынға жылжыту" + "Элемент жылжытылды" + "Қалтаға қосу: %1$s" + "%1$s бар қалтаға қосу" + "Элемент қалтаға қосылды" + "Мына бар қалтаны жасау: %1$s" + "Қалта жасалды" + "Негізгі экранға жылжыту" + "Экранды солға жылжыту" + "Экранды оңға жылжыту" + "Экран жылжытылды" + "Өлшемін өзгерту" + "Енін арттыру" + "Биіктігін арттыру" + "Енін азайту" + "Биіктігін азайту" + "Виджет өлшемінің ені %1$s, биіктігі %2$s болып өзгертілді" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 1cd8fe29b..3ebef7bbd 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "ការងារ" "មិន​បាន​ដំឡើង​កម្មវិធី។" "មិន​មាន​កម្មវិធី" "បាន​បិទ​កម្មវិធី​ដែល​បាន​ទាញ​យក​ក្នុង​របៀប​សុវត្ថិភាព" @@ -28,6 +29,9 @@ "បង្ហាញ​ Mem" "ប៉ះ & សង្កត់ ដើម្បី​ជ្រើស​ធាតុ​ក្រាហ្វិក។" "%1$d × %2$d" + "ស្វែងរកកម្មវិធី" + "កំពុងដំណើរការកម្មវិធី..." + "គ្មានកម្មវិធីដែលត្រូវជាមួយ \"%1$s\" ទេ" "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" "គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ" "កម្មវិធី" @@ -69,5 +73,28 @@ "ស្វែងរក" "មិន​បាន​ដំឡើង​កម្មវិធី​នេះ" "មិន​បាន​ដំឡើង​កម្មវិធី​សម្រាប់​រូបតំណាង​នេះ។ អ្នក​អាច​លុប​វា ឬ​ស្វែងរក​កម្មវិធី និង​ដំឡើង​វា​ដោយ​ដៃ។" - "បន្ថែមទៅចន្លោះបណ្តោះអាសន្ន" + "បន្ថែមទៅអេក្រង់ដើម" + "ផ្លាស់ធាតុមកទីនេះ" + "ធាតុដែលត្រូវបានបន្ថែមទៅអេក្រង់ដើម" + "ធាតុដែលបានដកចេញ" + "ផ្លាស់ទីធាតុ" + "ផ្លាស់ទីទៅជួរដេកទី %1$s ជួរឈរទី %2$s" + "ផ្លាស់ទីទៅទីតាំង %1$s" + "ផ្លាស់ទីទៅការចូលចិត្តទីតាំងទី %1$s" + "បានផ្លាស់ទីធាតុ" + "បន្ថែមទៅថតឯកសារ៖ %1$s" + "បន្ថែមទៅថតឯកសារដែលមានឈ្មោះ %1$s" + "បានបន្ថែមធាតុទៅថតឯកសារ" + "បង្កើតថតឯកសារជាមួយ៖ %1$s" + "បានបង្កើតថតឯកសារ" + "ផ្លាស់ទៅអេក្រង់ដើម" + "រំកិលអេក្រង់ទៅខាងឆ្វេង" + "រំកិលអេក្រង់ទៅខាងស្តាំ" + "អេក្រង់ដែលបានផ្លាស់ទី" + "ប្ដូរទំហំ" + "បង្កើនទទឹង" + "បង្កើនកម្ពស់" + "បន្ថយទទឹង" + "បន្ថយកម្ពស់" + "ធាតុក្រាហ្វិកដែលបានប្តូរទំហំទៅទទឹងប្រវែង %1$s កម្ពស់ប្រវែង %2$s" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index a823528df..438a37750 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "ಲಾಂಚರ್3" + "ಕೆಲಸ" "ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ" "ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" @@ -28,6 +29,9 @@ "ಸ್ಮರಣೆ ತೋರಿಸು" "ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ & ಹಿಡಿದುಕೊಳ್ಳಿ." "%1$d × %2$d" + "ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ" + "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..." + "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." "ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" @@ -69,5 +73,28 @@ "ಹುಡುಕು" "ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ" "ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು." - "ಕಾರ್ಯಕ್ಷೇತ್ರಕ್ಕೆ ಸೇರಿಸಿ" + "ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು" + "ಐಟಂ ಇಲ್ಲಿಗೆ ಸರಿಸಿ" + "ಮುಖಪುಟ ಪರದೆಗೆ ಐಟಂ ಸೇರಿಸಲಾಗಿದೆ" + "ಐಟಂ ತೆಗೆದುಹಾಕಲಾಗಿದೆ" + "ಐಟಂ ಸರಿಸಿ" + "%1$s ಸಾಲು %2$s ಕಾಲಮ್‌ಗೆ ಸರಿಸಿ" + "%1$s ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ" + "ಮೆಚ್ಚಿನ %1$s ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ" + "ಐಟಂ ಸರಿಸಲಾಗಿದೆ" + "ಫೋಲ್ಡರ್‌ಗೆ ಸೇರಿಸಿ: %1$s" + "%1$s ಮೂಲಕ ಫೋಲ್ಡರ್‌ಗೆ ಸೇರಿಸಿ" + "ಐಟಂ ಅನ್ನು ಫೋಲ್ಡರ್‌ಗೆ ಸೇರಿಸಲಾಗಿದೆ" + "ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಫೋಲ್ಡರ್ ರಚಿಸಿ: %1$s" + "ಫೋಲ್ಡರ್ ರಚಿಸಲಾಗಿದೆ" + "ಮುಖಪುಟಕ್ಕೆ ಸರಿಸಿ" + "ಪರದೆಯನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ" + "ಪರದೆಯನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ" + "ಪರದೆ ಸರಿಸಲಾಗಿದೆ" + "ಮರುಗಾತ್ರ" + "ಅಗಲವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ" + "ಎತ್ತರವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ" + "ಅಗಲವನ್ನು ಕಡಿಮೆ ಮಾಡಿ" + "ಎತ್ತರವನ್ನು ಕಡಿಮೆ ಮಾಡಿ" + "ವಿಜೆಟ್ ಅನ್ನು %1$s ಅಗಲ %2$s ಎತ್ತರಕ್ಕೆ ಮರುಗಾತ್ರಗೊಳಿಸಲಾಗಿದೆ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 628900aba..d7502e5a6 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "업무" "앱이 설치되지 않았습니다." "앱을 사용할 수 없음" "다운로드한 앱은 안전 모드에서 사용할 수 없습니다." @@ -28,6 +29,9 @@ "메모리 표시" "위젯을 선택하려면 길게 터치하세요." "%1$d×%2$d" + "앱 검색" + "앱 로드 중..." + "\'%1$s\'와(과) 일치하는 앱이 없습니다." "홈 화면에 더 이상 공간이 없습니다." "즐겨찾기 트레이에 더 이상 공간이 없습니다." "앱" @@ -69,5 +73,28 @@ "검색" "이 앱이 설치되어 있지 않음" "이 아이콘의 앱이 설치되어 있지 않습니다. 아이콘을 삭제하거나 앱을 검색하여 수동으로 설치하세요." - "작업공간에 추가" + "메인 스크린에 추가" + "여기에 항목을 이동" + "메인 스크린에 항목 추가됨" + "항목 삭제됨" + "항목 이동" + "%1$s%2$s열로 이동" + "%1$s번 위치로 이동" + "즐겨찾는 %1$s번 위치로 이동" + "항목을 이동했습니다." + "폴더에 추가: %1$s" + "%1$s이(가) 포함된 폴더에 추가" + "항목이 폴더에 추가되었습니다." + "다음이 포함된 폴더 만들기: %1$s" + "폴더를 만들었습니다." + "메인 스크린으로 이동" + "화면을 왼쪽으로 이동" + "화면을 오른쪽으로 이동" + "화면 이동됨" + "크기 조정" + "폭 늘리기" + "높이 늘리기" + "폭 줄이기" + "높이 줄이기" + "폭 %1$s, 높이 %2$s로 위젯 크기 조정됨" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index 865db3b3a..119c3028b 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Жумуш" "Колдонмо орнотулган эмес." "Колдонмо жеткиликтүү эмес" "Жүктөп алынган колдонмо Коопсуз режиминде иштен чыгарылды" @@ -28,6 +29,9 @@ "Мемди көргөзүү" "Виджетти тандаш үчүн, басып туруңуз" "%1$d × %2$d" + "Колдонмолорду издөө" + "Колдонмолор жүктөлүүдө…" + "\"%1$s\" дал келген колдонмолор табылган жок" "Бул Үй экранында бош орун жок." "Тандамалдар тайпасында орун калган жок" "Колдонмолор" @@ -69,5 +73,28 @@ "Издөө" "Бул колдонмо орнотулган эмес" "Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот." - "Жумуш ордуна кошуу" + "Башкы экранга кошуу" + "Бул нерсени бул жерге жылдыруу" + "Башкы экранга кошулду" + "Жоюлду" + "Муну жылдыруу" + "%1$s катарга %2$s тилкеге жылдыруу" + "%1$s орунга жылдыруу" + "Сүйүктүүлөргө %1$s жылдыруу" + "Нерсе жылдырылды" + "Куржунга кошуу: %1$s" + "%1$s куржунуна кошуу" + "Нерсе куржунга кошулду" + "Төмөнкү менен куржун түзүү: %1$s" + "Куржун түзүлдү" + "Башкы экранга жылдыруу" + "Экранды солго жылдыруу" + "Экранды оңго жылдыруу" + "Экран жылдырылды" + "Өлчөмүн өзгөртүү" + "Кеңейтүү" + "Бийиктетүү" + "Ичкертүү" + "Жапыздатуу" + "Виджеттин кеңдиги %1$s бийиктиги %2$s болду" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index b3c467693..729ced705 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "ວຽກ" "ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ." "ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້" "ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode" @@ -28,6 +29,9 @@ "ສະແດງຄວາມຈຳ" "ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ." "%1$d × %2$d" + "ຊອກຫາແອັບ" + "​ກຳ​ລັງ​ໂຫລດ​ແອັບ..." + "ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"%1$s\"" "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." "ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ" "ແອັບຯ" @@ -69,5 +73,28 @@ "ຊອກຫາ" "ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ" "​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ." - "ເພີ່ມ​ໃສ່​ບ່ອນ​ເຮັດ​ວຽກ" + "ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ" + "Move item here" + "ເພີ່ມ​ລາຍ​ການ​ໃສ່​ໜ້າ​ຈໍ​ຫຼັກ​ແລ້ວ" + "ເອົາ​ລາຍ​ການ​ອອກ​ໄປ​ແລ້ວ" + "ຍ້າຍ​ລາຍ​ການ" + "ຍ້າຍ​ໄປ​ໃສ່​ແຖວ %1$s ຖັນ %2$s" + "ຍ້າຍ​ໄປ​ໃສ່​ຕຳ​ແໜ່ງ %1$s" + "ຍ້າຍ​ໄປ​ໃສ່​ຕຳ​ແໜ່ງ​ທີ່​ມັກ %1$s" + "ຍ້າຍ​ລາຍ​ການ​ແລ້ວ" + "ເພີ່ມ​ໃສ່​ໂຟ​ລ​ເດີ: %1$s" + "ເພີ່ມ​ໃສ່​ໂຟ​ລ​ເດີ​ດ້ວຍ %1$s" + "ເພີ່ມ​ລາຍ​ການ​ໃສ່​ໂຟ​ລ​ເດີ​ແລ້ວ" + "ສ້າງ​ໂຟ​ລ​ເດີ​ກັບ: %1$s" + "ສ້າງ​ໂຟ​ລ​ເດີ​ແລ້ວ" + "ຍ້າຍ​ໄປ​ໃສ່​ໜ້າ​ຈໍ​ຫຼັກ" + "ຍ້າຍ​ໜ້າ​ຈໍ​ໄປ​ທາງ​ຊ້າຍ" + "ຍ້າຍ​ໜ້າ​ຈໍ​ໄປ​ທາງ​ຂວາ" + "ຍ້າຍ​ໜ້າ​ຈໍ​ແລ້ວ" + "ປັບຂະໜາດ" + "ເພີ່ມ​ລວງ​ກ້​ວາງ​ຂຶ້ນ" + "ເພີ່ມ​ລວງ​ສູງ​ຂຶ້ນ" + "ຫຼຸດ​ລວງ​ກ້​ວາງ​ລົງ" + "ຫຼຸດ​ລວງ​ສູງ​ລົງ" + "ປ່ຽນ​ຂະ​ໜາດ​ວິດ​ເຈັດ​ເປັນ​ລວງ​ກ້​ວາງ %1$s ລວງ​ສູງ %2$s ແລ້ວ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index a6d5edc6b..d104f188d 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Darbas" "Programa neįdiegta." "Programa nepasiekiama" "Atsisiųsta programa išjungta Saugos režimu" @@ -28,6 +29,9 @@ "Rodyti atmintinę" "Palieskite ir laikykite, kad pasirinkt. valdiklį." "%1$d × %2$d" + "Ieškoti programų" + "Įkeliamos programos..." + "Nerasta jokių užklausą „%1$s“ atitinkančių programų" "Šiame pagrindiniame ekrane vietos nebėra." "Mėgstamiausių dėkle nebėra vietos" "Programos" @@ -69,5 +73,28 @@ "Ieškoti" "Ši programa neįdiegta" "Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys." - "Pridėti prie darbo srities" + "Pridėti prie pagrind. ekrano" + "Perkelti elementą čia" + "Elementas pridėtas prie pagrindinio ekrano" + "Elementas perkeltas" + "Perkelti elementą" + "Perkelti į %1$s eilutę, %2$s stulpelį" + "Perkelti į %1$s poziciją" + "Perkelti į %1$s mėgstamiausių poziciją" + "Elementas perkeltas" + "Pridėti prie aplanko: „%1$s“" + "Pridėti prie aplanko su „%1$s“" + "Elementas pridėtas prie aplanko" + "Kurti aplanką naudojant: „%1$s“" + "Aplankas sukurtas" + "Perkelti į pagrindinį ekraną" + "Perkelti ekraną į kairę" + "Perkelti ekraną į dešinę" + "Ekranas perkeltas" + "Pakeisti dydį" + "Padidinti plotį" + "Padidinti aukštį" + "Sumažinti plotį" + "Sumažinti aukštį" + "Valdiklio dydis pakeistas: plotis – %1$s, aukštis – %2$s" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index fdbee78ed..95c030deb 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Darbs" "Lietotne nav instalēta." "Lietotne nav pieejama." "Lejupielādētā lietotne ir atspējota drošajā režīmā." @@ -28,6 +29,9 @@ "Rādīt atmiņu" "Lai izvēlētos logrīku, pieskarieties un turiet to." "%1$d × %2$d" + "Meklēt lietotnes" + "Notiek lietotņu ielāde…" + "Vaicājumam “%1$s” neatbilda neviena lietotne." "Šajā sākuma ekrānā vairs nav vietas." "Izlases joslā vairs nav vietas." "Lietotnes" @@ -69,5 +73,28 @@ "Meklēt" "Šī lietotne nav instalēta" "Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli." - "Pievienot darbvietai" + "Pievienot sākuma ekrānam" + "Pārvietot vienumu šeit" + "Vienums pievienots sākuma ekrānam" + "Vienums noņemts" + "Pārvietot vienumu" + "Pārvietot uz %1$s. rindu, %2$s. kolonnu" + "Pārvietot uz %1$s. pozīciju" + "Pārvietot uz %1$s. izlases pozīciju" + "Vienums pārvietots" + "Pievienot mapei: %1$s" + "Pievienot mapei, kurā ir %1$s" + "Vienums pievienots mapei" + "Izveidot mapi ar: %1$s" + "Mape izveidota" + "Pārvietot uz sākuma ekrānu" + "Pārvietot ekrānu pa kreisi" + "Pārvietot ekrānu pa labi" + "Ekrāns pārvietots" + "Mainīt lielumu" + "Palielināt platumu" + "Palielināt augstumu" + "Samazināt platumu" + "Samazināt augstumu" + "Logrīka lielums mainīts — platums: %1$s, augstums: %2$s." diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index f6df54dd3..8e25977e7 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Работа" "Апликацијата не е инсталирана." "Апликацијата не е достапна" "Преземената апликација е оневозможена во безбеден режим" @@ -28,6 +29,9 @@ "Прикажи „Мени“" "Допри и задржи за да се избере виџетот." "%1$d × %2$d" + "Пребарување апликации" + "Се вчитуваат апликации…" + "Не се најдени апликации што одговараат на „%1$s“" "Нема повеќе простор на овој екран на почетната страница." "Нема повеќе простор на лентата „Омилени“" "Апликации" @@ -69,5 +73,28 @@ "Барај" "Апликацијата не е инсталирана" "Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно." - "Додај на работна површина" + "Додај на Почетен екран" + "Премести ја ставката овде" + "Ставката е додадена на почетниот екран" + "Ставката е отстранета" + "Премести ја ставката" + "Премести во ред %1$s колона %2$s" + "Премести на место %1$s" + "Премести на место %1$s во омилени" + "Ставката е преместена" + "Додај во папката: %1$s" + "Додај во папка со %1$s" + "Ставката е додадена во папката" + "Создај папка со: %1$s" + "Папката е создадена" + "Премести на Почетен екран" + "Движи го екранот налево" + "Движи го екранот надесно" + "Екранот е преместен" + "Промени големина" + "Зголеми ширина" + "Зголеми висина" + "Намали ширина" + "Намали висина" + "Големината на виџетот е променета на ширина %1$s висина %2$s" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index de37f47c4..e45ebe1ad 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "ഔദ്യോഗികം" "അപ്ലിക്കേഷൻ ഇൻസ്‌റ്റാളുചെ‌യ്‌തിട്ടില്ല." "അപ്ലിക്കേഷൻ ലഭ്യമല്ല" "ഡൗൺലോഡുചെയ്‌ത അപ്ലിക്കേഷൻ സുരക്ഷാ മോഡിൽ പ്രവർത്തനരഹിതമാക്കി" @@ -28,6 +29,9 @@ "മെമ്മറി കാണിക്കുക" "ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക." "%1$d × %2$d" + "ആപ്പ്‌സ് തിരയുക" + "ആപ്പ്‌സ് ലോഡുചെയ്യുന്നു..." + "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല" "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." "പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല" "അപ്ലിക്കേഷനുകൾ" @@ -69,5 +73,28 @@ "തിരയുക" "ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല" "ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക." - "വർക്ക്‌സ്‌പെയ്‌സിൽ ചേർക്കുക" + "ഹോം സ്ക്രീനിൽ ചേർക്കുക" + "ഇനം ഇവിടേക്ക് നീക്കുക" + "ഹോം സ്‌ക്രീനിൽ ഇനം ചേർത്തു" + "ഇനം നീക്കംചെയ്‌തു" + "ഇനം നീക്കുക" + "വരി %1$s നിര %2$s-ലേക്ക് നീക്കുക" + "%1$s-ലേക്ക് നീക്കുക" + "ഇഷ്‌ടമുള്ള %1$s സ്ഥാനത്തേക്ക് നീക്കുക" + "ഇനം നീക്കി" + "ഫോൾഡറിൽ ചേർക്കുക: %1$s" + "%1$s ഉള്ള ഫോൾഡറിൽ ചേർക്കുക" + "ഫോൾഡറിൽ ഇനം ചേർത്തു" + "ഇതുപയോഗിച്ച് ഫോൾഡർ സൃഷ്‌ടിക്കുക: %1$s" + "ഫോൾഡർ സൃഷ്‌ടിച്ചു" + "ഹോം സ്‌ക്രീനിലേക്ക് നീക്കുക" + "സ്‌ക്രീൻ ഇടത്തേക്ക് നീക്കുക" + "സ്‌ക്രീൻ വലത്തേക്ക് നീക്കുക" + "സ്‌ക്രീൻ നീക്കി" + "വലുപ്പംമാറ്റുക" + "വീതി കൂട്ടുക" + "ഉയരം കൂട്ടുക" + "വീതി കുറയ്‌ക്കുക" + "ഉയരം കുറയ്‌ക്കുക" + "വീതി %1$s ഉയരം %2$s-ലേക്ക് വിഡ്‌ജെറ്റിന്റെ വലുപ്പം മാറ്റി" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index 27b166d3b..f4c757c33 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Ажил" "Апп суугаагүй байна." "Апп-г ашиглах боломжгүй" "Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн" @@ -28,6 +29,9 @@ "Мем харуулах" "Виджетийг авах бол хүрээд барина уу." "%1$d × %2$d" + "Апп хайх" + "Аппликейшныг ачаалж байна..." + "\"%1$s\"-тай нийцэх апп олдсонгүй" "Энэ Нүүр дэлгэц зайгүй." "\"Дуртай\" трей дээр өөр зай байхгүй байна" "Апп" @@ -69,5 +73,28 @@ "Хайх" "Энэ апп-г суулгаагүй байна" "Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой." - "Ажлын талбар нэмэх" + "Нүүр дэлгэц нэмэх" + "Энд байршуулах" + "Нүүр дэлгэцэнд нэмсэн зүйл" + "Арилгасан зүйл" + "Зөөх" + "%1$s мөр %2$s баганад зөөх" + "Байршил %1$s-д зөөх" + "Дуртай байршил болох %1$s-д зөөх" + "Зөөвөрлөсөн зүйл" + "Хавтас: %1$s руу нэм" + "%1$s-тай хавтас нэмэх" + "Хавтсанд нэмэгдсэн зүйл" + "Хавтсыг: %1$s нэрээр үүсгэ" + "Үүсгэсэн хавтас" + "Нүүр дэлгэц рүү зөөх" + "Дэлгэцийг зүүн тийш зөөх" + "Дэлгэцийг баруун тийш зөөх" + "Дэлгэцийг зөөсөн" + "Хэмжээг өөрчлөх" + "Өргөсгөх" + "Өндөрсгөх" + "Нарийсгах" + "Намсгах" + "Виджэтийн өргөн %1$s, өндөр %2$s болсон" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index ad75e8842..6c43755ad 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "कार्य" "अॅप स्थापित केलेला नाही." "अॅप उपलब्ध नाही" "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" @@ -28,6 +29,9 @@ "Mem दर्शवा" "विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." "%1$d × %2$d" + "अॅप्स शोधा" + "अॅप्स लोड करीत आहे..." + "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" "अॅप्स" @@ -69,5 +73,28 @@ "शोधा" "हा अॅप स्थापित केलेला नाही" "या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता." - "Workspace वर जोडा" + "मुख्य स्क्रीनवर जोडा" + "आयटम येथे हलवा" + "आयटम मुख्य स्क्रीनवर जोडला" + "आयटम काढला" + "आयटम हलवा" + "पंक्ति %1$s स्तंभ %2$s मध्ये हलवा" + "%1$s स्थानावर हलवा" + "आवडत्या %1$s स्थानावर हलवा" + "आयटम हलविला" + "फोल्‍डरवर जोडा: %1$s" + "%1$s सह फोल्डरमध्ये जोडा" + "फोल्‍डरमध्‍ये आयटम जोडले" + "यासह फोल्‍डर तयार करा: %1$s" + "फोल्‍डर तयार केले" + "मुख्य स्क्रीनवर हलवा" + "स्क्रीन डावीकडे हलवा" + "स्क्रीन उजवीकडे हलवा" + "स्क्रीन हलविली" + "आकार बदला" + "रूंदी वाढवा" + "उंची वाढवा" + "रुंदी कमी करा" + "उंची कमी करा" + "विजेटचा आकार रुंदी %1$s उंची %2$s मध्ये बदलला" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index e2b8e162c..208d72839 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Kerja" "Apl tidak dipasang." "Apl tidak tersedia" "Apl yang dimuat turun dilumpuhkan dalam mod Selamat" @@ -28,6 +29,9 @@ "Papar Mem" "Sentuh & tahan untuk mengambil widget." "%1$d × %2$d" + "Cari Apl" + "Memuatkan Apl…" + "Tiada Apl yang ditemui sepadan dengan \"%1$s\"" "Tiada lagi ruang pada skrin Laman Utama ini." "Tiada ruang dalam dulang Kegemaran lagi" "Apl" @@ -69,5 +73,28 @@ "Carian" "Apl ini tidak dipasang" "Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual." - "Tambahkan Pada Ruang Kerja" + "Tambahkan pada Skrin Utama" + "Alihkan item ke sini" + "Item ditambahkan pada skrin utama" + "Item dialih keluar" + "Alihkan Item" + "Alihkan ke baris %1$s lajur %2$s" + "Alihkan ke kedudukan %1$s" + "Alihkan ke kedudukan kegemaran %1$s" + "Item dialihkan" + "Tambahkan pada folder: %1$s" + "Tambahkan pada folder dengan %1$s" + "Item ditambahkan pada folder" + "Buat folder dengan: %1$s" + "Folder dibuat" + "Alihkan ke Skrin Utama" + "Alihkan skrin ke kiri" + "Alihkan skrin ke kanan" + "Skrin dialihkan" + "Ubah saiz" + "Tambahkan kelebaran" + "Tambahkan ketinggian" + "Kurangkan kelebaran" + "Kurangkan ketinggian" + "Saiz widget diubah menjadi %1$s lebar %2$s tinggi" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 7589f75c1..39f2957ba 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher၃" + "အလုပ်" "အပ်ပလီကေးရှင်း မထည့်သွင်းထားပါ" "App လက်လှမ်း မမှီပါ" "ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား" @@ -28,6 +29,9 @@ "Mem ကိုပြရန်" "ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ" "%1$d × %2$d" + "ရှာဖွေမှု Appများ" + "App များ ရယူနေစဉ်..." + "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ" "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" "အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ" "အပ်ပလီကေးရှင်းများ" @@ -69,5 +73,28 @@ "ရှာဖွေရန်" "App မတပ်ဆင်ရသေးပါ" "ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually." - "အလုပ်နေရာသို့ ပေါင်းထည့်မည်" + "ပင်မမျက်နှာစာသို့ ထည့်ပါ" + "၎င်းအား ဤသို့ ရွှေ့ပါ" + "ပင်မ ဖန်မျက်နှာပြင်သို့ ထည့်ပြီး၏" + "၎င်းအား ဖယ်ရှားပြီး၏" + "၎င်းအား ရွှေ့ပါ" + "အတန်း %1$s အတိုင် %2$s သို့ ရွှေ့ပါ" + "%1$s သို့ နေရာရွှေ့ပါ" + "စိတ်ကြိုက်နေရာ %1$s သို့ ရွှေ့ပါ" + "၎င်းအားရွှေ့ပြီး" + "ဖိုလ်ဒါသို့ ထည့်ရန်- %1$s" + "%1$s အမည်ရှိ ဖိုလ်ဒါသို့ ထည့်ပြီး၏" + "ဖိုလ်ဒါသို့ ထည့်ပြီး" + "ဖိုလ်ဒါ ပြုလုပ်ရန်- %1$s" + "ဖိုလ်ဒါ ပြုလုပ်ပြီး" + "ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ" + "မျက်နှာပြင် ဘယ်ဘက်သို့ ရွှေ့ပါ" + "မျက်နှာပြင် ညာဘက်သို့ ရွှေ့ပါ" + "ဖန်မျက်နှာပြင် ပြောင်းရွှေ့ပြီး၏" + "အရွယ်အစားပြောင်းပါ" + "အကျယ်အား တိုးပါ" + "အမြင့်အား တိုးပါ" + "အကျယ်အား လျှော့ပါ" + "အမြင့်အား လျှော့ပါ" + "Widget အား အကျယ် %1$s အမြင့် %2$s အရွယ်အစားပြန်လည်ချိန်ညှိပြီး၏" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index df9ec93e5..92ac79d45 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Jobb" "Appen er ikke installert." "Appen er ikke tilgjengelig" "En nedlastet app er deaktivert i sikker modus" @@ -28,6 +29,9 @@ "Vis minne" "Trykk og hold inne for å plukke opp en modul." "%1$d × %2$d" + "Søk i apper" + "Laster inn apper …" + "Fant ingen apper som samsvarer med «%1$s»" "Denne startsiden er full." "Favoritter-skuffen er full" "Apper" @@ -69,5 +73,28 @@ "Søk" "Denne appen er ikke installert" "Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt." - "Legg til i arbeidsområdet" + "Legg til på startskjermen" + "Flytt elementet hit" + "Elementet er lagt til på startskjermen" + "Elementet er fjernet" + "Flytt elementet" + "Flytt til rad %1$s, kolonne %2$s" + "Flytt til posisjon %1$s" + "Flytt til posisjonen for favoritter %1$s" + "Elementet er flyttet" + "Legg til i mappe: %1$s" + "Legg til mappen med %1$s" + "Elementet er lagt til i mappen" + "Opprett mappe med: %1$s" + "Mappen er opprettet" + "Flytt til startskjermen" + "Flytt skjermen til venstre" + "Flytt skjermen til høyre" + "Skjermen er flyttet" + "Endre størrelse" + "Øk bredden" + "Øk høyden" + "Reduser bredden" + "Reduser høyden" + "Størrelsen på modulen er endret til bredde %1$s og høyde %2$s" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index 4398334cf..77e7138db 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "कार्य" "अनुप्रयोग स्थापित छैन।" "अनुप्रयोग उपलब्ध छैन" "सुरक्षित मोडमा डाउनलोड गरेको अनुप्रयोग अक्षम गरिएको छ" @@ -28,6 +29,9 @@ "Mem देखाउनुहोस्" "एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।" "%1$d × %2$d" + "अनुप्रयोगहरू खोज्नुहोस्" + "अनुप्रयोगहरू लोड गरिँदै..." + "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्" "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" "मनपर्ने ट्रे अब कुनै ठाँउ छैन" "अनुप्रयोगहरू" @@ -46,10 +50,7 @@ "यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।" "बेनाम फोल्डर" "पृष्ठ %2$d को %1$d" - "गृह स्क्रिन %2$d को %2$d" - - - + "गृह स्क्रिन %1$d को %2$d" "स्वागतम" "तपाईँको अनुप्रयोग आईकनको प्रतिलिप गर्नुहोस्" "आफ्नो पुरानो गृह स्क्रीनबाट अाईकन र फोल्डरहरू आयात गर्नुहोस्?" @@ -72,5 +73,28 @@ "खोजी गर्नुहोस्" "यो अनुप्रयोग स्थापित छैन" "यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।" - "कार्यक्षेत्रमा थप्नुहोस्" + "गृह स्क्रिनमा थप्नुहोस्" + "वस्तु यहाँ सार्नुहोस्" + "वस्तु गृह स्क्रिनमा थपियो" + "वस्तु हटाइयो" + "वस्तु सार्नुहोस्" + "पङ्क्ति %1$s स्तम्भ %2$s मा सार्नुहोस्" + "स्थिति %1$s मा सार्नुहोस्" + "मनपर्ने स्थिति %1$s मा सार्नुहोस्" + "वस्तु सारियो" + "फोल्डर: %1$s मा थप्नुहोस्" + "फोल्डरमा %1$s सँग थप्नुहोस्" + "वस्तु फोल्डरमा थपियो" + "%1$s: मार्फत फोल्डर सिर्जना गर्नुहोस्" + "फोल्डर सिर्जना गरियो" + "गृह स्क्रिनमा सार्नुहोस्" + "स्क्रिनलाई बायाँ सार्नुहोस्" + "स्क्रिनलाई दायाँ सार्नुहोस्" + "स्क्रिन सारियो" + "पुनःआकार मिलाउनुहोस्" + "चौडाइ बढाउनुहोस्" + "उँचाइ बढाउनुहोस्" + "चौडाइ घटाउनुहोस्" + "उँचाइ घटाउनुहोस्" + "विजेट चौडाइ %1$s उचाइ %2$s मा पुनः आकार मिलाइयो" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 8a82624b3..914349322 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Werk" "App is niet geïnstalleerd." "App is niet beschikbaar" "Gedownloade app uitgeschakeld in veilige modus" @@ -28,6 +29,9 @@ "Geheugen weergeven" "Blijf aanraken om een widget toe te voegen." "%1$d × %2$d" + "Apps zoeken" + "Apps laden…" + "Er zijn geen apps gevonden die overeenkomen met \'%1$s\'" "Er is geen ruimte meer op dit startscherm." "Geen ruimte meer in het vak \'Favorieten\'" "Apps" @@ -35,7 +39,7 @@ "Verwijderen" "Verwijderen" "App-info" - "snelkoppelingen installeren" + "Snelkoppelingen instellen" "Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker." "instellingen en snelkoppelingen op de startpagina lezen" "De app toestaan de instellingen en snelkoppelingen op de startpagina te lezen." @@ -69,5 +73,28 @@ "Zoeken" "Deze app is niet geïnstalleerd" "De app voor dit pictogram is niet geïnstalleerd. U kunt het pictogram verwijderen of de app zoeken en handmatig installeren." - "Toevoegen aan werkruimte" + "Toevoegen aan startpagina" + "Item hier naartoe verplaatsen" + "Item toegevoegd aan startscherm" + "Item verwijderd" + "Item verplaatsen" + "Verplaatsen naar rij %1$s, kolom %2$s" + "Verplaatsen naar positie %1$s" + "Verplaatsen naar positie %1$s voor favorieten" + "Item verplaatst" + "Toevoegen aan map: %1$s" + "Toevoegen aan map met %1$s" + "Item toegevoegd aan map" + "Map maken met: %1$s" + "Map gemaakt" + "Verplaatsen naar startscherm" + "Scherm naar links verplaatsen" + "Scherm naar rechts verplaatsen" + "Scherm verplaatst" + "Formaat aanpassen" + "Breedte vergroten" + "Hoogte vergroten" + "Breedte verkleinen" + "Hoogte verkleinen" + "Formaat van widget gewijzigd in breedte %1$s en hoogte %2$s" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 94da2a52c..220652c54 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Praca" "Aplikacja nie jest zainstalowana." "Aplikacja niedostępna" "Pobrana aplikacja została wyłączona w trybie awaryjnym" @@ -28,6 +29,9 @@ "Pokaż pamięć" "Aby dodać widżet, kliknij go i przytrzymaj." "%1$d × %2$d" + "Szukaj w aplikacjach" + "Wczytuję aplikacje…" + "Nie znaleziono aplikacji pasujących do zapytania „%1$s”" "Brak miejsca na tym ekranie głównym." "Brak miejsca w Ulubionych" "Aplikacje" @@ -69,5 +73,28 @@ "Szukaj" "Ta aplikacja nie jest zainstalowana" "Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie." - "Dodaj do obszaru roboczego" + "Dodaj do strony głównej" + "Przenieś element tutaj" + "Element został dodany do ekranu głównego" + "Element został usunięty" + "Przenieś element" + "Przenieś do wiersza %1$s w kolumnie %2$s" + "Przenieś do pozycji %1$s" + "Przenieś do pozycji ulubionych: %1$s" + "Element został przeniesiony" + "Dodaj do folderu: %1$s" + "Dodaj do folderu z: %1$s" + "Element został dodany do folderu" + "Utwórz folder z: %1$s" + "Folder został utworzony" + "Przenieś na ekran główny" + "Przenieś ekran w lewo" + "Przenieś ekran w prawo" + "Ekran został przeniesiony" + "Zmień rozmiar" + "Zwiększ szerokość" + "Zwiększ wysokość" + "Zmniejsz szerokość" + "Zmniejsz wysokość" + "Szerokość i wysokość widżetu zmieniła się na %1$s x %2$s" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index ef1fa6f4e..636417ce1 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Iniciador3" + "Trabalho" "A aplicação não está instalada." "A aplicação não está disponível" "Aplicação transferida desativada no Modo de segurança" @@ -28,6 +29,9 @@ "Mostrar mem" "Prima sem soltar para escolher um widget." "%1$d × %2$d" + "Pesquisar aplicações" + "A carregar aplicações..." + "Não foram encontradas aplic. que correspondam a \"%1$s\"" "Sem espaço suficiente neste Ecrã principal." "Não existe mais espaço no tabuleiro de Favoritos" "Aplicações" @@ -69,5 +73,28 @@ "Pesquisar" "Esta aplicação não está instalada" "A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente." - "Adic. ao espaço de trabalho" + "Adicionar ao Ecrã principal" + "Mover o item para aqui" + "Item adicionado ao ecrã principal" + "Item removido" + "Mover item" + "Mover para a linha %1$s, coluna %2$s" + "Mover para a posição %1$s" + "Mover para a posição %1$s dos favoritos" + "Item movido" + "Adicionar à pasta: %1$s" + "Adicionar à pasta com %1$s" + "Item adicionado à pasta" + "Criar pasta com: %1$s" + "Pasta criada" + "Mover para o Ecrã principal" + "Mover ecrã para a esquerda" + "Mover ecrã para a direita" + "Ecrã movido" + "Redimensionar" + "Aumentar largura" + "Aumentar altura" + "Diminuir largura" + "Diminuir altura" + "Widget redimensionado para a largura %1$s, altura %2$s" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 8c592c191..c03b00cec 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Trabalho" "O app não está instalado." "O app não está disponível" "App transferido por download desativado no modo de segurança" @@ -28,6 +29,9 @@ "Mostrar memória" "Toque e pressione para selecionar um widget." "%1$d × %2$d" + "Pesquisar apps" + "Carregando apps…" + "Nenhum app encontrado que corresponda a \"%1$s\"" "Não há mais espaço na tela inicial." "Sem espaço na bandeja de favoritos" "Apps" @@ -69,5 +73,28 @@ "Pesquisar" "Este app não está instalado" "O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente." - "Adicionar a espaço de trabalho" + "Adicionar à tela inicial" + "Mover item para cá" + "Item adicionado à tela inicial" + "Item removido" + "Mover item" + "Mover para a linha %1$s, coluna %2$s" + "Mover para a posição %1$s" + "Mover para a posição %1$s dos favoritos" + "Item movido" + "Adicionar à pasta: %1$s" + "Adicionar à pasta com %1$s" + "Item adicionado à pasta" + "Criar pasta com: %1$s" + "Pasta criada" + "Mover para a tela inicial" + "Mover tela para a esquerda" + "Mover tela para a direita" + "Tela movida" + "Redimensionar" + "Aumentar largura" + "Aumentar altura" + "Diminuir largura" + "Diminuir altura" + "Widget redimensionado para a largura %1$s, altura %2$s" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 1bcaa5a82..a5f2b01d4 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Work" "Aplicația nu este instalată." "Aplicația nu este disponibilă" "Aplicația descărcată este dezactivată în modul de siguranță" @@ -28,6 +29,9 @@ "Afișați memoria" "Atingeți lung un widget pentru a-l alege." "%1$d × %2$d" + "Căutați aplicații" + "Se încarcă aplicațiile..." + "Nu s-a găsit nicio aplicație pentru „%1$s”" "Nu mai este loc pe acest Ecran de pornire." "Spațiu epuizat în bara Preferate" "Aplicații" @@ -69,5 +73,28 @@ "Căutați" "Aplicația nu este instalată" "Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual." - "Adăugați în spațiul de lucru" + "Adăugați pe ecranul de pornire" + "Mutați elementul aici" + "Element adăugat pe ecranul de pornire" + "Element eliminat" + "Mutați elementul" + "Mutați pe rândul %1$s, coloana %2$s" + "Mutați pe poziția %1$s" + "Mutați în preferate, pe poziția %1$s" + "Element mutat" + "Adăugați în dosar: %1$s" + "Adăugați în dosarul cu %1$s" + "Element adăugat în dosar" + "Creați dosar cu: %1$s" + "Dosar creat" + "Mutați pe ecranul de pornire" + "Mutați ecranul la stânga" + "Mutați ecranul la dreapta" + "Ecran mutat" + "Redimensionați" + "Creșteți lățimea" + "Creșteți înălțimea" + "Reduceți lățimea" + "Reduceți înălțimea" + "Widgetul a fost redimensionat la lățimea %1$s și înălțimea %2$s" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index c5eb1c27d..bd126950b 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Работа" "Приложение удалено" "Приложение недоступно" "Скачанное приложение отключено в безопасном режиме" @@ -28,6 +29,9 @@ "Сведения о памяти" "Чтобы выбрать виджет, нажмите на значок и удерживайте его." "%1$d x %2$d" + "Поиск приложений" + "Загрузка…" + "По запросу \"%1$s\" ничего не найдено" "На этом экране все занято" "В разделе \"Избранное\" больше нет места" "Приложения" @@ -69,5 +73,28 @@ "Найти" "Приложение не установлено" "Приложение не установлено. Вы можете удалить значок или найти приложение и установить его вручную." - "Добавить в рабочую область" + "Добавить на главный экран" + "Переместить элемент сюда" + "Элемент добавлен на главный экран" + "Элемент удален" + "Переместить элемент" + "Переместить в ячейку %1$s %2$s" + "Переместить в позицию %1$s" + "Переместить в Избранное (%1$s)" + "Элемент перемещен." + "Добавить в папку %1$s." + "Добавить в папку с приложением \"%1$s\"" + "Элемент добавлен в папку." + "Создать папку с элементом %1$s." + "Папка создана." + "Переместить на главный экран" + "Переместить экран влево" + "Переместить экран вправо" + "Экран перемещен" + "Изменить размер" + "Увеличить ширину" + "Увеличить высоту" + "Уменьшить ширину" + "Уменьшить высоту" + "Изменен размер виджета: до %1$s в ширину и %2$s в высоту" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index fcfa71739..71a37d171 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "කාර්යාලය" "යෙදුම ස්ථාපනය කර නැත." "යෙදුම නොතිබේ" "ආරක්ෂිත ආකාරය තුළ බාගන්න ලද යෙදුම් අබල කරන්න" @@ -28,6 +29,9 @@ "Mem පෙන්වන්න" "විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න." "%1$d × %2$d" + "යෙදුම් සෙවීම" + "යෙදුම් පූරණය වෙමින්…" + "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." "ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත" "යෙදුම්" @@ -69,5 +73,28 @@ "සොයන්න" "මෙම යෙදුම ස්ථාපනය කර නොමැත" "මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න." - "කාර්ය ඉඩ වෙත එකතු කරන්න" + "මුල් තිරය වෙත එක් කරන්න" + "මෙතනට අයිතමය ගෙන එන්න" + "අයිතමය මුල් තිරය වෙත එකතු කරන ලදි" + "අයිතමය ඉවත් කරන ලදි" + "අයිතමය ගෙනයන්න" + "පේළිය %1$s තීරුව %2$s වෙත ගෙන යන්න" + "%1$s ස්ථානය වෙත ගෙන යන්න" + "ප්‍රියතම ස්ථානය %1$s වෙත ගෙන යන්න" + "අයිතමය ගෙන යන ලදි" + "ෆෝල්ඩරය එක් කරන්න: %1$s" + "%1$s සමග ෆෝල්ඩරය වෙත එකතු කරන්න" + "අයිතමය ෆෝඩරය වෙතට එක් කරන ලදි" + "මේ සමග ෆෝල්ඩරය සාදන්න: %1$s" + "ෆෝල්ඩරය සාදන ලදි" + "මුල් තිරය වෙත ගෙන යන්න" + "තිරය වම් පැත්තට ගෙනයන්න" + "තිරය දකුණු පැත්තට ගෙනයන්න" + "තිරය ගෙන යන ලදි" + "නැවත ප්‍රමාණගත කිරීම" + "පළල වැඩි කරන්න" + "උස වැඩි කරන්න" + "පළල අඩු කරන්න" + "උස අඩු කරන්න" + "විජට් පළල %1$s උස %2$s වෙත ප්‍රමාණකරණය කරන ලදි" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 7b20ba41a..920c77c2e 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Pracovné" "Aplikácia nie je nainštalovaná." "Aplikácia nie je k dispozícii" "Stiahnutá aplikácia je v núdzovom režime zakázaná" @@ -28,13 +29,16 @@ "Zobraziť pamäť" "Miniaplikáciu pridáte stlačením a podržaním." "%1$d × %2$d" + "Vyhľadávanie v aplikáciách" + "Načítavajú sa aplikácie..." + "Nenašli sa žiadne aplikácie zodpovedajúce dopytu %1$s" "Na tejto ploche už nie je miesto" "Na paneli Obľúbené položky už nie je miesto" "Aplikácie" "Domovská stránka" "Odstrániť" "Odinštalovať" - "Informácie o aplikácii" + "O aplikácii" "inštalovať odkazy" "Povoľuje aplikácii pridať odkazy bez zásahu používateľa." "čítanie nastavení a odkazov plochy" @@ -54,7 +58,7 @@ "ZAČAŤ ODZNOVA" "Pozadia, miniaplikácie a nastavenia" "Ak si chcete pozadie prispôsobiť, klepnite naň a podržte ho" - "ROZUMIEM" + "DOBRE" "Otvorený priečinok, %1$d x %2$d" "Dotykom zavriete priečinok" "Dotykom premenovanie uložíte" @@ -69,5 +73,28 @@ "Vyhľadať" "Táto aplikácia nie je nainštalovaná" "Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte." - "Pridať do pracovného priestoru" + "Pridať na plochu" + "Presunúť položku sem" + "Položka bola pridaná na plochu" + "Položka bola odstránená" + "Presunúť položku" + "Presunúť do stĺpca %2$s v riadku %1$s" + "Presunúť na %1$s. miesto" + "Presunúť na %1$s. miesto v obľúbených položkách" + "Položka bola presunutá" + "Pridať do priečinka: %1$s" + "Pridať do priečinka %1$s" + "Položka bola pridaná do priečinka" + "Vytvoriť priečinok pomocou: %1$s" + "Priečinok bol vytvorený" + "Presunúť na plochu" + "Presunúť obrazovku doľava" + "Presunúť obrazovku doprava" + "Obrazovka bola posunutá" + "Zmeniť veľkosť" + "Zvýšiť šírku" + "Zväčšiť výšku" + "Znížiť šírku" + "Znížiť výšku" + "Veľkosť miniaplikácie bola zmenená na %1$s x %2$s (šírka x výška)" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index ae8c071fa..3d7ee5e99 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Zaganjalnik3" + "Služba" "Aplikacija ni nameščena." "Aplikacija ni na voljo" "Prenesena aplikacija je onemogočena v Varnem načinu" @@ -28,6 +29,9 @@ "Pokaži pomnilnik" "Za izbiro pripomočka se ga dotaknite in pridržite." "%1$d × %2$d" + "Iskanje po aplikacijah" + "Nalaganje aplikacij …" + "Ni aplikacij, ki bi ustrezale poizvedbi »%1$s«" "Na tem začetnem zaslonu ni več prostora." "V vrstici za priljubljene ni več prostora" "Aplikacije" @@ -69,5 +73,28 @@ "Iskanje" "Ta aplikacija ni nameščena." "Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno." - "Dodajanje v delovni prostor" + "Dodajanje na začetni zaslon" + "Premik elementa sem" + "Element je bil dodan na začetni zaslon" + "Element je bil odstranjen" + "Premik elementa" + "Premik v %1$s. vrstico %2$s. stolpca" + "Premk na mesto št. %1$s" + "Premik na mesto priljubljenih št. %1$s" + "Element je premaknjen" + "Dodajanje v mapo: %1$s" + "Dodajanje v mapo z aplikacijo %1$s" + "Element je dodan v mapo" + "Ustvarjanje mape s tem: %1$s" + "Mapa je ustvarjena" + "Premik na začetni zaslon" + "Premik zaslona levo" + "Premika zaslona desno" + "Zaslon je bil premaknjen" + "Spreminjanje velikosti" + "Povečanje širine" + "Povečanje višine" + "Zmanjšanje širine" + "Zmanjšanje višine" + "Velikost pripomočka je bila spremenjena na %1$s širine in %2$s višine" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 744fe37a1..f4c853625 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Work" "Апликација није инсталирана." "Апликација није доступна" "Преузета апликација је онемогућена у Безбедном режиму" @@ -28,6 +29,9 @@ "Прикажи меморију" "Додирните и задржите да бисте изабрали виџет." "%1$d×%2$d" + "Претражите апликације" + "Апликације се учитавају..." + "Није пронађена ниједна апликација за „%1$s“" "Нема више простора на овом почетном екрану." "Нема више простора на траци Омиљено" "Апликације" @@ -69,5 +73,28 @@ "Претражи" "Ова апликација није инсталирана" "Апликација за ову икону није инсталирана. Можете да је уклоните или да потражите апликацију и инсталирате је ручно." - "Додај у радни простор" + "Додај на почетни екран" + "Премести ставку овде" + "Ставка је додата на почетни екран" + "Ставка је уклоњена" + "Премести ставку" + "Премести у ред %1$s и колону %2$s" + "Премести на %1$s. позицију" + "Премести на %1$s. позицију у омиљеним" + "Ставка је премештена" + "Додај у директоријум: %1$s" + "Додај у директоријум у коме је %1$s" + "Ставка је додата у директоријум" + "Направите директоријум са: %1$s" + "Директоријум је направљен" + "Премести на почетни екран" + "Помери екран улево" + "Помери екран удесно" + "Екран је померен" + "Промени величину" + "Повећај ширину" + "Повећај висину" + "Смањи ширину" + "Смањи висину" + "Величина виџета је промењена на ширину %1$s и висину %2$s" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index cb1a42986..b015033fe 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Arbete" "Appen är inte installerad." "Appen är inte tillgänglig" "Den hämtade appen inaktiverades i säkert läge" @@ -28,6 +29,9 @@ "Visa Mem" "Tryck länge om du vill flytta en widget." "%1$d × %2$d" + "Sök efter appar" + "Läser in appar …" + "Det gick inte att hitta några appar som matchar %1$s" "Det finns inte plats för mer på den här startskärmen." "Favoritfältet är fullt" "Appar" @@ -69,5 +73,28 @@ "Sök" "Appen är inte installerad" "Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt." - "Lägg till på arbetsyta" + "Lägg till på startskärmen" + "Flytta objekt hit" + "Objektet har lagts till på startskärmen" + "Objektet har tagits bort" + "Flytta objekt" + "Flytta till rad %1$s, kolumn %2$s" + "Flytta till plats %1$s" + "Flytta till favoritplats %1$s" + "Objektet har flyttats" + "Lägg till i mapp: %1$s" + "Lägg till i mappen med %1$s" + "Objektet har lagts till i mappen" + "Skapa mapp med: %1$s" + "Mappen har skapats" + "Flytta till startskärmen" + "Flytta skärmen till vänster" + "Flytta skärmen till höger" + "Skärmen har flyttats" + "Ändra storlek" + "Öka bredden" + "Öka höjden" + "Minska bredden" + "Minska höjden" + "Widgetens storlek har ändrats till: bredd %1$s, höjd %2$s" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 5283e47c9..a9319cb3a 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Kizindua3" + "Kazini" "Programu haijasakinishwa." "Programu haipatikani" "Programu iliyopakuliwa imezimwa katika Hali Salama" @@ -28,6 +29,9 @@ "Onyesha Kumbukumbu" "Gusa na ushikilie ili kuteua wijeti." "%1$d × %2$d" + "Tafuta Programu" + "Inapakia Programu..." + "Haikupata programu zinazolingana na \"%1$s\"" "Hakuna nafasi katika skrini hii ya Mwanzo." "Hakuna nafasi zaidi katika treya ya Vipendeleo" "Programu" @@ -71,5 +75,28 @@ "Tafuta" "Programu hii haijasakinishwa" "Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." - "Ongeza Kwenye Nafasi ya kazi" + "Ongeza kwenye skrini ya Kwanza" + "Hamishia kipengee hapa" + "Kipengee kimeongezwa kwenye skrini ya kwanza" + "Kipengee kimeondolewa" + "Hamisha kipengee" + "Hamishia safu mlalo %1$s safu wima %2$s" + "Hamishia nafasi ya %1$s" + "Hamishia nafasi inayopendwa ya %1$s" + "Kipengee kimesogezwa" + "Ongeza kwenye folda: %1$s" + "Ongeza kwenye folda iliyo na %1$s" + "Kipengee kimeongezwa kwenye folda" + "Unda folda ukitumia: %1$s" + "Folda imeundwa" + "Hamishia Skrini ya kwanza" + "Sogeza skrini kushoto" + "Sogeza skrini kulia" + "Skrini imesogezwa" + "Badilisha ukubwa" + "Ongeza upana" + "Ongeza urefu" + "Punguza upana" + "Punguza urefu" + "Wijeti imepunguzwa hadi upana %1$s urefu %2$s" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index a610bf82d..a70598b1f 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "லாஞ்சர்3" + "பணியிடம்" "பயன்பாடு நிறுவப்படவில்லை." "பயன்பாடு இல்லை" "இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது" @@ -28,6 +29,9 @@ "நினைவகத்தைக் காட்டு" "விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்." "%1$d × %2$d" + "பயன்பாடுகளில் தேடுக" + "பயன்பாடுகளை ஏற்றுகிறது..." + "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" "முகப்புத் திரையில் இடமில்லை." "பிடித்தவை ட்ரேயில் இடமில்லை" "பயன்பாடுகள்" @@ -69,5 +73,28 @@ "தேடு" "பயன்பாடு நிறுவப்படவில்லை" "ஐகானுக்கான பயன்பாடு நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்." - "பணியிடத்தில் சேர்" + "முகப்புத் திரையில் சேர்" + "இங்கு நகர்த்து" + "முகப்புத் திரையில் சேர்க்கப்பட்டது" + "அகற்றப்பட்டது" + "நகர்த்து" + "%1$s வரிசை, %2$s நெடுவரிசைக்கு நகர்த்து" + "நிலை %1$sக்கு நகர்த்து" + "விரும்பும் நிலை %1$sக்கு நகர்த்து" + "உருப்படி நகர்த்தப்பட்டது" + "இந்தக் கோப்புறையில் சேர்க்கும்: %1$s" + "%1$s உள்ள கோப்புறையில் சேர்க்கும்" + "கோப்புறையில் உருப்படி சேர்க்கப்பட்டது" + "இதனுடன் கோப்புறையை உருவாக்கும்: %1$s" + "கோப்புறை உருவாக்கப்பட்டது" + "முகப்புத் திரைக்கு நகர்த்து" + "திரையை இடப்புறம் நகர்த்து" + "திரையை வலப்புறம் நகர்த்து" + "திரை நகர்த்தப்பட்டது" + "அளவு மாற்று" + "அகலத்தை அதிகரி" + "உயரத்தை அதிகரி" + "அகலத்தைக் குறை" + "உயரத்தைக் குறை" + "அகலம் %1$s மற்றும் உயரம் %2$sக்கு விட்ஜெட் அளவு மாற்றப்பட்டது" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index e3fd8e405..f47d428c7 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "లాంచర్3" + "కార్యాలయం" "అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు." "అనువర్తనం అందుబాటులో లేదు" "డౌన్‌లోడ్ చేసిన అనువర్తనం సురక్షిత మోడ్‌లో నిలిపివేయబడింది" @@ -28,6 +29,9 @@ "మెమరీ చూపు" "విడ్జెట్‌ను ఎంచుకోవడానికి తాకి & నొక్కి పెట్టండి." "%1$d × %2$d" + "అనువర్తనాలను శోధించండి" + "అనువర్తనాలను లోడ్ చేస్తోంది…" + "\"%1$s\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." "ఇష్టమైనవి ట్రేలో ఖాళీ లేదు" "అనువర్తనాలు" @@ -69,5 +73,28 @@ "శోధించు" "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" "ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." - "వర్క్‌స్పేస్‌కు జోడించు" + "హోమ్ స్క్రీన్‌కు జోడించు" + "అంశాన్ని ఇక్కడికి తరలించు" + "అంశం హోమ్‌స్క్రీన్‌కి జోడించబడింది" + "అంశం తీసివేయబడింది" + "అంశాన్ని తరలించు" + "అడ్డు వరుస %1$s నిలువు వరుస %2$sకి తరలించు" + "%1$sవ స్థానానికి తరలించు" + "ఇష్టమైనవిలో %1$sవ స్థానానికి తరలించు" + "అంశం తరలించబడింది" + "ఈ ఫోల్డర్‌కి జోడించండి: %1$s" + "%1$s గల ఫోల్డర్‌కు జోడించు" + "అంశం ఫోల్డర్‌కు జోడించబడింది" + "ఈ పేరుతో ఫోల్డర్‌ను సృష్టించండి: %1$s" + "ఫోల్డర్ సృష్టించబడింది" + "హోమ్‌స్క్రీన్‌కు తరలించు" + "స్క్రీన్‌ను ఎడమవైపుకి జరుపు" + "స్క్రీన్‌ను కుడివైపుకి జరుపు" + "స్క్రీన్ జరపబడింది" + "పరిమాణం మార్చు" + "వెడల్పును పెంచు" + "ఎత్తును పెంచు" + "వెడల్పును తగ్గించు" + "ఎత్తును తగ్గించు" + "విడ్జెట్ పరిమాణం వెడల్పు %1$sకి, ఎత్తు %2$sకి మార్చబడింది" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 72d2d4bad..d53caf66b 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "งาน" "ไม่ได้ติดตั้งแอป" "แอปไม่พร้อมใช้งาน" "แอปที่ดาวน์โหลดถูกปิดในโหมดปลอดภัย" @@ -28,6 +29,9 @@ "แสดง Mem" "แตะค้างเพื่อรับวิดเจ็ต" "%1$d × %2$d" + "ค้นหาแอป" + "กำลังโหลดแอป…" + "ไม่พบแอปที่ตรงกับ \"%1$s\"" "ไม่มีที่ว่างในหน้าจอหลักนี้" "ไม่มีพื้นที่เหลือในถาดรายการโปรด" "แอป" @@ -69,5 +73,28 @@ "ค้นหา" "ไม่ได้ติดตั้งแอปนี้" "ยังไม่ได้ติดตั้งแอปสำหรับไอคอนนี้ คุณสามารถนำไอคอนออก หรือค้นหาแอปดังกล่าวแล้วติดตั้งด้วยตนเอง" - "เพิ่มไปยังพื้นที่ทำงาน" + "เพิ่มลงในหน้าแรก" + "ย้ายรายการมาที่นี่" + "เพิ่มรายการไปยังหน้าจอหลักแล้ว" + "นำออกรายการออกแล้ว" + "ย้ายรายการ" + "ย้ายไปที่แถว %1$s คอลัมน์ %2$s" + "ย้ายไปยังตำแหน่ง %1$s" + "ย้ายไปยังตำแหน่งที่ %1$s ของรายการโปรด" + "ย้ายรายการแล้ว" + "เพิ่มไปยังโฟลเดอร์: %1$s" + "เพิ่มไปยังโฟลเดอร์ที่มี %1$s" + "เพิ่มรายการไปยังโฟลเดอร์แล้ว" + "สร้างโฟลเดอร์ด้วย: %1$s" + "สร้างโฟลเดอร์แล้ว" + "ย้ายไปที่หน้าจอหลัก" + "เลื่อนหน้าจอไปทางซ้าย" + "เลื่อนหน้าจอไปทางขวา" + "ย้ายหน้าจอแล้ว" + "ปรับขนาด" + "เพิ่มความกว้าง" + "เพิ่มความสูง" + "ลดความกว้าง" + "ลดความสูง" + "ปรับขนาดของวิดเจ็ตเป็นกว้าง %1$s สูง %2$s แล้ว" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 948139bda..fb83f4b2b 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Trabaho" "Hindi naka-install ang app." "Hindi available ang app" "Naka-disable ang na-download na app sa Safe mode" @@ -28,6 +29,9 @@ "Ipakita ang Mem" "Pindutin nang matagal upang kumuha ng widget." "%1$d × %2$d" + "Mga App sa Paghahanap" + "Nilo-load ang Mga App…" + "Walang nakitang Mga App na tumutugma sa \"%1$s\"" "Wala nang lugar sa Home screen na ito." "Wala nang lugar sa tray ng Mga Paborito" "Apps" @@ -69,5 +73,28 @@ "Maghanap" "Hindi naka-install ang app na ito" "Hindi naka-install ang app para sa icon na ito. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano." - "Idagdag Sa Workspace" + "Idagdag sa Home screen" + "Ilipat ang item dito" + "Naidagdag sa home screen ang item" + "Naalis na ang item" + "Ilipat ang item" + "Ilipat sa row %1$s column %2$s" + "Ilipat sa posisyon %1$s" + "Ilipat sa posisyon %1$s sa mga paborito" + "Nalipat ang item" + "Idagdag sa folder: %1$s" + "Idagdag sa folder kasama ng %1$s" + "Idinagdag ang item sa folder" + "Gumawa ng folder na may: %1$s" + "Nagawa ang folder" + "Ilipat sa Home screen" + "Ilipat sa kaliwa ang screen" + "Ilipat sa kanan ang screen" + "Nailipat ang screen" + "I-resize" + "Dagdagan ang lapad" + "Dagdagan ang taas" + "Bawasan ang lapad" + "Bawasan ang taas" + "Na-resize ang widget sa lapad %1$s taas %2$s" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index b9add4e20..f99118fd2 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "İş" "Uygulama yüklü değil." "Uygulama kullanılamıyor" "İndirilen uygulama Güvenli modda devre dışı bırakıldı" @@ -28,6 +29,9 @@ "Belleği Göster" "Widget seçmek için dokunun ve basılı tutun." "%1$d × %2$d" + "Uygulamalarda Ara" + "Uygulamalar Yükleniyor…" + "\"%1$s\" ile eşleşen uygulama bulunamadı" "Bu Ana ekranda yer kalmadı." "Favoriler tepsisinde başka yer kalmadı" "Uygulamalar" @@ -69,5 +73,28 @@ "Ara" "Bu uygulama yüklü değil" "Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz." - "Çalışma Alanına Ekle" + "Ana ekrana ekle" + "Öğeyi buraya taşı" + "Öğe ana ekrana eklendi" + "Öğe kaldırıldı" + "Öğeyi taşı" + "%1$s. satır %2$s. sütuna taşı" + "%1$s. sıraya taşı" + "Favorilerde %1$s. sıraya taşı" + "Öğe taşındı" + "Klasöre ekle: %1$s" + "%1$s öğesini içeren klasöre ekle" + "Öğe, klasöre eklendi" + "Şu öğeyle klasör oluştur: %1$s" + "Klasör oluşturuldu" + "Ana ekrana taşı" + "Ekranı sola taşı" + "Ekranı sağa taşı" + "Ekran taşındı" + "Yeniden boyutlandır" + "Genişliği artır" + "Yüksekliği artır" + "Genişliği azalt" + "Yüksekliği azalt" + "Widget, %1$s genişlik ve %2$s yükseklik değerine yeniden boyutlandırıldı" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index a1eead8b8..e8f6ca7f1 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Робоча папка" "Додаток видалено." "Додаток недоступний" "Завантажений додаток вимкнено в безпечному режимі" @@ -28,6 +29,9 @@ "Показати пам’ять" "Натисніть і утримуйте, щоб вибрати віджет." "%1$d × %2$d" + "Пошук додатків" + "Завантаження додатків…" + "Немає додатків для запиту \"%1$s\"" "На цьому головному екрані більше немає місця." "В області \"Вибране\" немає місця" "Додатки" @@ -35,7 +39,7 @@ "Вилучити" "Видалити" "Про програму" - "установлювати ярлики" + "створення ярликів" "Дозволяє програмі самостійно додавати ярлики." "читати налаштування та ярлики головного екрана" "Дозволяє програмі читати налаштування та ярлики на головному екрані." @@ -69,5 +73,28 @@ "Шукати" "Цей додаток не встановлено" "Додаток для цього значка не встановлено. Можна видалити значок або знайти додаток і встановити його вручну." - "Додати в робочу область" + "Додати на головний екран" + "Перемістити елемент сюди" + "Елемент додано на головний екран" + "Елемент вилучено" + "Перемістити елемент" + "Перемістити в рядок %1$s, стовпець %2$s" + "Перемістити на %1$s місце" + "Перемістити у вибране на %1$s місце" + "Елемент переміщено" + "Додати в папку \"%1$s\"" + "Додати в папку з додатком %1$s" + "Елемент додано в папку" + "Створити папку з: %1$s" + "Папку створено" + "Перемістити на головний екран" + "Перемістити екран ліворуч" + "Перемістити екран праворуч" + "Екран переміщено" + "Змінити розміри" + "Збільшити ширину" + "Збільшити висоту" + "Зменшити ширину" + "Зменшити висоту" + "Розміри віджета змінено на %1$s завширшки та %2$s заввишки" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index efad22861..04402b068 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "دفتری" "ایپ انسٹال نہیں ہے۔" "ایپ دستیاب نہیں ہے" "ڈاؤن لوڈ کردہ ایپ کو محفوظ وضع میں غیر فعال کر دیا گیا" @@ -28,6 +29,9 @@ "‏Mem دکھائیں" "کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔" "%1$d × %2$d" + "ایپس تلاش کریں" + "ایپس لوڈ ہو رہی ہیں…" + "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" "پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے" "ایپس" @@ -69,5 +73,28 @@ "تلاش کریں" "یہ ایپ انسٹال کردہ نہیں ہے" "اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔" - "ورک اسپیس میں شامل کریں" + "ہوم اسکرین میں شامل کریں" + "آئٹم یہاں منتقل کریں" + "آئٹم کو ہوم اسکرین میں شامل کر دیا گیا" + "آئٹم ہٹا دیا گیا" + "آئٹم منتقل کریں" + "قطار %1$s کالم %2$s میں منتقل کریں" + "پوزیشن %1$s میں منتقل کریں" + "پسندیدہ پوزیشن %1$s میں منتقل کریں" + "آئٹم منتقل کر دیا گیا" + "فولڈر میں شامل کریں: %1$s" + "%1$s کے فولڈر میں شامل کریں" + "آئٹم فولڈر میں شامل کر دیا گیا" + "اس کے ساتھ فولڈر بنائیں: %1$s" + "فولڈر بنا دیا گیا" + "ہوم اسکرین میں منتقل کریں" + "اسکرین کو بائیں منتقل کریں" + "اسکرین کو دائیں منتقل کریں" + "اسکرین منتقل کر دی گئی" + "سائز تبدیل کریں" + "چوڑائی بڑھائیں" + "اونچائی بڑھائیں" + "چوڑائی کم کریں" + "اونچائی کم کریں" + "ویجیٹ کے سائز کو چوڑائی %1$s اونچائی %2$s میں تبدیل کر دیا گیا" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index b594e7db2..2dbab33eb 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Ishga tushirgich3" + "Ishga oid" "Ilova o‘rnatilmadi." "Ilova mavjud emas" "Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi" @@ -28,14 +29,17 @@ "Xotirani ko‘rsatish" "Vidjetni tanlash uchun bosib turing." "%1$d × %2$d" + "Ilovalarni qidirish" + "Ilovalar yuklanmoqda…" + "“%1$s” bilan mos hech qanday ilova topilmadi" "Uy ekranida bitta ham xona yo‘q." "Ajratilganlarda birorta ham xona yo‘q" "Ilovalar" "Uy" "O‘chirish" "O‘chirish" - "Ilova ma’lumoti" - "yorliqlar o‘rnatish" + "Ilova haqida" + "yorliqlar yaratish" "Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi." "Uy sozlamalari va yorliqlarini o‘qish" "Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalarni o‘qish uchun ruxsat beradi." @@ -69,5 +73,28 @@ "Qidirish" "Ushbu ilova o‘rnatilmagan" "Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin." - "Ishchi maydonga qo‘shish" + "Bosh ekranga qo‘shish" + "Obyektni bu yerga ko‘chirish" + "Obyekt bosh ekranga qo‘shildi" + "Obyekt o‘chirib tashlandi" + "Obyektni ko‘chirib o‘tkazish" + "%1$s %2$s katakka ko‘chirib o‘tkazish" + "%1$s-joyga ko‘chirib o‘tkazish" + "Sevimlilarga (%1$s) ko‘chirib o‘tkazish" + "Element ko‘chirib o‘tkazildi" + "%1$s jildiga qo‘shish" + "%1$s ilovasi bor jildga qo‘shish" + "Element jildga qo‘shildi" + "%1$s bilan jild yaratish" + "Jild yaratildi" + "Bosh ekranga ko‘chirish" + "Ekranni chapga siljitish" + "Ekranni o‘ngga siljitish" + "Ekran siljitildi" + "O‘lchamini o‘zgartirish" + "Enini uzaytirish" + "Bo‘yini uzaytirish" + "Enini kichraytirish" + "Bo‘yini kichraytirish" + "Vidjetning eni %1$s, bo‘yi %2$s qilib o‘zgartirildi" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 278bb5484..48c8c09b8 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Work" "Ứng dụng chưa được cài đặt." "Ứng dụng không có sẵn" "Ứng dụng đã tải xuống bị tắt ở chế độ An toàn" @@ -28,6 +29,9 @@ "Hiển thị bộ nhớ" "Chạm và giữ để chọn tiện ích con." "%1$d × %2$d" + "Tìm kiếm ứng dụng" + "Đang tải ứng dụng..." + "Không tìm thấy ứng dụng nào phù hợp với \"%1$s\"" "Không còn chỗ trên Màn hình chính này." "Không còn chỗ trong khay Mục yêu thích" "Ứng dụng" @@ -69,5 +73,28 @@ "Tìm kiếm" "Ứng dụng này chưa được cài đặt" "Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công." - "Thêm vào không gian làm việc" + "Thêm vào màn hình chính" + "Di chuyển mục vào đây" + "Đã thêm mục vào màn hình chính" + "Đã xóa mục" + "Di chuyển mục" + "Di chuyển đến hàng %1$s cột %2$s" + "Di chuyển tới vị trí %1$s" + "Di chuyển tới vị trí mục yêu thích %1$s" + "Đã di chuyển mục" + "Thêm vào thư mục: %1$s" + "Thêm vào thư mục có %1$s" + "Đã thêm mục vào thư mục" + "Tạo thư mục bằng: %1$s" + "Đã tạo thư mục" + "Di chuyển đến màn hình chính" + "Di chuyển màn hình sang trái" + "Di chuyển màn hình sang phải" + "Đã di chuyển màn hình" + "Đổi kích thước" + "Tăng chiều rộng" + "Tăng chiều cao" + "Giảm chiều rộng" + "Giảm chiều cao" + "Đã đổi kích thước tiện ích thành chiều rộng %1$s chiều cao %2$s" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 9cd26b946..5a8cee998 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "Work" "未安装该应用。" "应用不可用" "安全模式下不允许使用下载的此应用" @@ -28,6 +29,9 @@ "显示内存空间" "触摸并按住小部件即可选择。" "%1$d × %2$d" + "搜索应用" + "正在加载应用…" + "未找到与“%1$s”相符的应用" "此主屏幕上已没有空间。" "收藏栏已满" "应用" @@ -69,5 +73,28 @@ "搜索" "未安装此应用" "未安装此图标对应的应用。您可以移除此图标,也可以尝试搜索相应的应用并手动安装。" - "添加至工作区" + "添加到主屏幕" + "将项目移至此处" + "已将项目添加到主屏幕" + "项目已移除" + "移动项目" + "移至第 %1$s 行第 %2$s 列" + "移至第 %1$s 个位置" + "移至收藏夹第 %1$s 个位置" + "已移动项目" + "添加到“%1$s”文件夹" + "添加到“%1$s”所在文件夹" + "项目已添加到文件夹" + "创建“%1$s”文件夹" + "文件夹已创建" + "移至主屏幕" + "将屏幕向左移动" + "将屏幕向右移动" + "屏幕已移动" + "调整大小" + "增加宽度" + "增加高度" + "减小宽度" + "减小高度" + "小部件尺寸已调整为:宽度 %1$s,高度 %2$s" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 052e8a0a1..89be2f626 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "工作" "尚未安裝應用程式。" "目前無法使用這個應用程式" "在安全模式中無法使用「已下載的應用程式」功能" @@ -28,6 +29,9 @@ "顯示記憶體" "輕觸並按住小工具即可選取。" "%1$d × %2$d" + "搜尋應用程式" + "正在載入應用程式…" + "無法找到與「%1$s」相符的應用程式" "主畫面已無空間。" "我的收藏寄存區沒有足夠空間" "應用程式" @@ -69,5 +73,28 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" - "加入工作區" + "新增至主畫面" + "移動項目至這裡" + "已將項目加入至主畫面" + "項目已移除" + "移動項目" + "移動至第 %1$s 行第 %2$s 列" + "移動至位置 %1$s" + "移動至喜愛的位置 %1$s" + "已移動項目" + "加入資料夾:%1$s" + "加入至資料夾:%1$s" + "項目已加入資料夾" + "使用以下項目建立資料夾:%1$s" + "已建立資料夾" + "移動至主畫面" + "向左移動螢幕" + "向右移動螢幕" + "已移動螢幕" + "重新調整大小" + "增加闊度" + "增加高度" + "減少闊度" + "減少高度" + "已調整小工具的大小至闊 %1$s%2$s" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index b6b5c4eb5..86357ab34 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Launcher3" + "公司" "應用程式未安裝。" "應用程式目前無法使用" "在安全模式中無法使用「已下載的應用程式」功能" @@ -28,6 +29,9 @@ "顯示記憶體" "輕觸並按住小工具即可選取。" "%1$d × %2$d" + "搜尋應用程式" + "正在載入應用程式…" + "找不到符合「%1$s」的應用程式" "這個主螢幕已無空間。" "「我的最愛」匣已無可用空間" "應用程式" @@ -69,5 +73,28 @@ "搜尋" "尚未安裝這個應用程式" "您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。" - "新增至工作區" + "新增至主畫面" + "將項目移至這裡" + "已將項目新增到主畫面" + "已移除項目" + "移動項目" + "移至第 %1$s 列第 %2$s 欄" + "移至位置 %1$s" + "移至收藏位置 %1$s" + "已移動項目" + "新增到「%1$s」資料夾" + "新增到「%1$s」所在資料夾" + "已將項目新增到資料夾" + "建立「%1$s」資料夾" + "已建立資料夾" + "移至主畫面" + "將畫面往左移" + "將畫面往右移" + "已移動畫面" + "調整大小" + "增加寬度" + "增加高度" + "減少寬度" + "減少高度" + "已將小工具的寬度和高度分別調整為 %1$s%2$s" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 2dbd72237..e5281a14e 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -21,6 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Isiqalisi3" + "Umsebenzi" "Uhlelo lokusebenza alufakiwe." "Uhlelo lokusebenza alutholakali" "Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile" @@ -28,6 +29,9 @@ "Bonisa i-Mem" "Thinta uphinde ubambe ukuze uphakamise iwijethi." "%1$d × %2$d" + "Sesha Izinhlelo Zokusebenza" + "Ilayisha izinhlelo zokusebenza..." + "Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"%1$s\"" "Asisekho isikhala kulesi sikrini Sasekhaya." "Asisekho isikhala kwitreyi lezintandokazi" "Izinhlelo zokusebenza" @@ -69,5 +73,28 @@ "Sesha" "Lolu hlelo lokusebenza alifakiwe" "Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela." - "Engeza kusikhala sokusebenza" + "Faka kusikrini sasekhaya" + "Hambisa into lapha" + "Into ingezwe kusikrini sasekhaya" + "Into isusiwe" + "Hambisa into" + "Hambisa kurowu engu-%1$s ikholomu engu-%2$s" + "Hambisa kusimo esingu-%1$s" + "Hambisa kusimo sezintandokazi esingu-%1$s" + "Into ihanjisiwe" + "Engeza kufolda: %1$s" + "Engeza kufolda nge-%1$s" + "Into ingeziwe kufolda" + "Dala ifolda nge-: %1$s" + "Ifolda idaliwe" + "Hambisa kusikrini sasekhaya" + "Hambisa isikrini kwesokunxele" + "Hambisa isikrini kwesokudla" + "Isikrini sihanjisiwe" + "Shintsha usayizi" + "Khuphula ububanzi" + "Khuphula ubude" + "Nciphisa ububanzi" + "Nciphisa ubude" + "Iwijethi inikezwe usayizi omusha ngobubanzi obungu-%1$s ubude obungu-%2$s" -- cgit v1.2.3 From eaf291b9feba4e332d258b6d53ff119c88dc0d39 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 17 Jun 2015 21:12:44 -0700 Subject: Widgets model should respect AppFilter. > This pattern is already used in AllAppsList > mBgWidgetsModel variable cleanup inside LauncherModel. b/21739736 Change-Id: I03a05064ce3c3e5effb09055af9f4d4de2811c95 --- src/com/android/launcher3/LauncherModel.java | 14 +++++----- src/com/android/launcher3/model/WidgetsModel.java | 32 ++++++++++++++++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index a132e919a..b148b06e0 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -242,6 +242,7 @@ public class LauncherModel extends BroadcastReceiver mApp = app; mBgAllAppsList = new AllAppsList(iconCache, appFilter); + mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter); mIconCache = iconCache; final Resources res = context.getResources(); @@ -3346,10 +3347,12 @@ public class LauncherModel extends BroadcastReceiver public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks, final boolean refresh) { - runOnWorkerThread(new Runnable(){ + runOnWorkerThread(new Runnable() { @Override public void run() { - final WidgetsModel model = createWidgetsModel(context, refresh); + updateWidgetsModel(context, refresh); + final WidgetsModel model = mBgWidgetsModel.clone(); + mHandler.post(new Runnable() { @Override public void run() { @@ -3359,7 +3362,6 @@ public class LauncherModel extends BroadcastReceiver } } }); - mBgWidgetsModel = model; // update the Widget entries inside DB on the worker thread. LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews( model.getRawList()); @@ -3372,15 +3374,13 @@ public class LauncherModel extends BroadcastReceiver * * @see #loadAndBindWidgetsAndShortcuts */ - @Thunk WidgetsModel createWidgetsModel(Context context, boolean refresh) { + @Thunk void updateWidgetsModel(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); - WidgetsModel model = new WidgetsModel(context); - model.addWidgetsAndShortcuts(widgetsAndShortcuts); - return model; + mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts); } @Thunk static boolean isPackageDisabled(Context context, String packageName, diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index 625d4d696..09a3242b5 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -1,10 +1,12 @@ package com.android.launcher3.model; +import android.content.ComponentName; import android.content.Context; import android.content.pm.ResolveInfo; import android.util.Log; +import com.android.launcher3.AppFilter; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; @@ -39,26 +41,32 @@ public class WidgetsModel { private final Comparator mWidgetAndShortcutNameComparator; private final Comparator mAppNameComparator; private final IconCache mIconCache; + private final AppFilter mAppFilter; private AlphabeticIndexCompat mIndexer; - public WidgetsModel(Context context) { + public WidgetsModel(Context context, IconCache iconCache, AppFilter appFilter) { mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); - mIconCache = LauncherAppState.getInstance().getIconCache(); + mIconCache = iconCache; + mAppFilter = appFilter; mIndexer = new AlphabeticIndexCompat(context); } private WidgetsModel(WidgetsModel model) { mPackageItemInfos = (ArrayList) model.mPackageItemInfos.clone(); mWidgetsList = (HashMap>) model.mWidgetsList.clone(); - // mRawList is not copied as should not be needed. + mRawList = (ArrayList) model.mRawList.clone(); mWidgetAndShortcutNameComparator = model.mWidgetAndShortcutNameComparator; mAppNameComparator = model.mAppNameComparator; mIconCache = model.mIconCache; + mAppFilter = model.mAppFilter; } // Access methods that may be deleted if the private fields are made package-private. public int getPackageSize() { + if (mPackageItemInfos == null) { + return 0; + } return mPackageItemInfos.size(); } @@ -78,7 +86,7 @@ public class WidgetsModel { return mRawList; } - public void addWidgetsAndShortcuts(ArrayList rawWidgetsShortcuts) { + public void setWidgetsAndShortcuts(ArrayList rawWidgetsShortcuts) { Utilities.assertWorkerThread(); mRawList = rawWidgetsShortcuts; if (DEBUG) { @@ -96,15 +104,27 @@ public class WidgetsModel { // add and update. for (Object o: rawWidgetsShortcuts) { String packageName = ""; + ComponentName componentName = null; if (o instanceof LauncherAppWidgetProviderInfo) { LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; + componentName = widgetInfo.provider; packageName = widgetInfo.provider.getPackageName(); } else if (o instanceof ResolveInfo) { ResolveInfo resolveInfo = (ResolveInfo) o; + componentName = new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); packageName = resolveInfo.activityInfo.packageName; - } else { - Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s", + } + + if (componentName == null) { + Log.e(TAG, String.format("Widget cannot be set for class=%s", o.getClass().toString())); + continue; + } + if (mAppFilter != null && !mAppFilter.shouldShowApp(componentName)) { + Log.d(TAG, String.format("%s is filtered and not added to the widget tray.", + packageName)); + continue; } PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); -- cgit v1.2.3 From 289ec3af9a1cd121a221235e0a27d4455e85efdf Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 17 Jun 2015 23:08:05 -0700 Subject: Remove DEBUG flag inside WidgetsListAdapter Change-Id: I7e5b3162f2b3768caff7ff79fd75567a793a1f45 --- src/com/android/launcher3/widget/WidgetsListAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index e82c0a631..d07c9553a 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -54,7 +54,7 @@ import java.util.List; public class WidgetsListAdapter extends Adapter { private static final String TAG = "WidgetsListAdapter"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private Launcher mLauncher; private LayoutInflater mLayoutInflater; -- cgit v1.2.3 From 6b1c73f50a99a215c923aa0caca8e63c593a4eb2 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 18 Jun 2015 11:38:42 -0700 Subject: Moving predicted apps callbacks to use ComponentKey. Bug: 21270185 Change-Id: If2d9dd77235e7a96de96a1a5441a589ef8a72a7f --- src/com/android/launcher3/Launcher.java | 5 ++-- src/com/android/launcher3/LauncherCallbacks.java | 3 +- src/com/android/launcher3/LauncherExtension.java | 3 +- .../launcher3/allapps/AllAppsContainerView.java | 2 +- .../launcher3/allapps/AlphabeticalAppsList.java | 32 +++++++++++++--------- src/com/android/launcher3/util/ComponentKey.java | 32 +++++++++++++++++++++- 6 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9989abb6d..c4187580e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -93,17 +93,16 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; - import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.allapps.AllAppsContainerView; -import com.android.launcher3.allapps.AllAppsSearchBarController; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -3436,7 +3435,7 @@ public class Launcher extends Activity */ private void tryAndUpdatePredictedApps() { if (mLauncherCallbacks != null) { - List apps = mLauncherCallbacks.getPredictedApps(); + List apps = mLauncherCallbacks.getPredictedApps(); if (!apps.isEmpty()) { mAppsView.setPredictedApps(apps); } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index e73275400..56db7747c 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -8,6 +8,7 @@ import android.view.Menu; import android.view.View; import android.view.ViewGroup; import com.android.launcher3.allapps.AllAppsSearchBarController; +import com.android.launcher3.util.ComponentKey; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -92,7 +93,7 @@ public interface LauncherCallbacks { public boolean overrideWallpaperDimensions(); public boolean isLauncherPreinstalled(); public AllAppsSearchBarController getAllAppsSearchBarController(); - public List getPredictedApps(); + public List getPredictedApps(); /** * Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup, diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java index fafb070ec..857ec57a7 100644 --- a/src/com/android/launcher3/LauncherExtension.java +++ b/src/com/android/launcher3/LauncherExtension.java @@ -12,6 +12,7 @@ import android.view.Menu; import android.view.View; import android.view.ViewGroup; import com.android.launcher3.allapps.AllAppsSearchBarController; +import com.android.launcher3.util.ComponentKey; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -253,7 +254,7 @@ public class LauncherExtension extends Launcher { } @Override - public List getPredictedApps() { + public List getPredictedApps() { return new ArrayList<>(); } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 0fbe8e962..d49a06b3a 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -207,7 +207,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc /** * Sets the current set of predicted apps. */ - public void setPredictedApps(List apps) { + public void setPredictedApps(List apps) { mApps.setPredictedApps(apps); } diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index b7b6ed7fc..aa73c74cf 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -15,14 +15,14 @@ */ package com.android.launcher3.allapps; -import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; - import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.model.AppNameComparator; import com.android.launcher3.util.ComponentKey; @@ -159,7 +159,7 @@ public class AlphabeticalAppsList { // The set of sections that we allow fast-scrolling to (includes non-merged sections) private List mFastScrollerSections = new ArrayList<>(); // The set of predicted app component names - private List mPredictedAppComponents = new ArrayList<>(); + private List mPredictedAppComponents = new ArrayList<>(); // The set of predicted apps resolved from the component names and the current set of apps private List mPredictedApps = new ArrayList<>(); // The of ordered component names as a result of a search query @@ -268,7 +268,7 @@ public class AlphabeticalAppsList { * 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. */ - public void setPredictedApps(List apps) { + public void setPredictedApps(List apps) { mPredictedAppComponents.clear(); mPredictedAppComponents.addAll(apps); onAppsUpdated(); @@ -386,21 +386,27 @@ public class AlphabeticalAppsList { if (DEBUG_PREDICTIONS) { if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) { - mPredictedAppComponents.add(mApps.get(0).componentName); - mPredictedAppComponents.add(mApps.get(0).componentName); - mPredictedAppComponents.add(mApps.get(0).componentName); - mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, + UserHandleCompat.myUserHandle())); + mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, + UserHandleCompat.myUserHandle())); + mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, + UserHandleCompat.myUserHandle())); + mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, + UserHandleCompat.myUserHandle())); } } // Process the predicted app components mPredictedApps.clear(); if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { - for (ComponentName cn : mPredictedAppComponents) { - for (AppInfo info : mApps) { - if (cn.equals(info.componentName)) { - mPredictedApps.add(info); - break; + for (ComponentKey ck : mPredictedAppComponents) { + AppInfo info = mComponentToAppMap.get(ck); + if (info != null) { + mPredictedApps.add(info); + } else { + if (LauncherAppState.isDogfoodBuild()) { + Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher)); } } // Stop at the number of predicted apps diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java index 0f17f009e..6a7df4318 100644 --- a/src/com/android/launcher3/util/ComponentKey.java +++ b/src/com/android/launcher3/util/ComponentKey.java @@ -17,8 +17,9 @@ package com.android.launcher3.util; */ import android.content.ComponentName; - +import android.content.Context; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; import java.util.Arrays; @@ -38,6 +39,35 @@ public class ComponentKey { } + /** + * Creates a new component key from an encoded component key string in the form of + * [flattenedComponentString#userId]. If the userId is not present, then it defaults + * to the current user. + */ + public ComponentKey(Context context, String componentKeyStr) { + int userDelimiterIndex = componentKeyStr.indexOf("#"); + if (userDelimiterIndex != -1) { + String componentStr = componentKeyStr.substring(0, userDelimiterIndex); + Long componentUser = Long.valueOf(componentKeyStr.substring(userDelimiterIndex + 1)); + componentName = ComponentName.unflattenFromString(componentStr); + user = UserManagerCompat.getInstance(context) + .getUserForSerialNumber(componentUser.longValue()); + } else { + // No user provided, default to the current user + componentName = ComponentName.unflattenFromString(componentKeyStr); + user = UserHandleCompat.myUserHandle(); + } + mHashCode = Arrays.hashCode(new Object[] {componentName, user}); + } + + /** + * Encodes a component key as a string of the form [flattenedComponentString#userId]. + */ + public String flattenToString(Context context) { + return componentName.flattenToString() + "#" + + UserManagerCompat.getInstance(context).getSerialNumberForUser(user); + } + @Override public int hashCode() { return mHashCode; -- cgit v1.2.3 From 230eade4a35d9dfe9b186da46e38ca05f0256a69 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 18 Jun 2015 12:48:51 -0700 Subject: Converting folder name to string before applying it to folder info > getText() returns Spannable which can contain text styling Change-Id: Iacdf9da47b54b5cd290a355152383d75bf3e6836 --- src/com/android/launcher3/Folder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 94f8fc875..85f58a1b1 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -301,14 +301,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setHint(sHintText); // Convert to a string here to ensure that no other state associated with the text field // gets saved. - CharSequence newTitle = mFolderName.getText(); + String newTitle = mFolderName.getText().toString(); mInfo.setTitle(newTitle); LauncherModel.updateItemInDatabase(mLauncher, mInfo); if (commit) { sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - String.format(getContext().getString(R.string.folder_renamed), - newTitle.toString())); + String.format(getContext().getString(R.string.folder_renamed), newTitle)); } // In order to clear the focus from the text field, we set the focus on ourself. This // ensures that every time the field is clicked, focus is gained, giving reliable behavior. -- cgit v1.2.3 From 2003c75ac43ecd66ee8a89142b7969feb993abe7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 18 Jun 2015 13:56:59 -0700 Subject: Cancelling launcher reload on mcc change Bug: 21022833 Change-Id: Iccd477e7b1638d162e1a500cdb7a52b8d359c657 --- src/com/android/launcher3/IconCache.java | 10 ++++------ src/com/android/launcher3/LauncherAppState.java | 1 - src/com/android/launcher3/LauncherModel.java | 19 ------------------- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 432b33c6b..91b242854 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -219,7 +219,7 @@ public class IconCache { // Remove all active icon update tasks. mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); - mIconDb.updateSystemStateString(mContext); + mIconDb.updateSystemStateString(); for (UserHandleCompat user : mUserManager.getUserProfiles()) { // Query for the set of apps final List apps = mLauncherApps.getActivityList(null, user); @@ -756,15 +756,13 @@ public class IconCache { public IconDB(Context context) { super(context, LauncherFiles.APP_ICONS_DB, null, DB_VERSION); - updateSystemStateString(context); + updateSystemStateString(); } - public void updateSystemStateString(Context c) { - mSystemState = Locale.getDefault().toString() + "," - + c.getResources().getConfiguration().mcc; + public void updateSystemStateString() { + mSystemState = Locale.getDefault().toString(); } - @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 0565d3f4b..83d90da97 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -95,7 +95,6 @@ public class LauncherAppState { // Register intent receivers IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); // For handling managed profiles diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d2112afb4..f09ad7599 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -30,8 +30,6 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; -import android.content.res.Configuration; -import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; @@ -177,8 +175,6 @@ public class LauncherModel extends BroadcastReceiver @Thunk IconCache mIconCache; - protected int mPreviousConfigMcc; - @Thunk final LauncherAppsCompat mLauncherApps; @Thunk final UserManagerCompat mUserManager; @@ -243,9 +239,6 @@ public class LauncherModel extends BroadcastReceiver mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter); mIconCache = iconCache; - final Resources res = context.getResources(); - Configuration config = res.getConfiguration(); - mPreviousConfigMcc = config.mcc; mLauncherApps = LauncherAppsCompat.getInstance(context); mUserManager = UserManagerCompat.getInstance(context); } @@ -1286,18 +1279,6 @@ public class LauncherModel extends BroadcastReceiver if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); - } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { - // Check if configuration change was an mcc/mnc change which would affect app resources - // and we would need to clear out the labels in all apps/workspace. Same handling as - // above for ACTION_LOCALE_CHANGED - Configuration currentConfig = context.getResources().getConfiguration(); - if (mPreviousConfigMcc != currentConfig.mcc) { - Log.d(TAG, "Reload apps on config change. curr_mcc:" - + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc); - forceReload(); - } - // Update previousConfig - mPreviousConfigMcc = currentConfig.mcc; } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) || SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) { Callbacks callbacks = getCallback(); -- cgit v1.2.3 From 78564d4e2ddb4f8aba481b6943c155e121b41a7f Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 18 Jun 2015 14:01:55 -0700 Subject: Updating ComponentKey for compatibility with build. Change-Id: I293177c0eff8c162c2d21bfc2aebf83926c62690 --- src/com/android/launcher3/util/ComponentKey.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java index 0f17f009e..7a7ed4a4f 100644 --- a/src/com/android/launcher3/util/ComponentKey.java +++ b/src/com/android/launcher3/util/ComponentKey.java @@ -17,7 +17,7 @@ package com.android.launcher3.util; */ import android.content.ComponentName; - +import android.content.Context; import com.android.launcher3.compat.UserHandleCompat; import java.util.Arrays; @@ -38,6 +38,11 @@ public class ComponentKey { } + public ComponentKey(Context context, String componentKeyStr) { + // Do nothing + throw new UnsupportedOperationException(); + } + @Override public int hashCode() { return mHashCode; -- cgit v1.2.3 From 60331a9be74a14051e6e192db69307ce652da2ae Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 18 Jun 2015 15:05:56 -0700 Subject: Revert resource name to what platform ui automator test defines. b/21923650 Change-Id: I26a9405ac0567f856a43d83886df4ece938c11a7 --- res/layout-land/launcher.xml | 2 ++ res/layout-port/launcher.xml | 2 ++ res/layout-sw720dp/launcher.xml | 5 +++-- res/layout/all_apps_container.xml | 3 ++- res/layout/widgets_view.xml | 1 + src/com/android/launcher3/allapps/AllAppsContainerView.java | 2 +- 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index 1c917bf09..6500ebcd2 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -34,6 +34,7 @@ android:layout_height="52dp" /> + + + + + + - + + Date: Fri, 19 Jun 2015 12:09:35 -0700 Subject: Add extras required for compilation -> Patching back from a future branch Change-Id: I5127a82560e39bf391fe083d0eb8932cf2473562 --- src/com/android/launcher3/Stats.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index a87986562..0a9807277 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -37,6 +37,22 @@ public class Stats { public static final String EXTRA_SCREEN = "screen"; public static final String EXTRA_CELLX = "cellX"; public static final String EXTRA_CELLY = "cellY"; + public static final String EXTRA_SOURCE = "source"; + + public static final String SOURCE_EXTRA_CONTAINER = "container"; + public static final String SOURCE_EXTRA_CONTAINER_PAGE = "container_page"; + public static final String SOURCE_EXTRA_SUB_CONTAINER = "sub_container"; + public static final String SOURCE_EXTRA_SUB_CONTAINER_PAGE = "sub_container_page"; + + public static final String CONTAINER_SEARCH_BOX = "search_box"; + public static final String CONTAINER_ALL_APPS = "all_apps"; + public static final String CONTAINER_HOMESCREEN = "homescreen"; // aka. Workspace + public static final String CONTAINER_HOTSEAT = "hotseat"; + + public static final String SUB_CONTAINER_FOLDER = "folder"; + public static final String SUB_CONTAINER_ALL_APPS_A_Z = "a-z"; + public static final String SUB_CONTAINER_ALL_APPS_PREDICTION = "prediction"; + public static final String SUB_CONTAINER_ALL_APPS_SEARCH = "search"; private static final int LOG_VERSION = 1; private static final int LOG_TAG_VERSION = 0x1; -- cgit v1.2.3 From 4d113a5ff44ff1a7f19263bde21581fbf9a54212 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 27 May 2015 10:05:28 -0700 Subject: Using material style overscroll effect for workspace and folders Bug: 21335369 Change-Id: I53cc6edfa87334b9326f1dedd90c3e2222beade5 --- res/drawable-hdpi/overscroll_glow_left.9.png | Bin 858 -> 0 bytes res/drawable-hdpi/overscroll_glow_right.9.png | Bin 856 -> 0 bytes res/drawable-mdpi/overscroll_glow_left.9.png | Bin 552 -> 0 bytes res/drawable-mdpi/overscroll_glow_right.9.png | Bin 525 -> 0 bytes res/drawable-xhdpi/overscroll_glow_left.9.png | Bin 1249 -> 0 bytes res/drawable-xhdpi/overscroll_glow_right.9.png | Bin 1248 -> 0 bytes res/drawable-xxhdpi/overscroll_glow_left.9.png | Bin 4562 -> 0 bytes res/drawable-xxhdpi/overscroll_glow_right.9.png | Bin 2738 -> 0 bytes res/values/colors.xml | 3 + src/com/android/launcher3/CellLayout.java | 33 -- src/com/android/launcher3/FolderPagedView.java | 14 +- src/com/android/launcher3/PagedView.java | 212 +++++------- src/com/android/launcher3/Workspace.java | 44 +-- .../android/launcher3/util/LauncherEdgeEffect.java | 365 +++++++++++++++++++++ .../launcher3/util/ManagedProfileHeuristic.java | 19 ++ 15 files changed, 488 insertions(+), 202 deletions(-) delete mode 100644 res/drawable-hdpi/overscroll_glow_left.9.png delete mode 100644 res/drawable-hdpi/overscroll_glow_right.9.png delete mode 100644 res/drawable-mdpi/overscroll_glow_left.9.png delete mode 100644 res/drawable-mdpi/overscroll_glow_right.9.png delete mode 100644 res/drawable-xhdpi/overscroll_glow_left.9.png delete mode 100644 res/drawable-xhdpi/overscroll_glow_right.9.png delete mode 100644 res/drawable-xxhdpi/overscroll_glow_left.9.png delete mode 100644 res/drawable-xxhdpi/overscroll_glow_right.9.png create mode 100644 src/com/android/launcher3/util/LauncherEdgeEffect.java diff --git a/res/drawable-hdpi/overscroll_glow_left.9.png b/res/drawable-hdpi/overscroll_glow_left.9.png deleted file mode 100644 index aaf43c73a..000000000 Binary files a/res/drawable-hdpi/overscroll_glow_left.9.png and /dev/null differ diff --git a/res/drawable-hdpi/overscroll_glow_right.9.png b/res/drawable-hdpi/overscroll_glow_right.9.png deleted file mode 100644 index d03486441..000000000 Binary files a/res/drawable-hdpi/overscroll_glow_right.9.png and /dev/null differ diff --git a/res/drawable-mdpi/overscroll_glow_left.9.png b/res/drawable-mdpi/overscroll_glow_left.9.png deleted file mode 100644 index b79cdcdc2..000000000 Binary files a/res/drawable-mdpi/overscroll_glow_left.9.png and /dev/null differ diff --git a/res/drawable-mdpi/overscroll_glow_right.9.png b/res/drawable-mdpi/overscroll_glow_right.9.png deleted file mode 100644 index 1321303e1..000000000 Binary files a/res/drawable-mdpi/overscroll_glow_right.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/overscroll_glow_left.9.png b/res/drawable-xhdpi/overscroll_glow_left.9.png deleted file mode 100644 index 4f248f70b..000000000 Binary files a/res/drawable-xhdpi/overscroll_glow_left.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/overscroll_glow_right.9.png b/res/drawable-xhdpi/overscroll_glow_right.9.png deleted file mode 100644 index 818a70db8..000000000 Binary files a/res/drawable-xhdpi/overscroll_glow_right.9.png and /dev/null differ diff --git a/res/drawable-xxhdpi/overscroll_glow_left.9.png b/res/drawable-xxhdpi/overscroll_glow_left.9.png deleted file mode 100644 index 1a895cdc8..000000000 Binary files a/res/drawable-xxhdpi/overscroll_glow_left.9.png and /dev/null differ diff --git a/res/drawable-xxhdpi/overscroll_glow_right.9.png b/res/drawable-xxhdpi/overscroll_glow_right.9.png deleted file mode 100644 index 576676145..000000000 Binary files a/res/drawable-xxhdpi/overscroll_glow_right.9.png and /dev/null differ diff --git a/res/values/colors.xml b/res/values/colors.xml index e2b8a2ea7..7d36101ef 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -29,6 +29,9 @@ #FFF + #FFFFFFFF + #FF757575 + #FF666666 #FFF5F5F5 #FF243036 diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index b5d0dca24..2cde3d53d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -100,17 +100,11 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private ArrayList mFolderOuterRings = new ArrayList(); private int[] mFolderLeaveBehindCell = {-1, -1}; - private static final float FOREGROUND_ALPHA_DAMPER = 0.65f; - private int mForegroundAlpha = 0; private float mBackgroundAlpha; private static final int BACKGROUND_ACTIVATE_DURATION = 120; private final TransitionDrawable mBackground; - private final Drawable mOverScrollLeft; - private final Drawable mOverScrollRight; - private Drawable mOverScrollForegroundDrawable; - // These values allow a fixed measurement to be set on the CellLayout. private int mFixedWidth = -1; private int mFixedHeight = -1; @@ -218,9 +212,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel); mBackground.setCallback(this); - mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left); - mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right); - mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx); @@ -396,19 +387,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return mDropPending; } - void setOverScrollAmount(float r, boolean left) { - if (left && mOverScrollForegroundDrawable != mOverScrollLeft) { - mOverScrollForegroundDrawable = mOverScrollLeft; - } else if (!left && mOverScrollForegroundDrawable != mOverScrollRight) { - mOverScrollForegroundDrawable = mOverScrollRight; - } - - r *= FOREGROUND_ALPHA_DAMPER; - mForegroundAlpha = (int) Math.round((r * 255)); - mOverScrollForegroundDrawable.setAlpha(mForegroundAlpha); - invalidate(); - } - @Override public void setPressedIcon(BubbleTextView icon, Bitmap background) { if (icon == null || background == null) { @@ -553,14 +531,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - if (mForegroundAlpha > 0) { - mOverScrollForegroundDrawable.draw(canvas); - } - } - public void showFolderAccept(FolderRingAnimator fra) { mFolderOuterRings.add(fra); } @@ -920,9 +890,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mBackground.getPadding(mTempRect); mBackground.setBounds(-mTempRect.left, -mTempRect.top, w + mTempRect.right, h + mTempRect.bottom); - - mOverScrollLeft.setBounds(0, 0, w, h); - mOverScrollRight.setBounds(0, 0, w, h); } @Override diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index b7a5aa8cf..f2ec1b68c 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -97,6 +97,8 @@ public class FolderPagedView extends PagedView { mIsRtl = Utilities.isRtl(getResources()); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + + setEdgeGlowColor(getResources().getColor(R.color.folder_edge_effect_color)); } public void setFolder(Folder folder) { @@ -459,16 +461,16 @@ public class FolderPagedView extends PagedView { ? -SCROLL_HINT_FRACTION : SCROLL_HINT_FRACTION; int hint = (int) (fraction * getWidth()); int scroll = getScrollForPage(getNextPage()) + hint; - int delta = scroll - mUnboundedScrollX; + int delta = scroll - getScrollX(); if (delta != 0) { mScroller.setInterpolator(new DecelerateInterpolator()); - mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, Folder.SCROLL_HINT_DURATION); + mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION); invalidate(); } } public void clearScrollHint() { - if (mUnboundedScrollX != getScrollForPage(getNextPage())) { + if (getScrollX() != getScrollForPage(getNextPage())) { snapToPage(getNextPage()); } } @@ -667,4 +669,10 @@ public class FolderPagedView extends PagedView { public int itemsPerPage() { return mMaxItemsPerPage; } + + @Override + protected void getEdgeVerticalPostion(int[] pos) { + pos[0] = 0; + pos[1] = getViewportHeight(); + } } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 3d00034cc..e3cdc29a0 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -47,7 +47,10 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; + +import com.android.launcher3.util.LauncherEdgeEffect; import com.android.launcher3.util.Thunk; + import java.util.ArrayList; /** @@ -63,13 +66,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private static final int MIN_LENGTH_FOR_FLING = 25; protected static final int PAGE_SNAP_ANIMATION_DURATION = 750; - protected static final int OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION = 350; protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; protected static final float NANOTIME_DIV = 1000000000.0f; - private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; - private static final float OVERSCROLL_DAMP_FACTOR = 0.07f; - private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; // The page is moved more than halfway, automatically move to the next page on touch up. private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f; @@ -82,10 +81,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private static final int MIN_SNAP_VELOCITY = 1500; private static final int MIN_FLING_VELOCITY = 250; - // We are disabling touch interaction of the widget region for factory ROM. - private static final boolean DISABLE_TOUCH_INTERACTION = false; - private static final boolean DISABLE_TOUCH_SIDE_PAGES = true; - public static final int INVALID_RESTORE_PAGE = -1001; private boolean mFreeScroll = false; @@ -153,15 +148,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mCellCountY = 0; protected boolean mCenterPagesVertically; protected boolean mAllowOverScroll = true; - protected int mUnboundedScrollX; protected int[] mTempVisiblePagesRange = new int[2]; protected boolean mForceDrawAllChildrenNextFrame; - // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise - // it is equal to the scaled overscroll position. We use a separate value so as to prevent - // the screens from continuing to translate beyond the normal bounds. - protected int mOverScrollX; - protected static final int INVALID_POINTER = -1; protected int mActivePointerId = INVALID_POINTER; @@ -192,7 +181,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private float mMinScale = 1f; private boolean mUseMinScale = false; protected View mDragView; - protected AnimatorSet mZoomInOutAnim; private Runnable mSidePageHoverRunnable; @Thunk int mSidePageHoverIndex = -1; // This variable's scope is only for the duration of startReordering() and endReordering() @@ -214,6 +202,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected final Rect mInsets = new Rect(); protected final boolean mIsRtl; + // Edge effect + private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect(); + private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect(); + public interface PageSwitchListener { void onPageSwitch(View newPage, int newPageIndex); } @@ -262,6 +254,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity); mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity); setOnHierarchyChangeListener(this); + setWillNotDraw(false); + } + + protected void setEdgeGlowColor(int color) { + mEdgeGlowLeft.setColor(color); + mEdgeGlowRight.setColor(color); } protected void setDefaultInterpolator(Interpolator interpolator) { @@ -560,7 +558,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public void scrollBy(int x, int y) { - scrollTo(mUnboundedScrollX + x, getScrollY() + y); + scrollTo(getScrollX() + x, getScrollY() + y); } @Override @@ -578,12 +576,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc x = Math.max(x, mFreeScrollMinScrollX); } - mUnboundedScrollX = x; - boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0); boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX); if (isXBeforeFirstPage) { - super.scrollTo(0, y); + super.scrollTo(mIsRtl ? mMaxScrollX : 0, y); if (mAllowOverScroll) { mWasInOverscroll = true; if (mIsRtl) { @@ -593,7 +589,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } } else if (isXAfterLastPage) { - super.scrollTo(mMaxScrollX, y); + super.scrollTo(mIsRtl ? 0 : mMaxScrollX, y); if (mAllowOverScroll) { mWasInOverscroll = true; if (mIsRtl) { @@ -607,7 +603,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc overScroll(0); mWasInOverscroll = false; } - mOverScrollX = x; super.scrollTo(x, y); } @@ -646,8 +641,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (mScroller.computeScrollOffset()) { // Don't bother scrolling if the page does not need to be moved if (getScrollX() != mScroller.getCurrX() - || getScrollY() != mScroller.getCurrY() - || mOverScrollX != mScroller.getCurrX()) { + || getScrollY() != mScroller.getCurrY()) { float scaleX = mFreeScroll ? getScaleX() : 1f; int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX)); scrollTo(scrollX, mScroller.getCurrY()); @@ -978,21 +972,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc requestLayout(); } - protected void screenScrolled(int screenCenter) { - boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; - - if (mFadeInAdjacentScreens && !isInOverscroll) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child != null) { - float scrollProgress = getScrollProgress(screenCenter, child, i); - float alpha = 1 - Math.abs(scrollProgress); - child.setAlpha(alpha); - } - } - invalidate(); - } - } + /** + * Called when the center screen changes during scrolling. + */ + protected void screenScrolled(int screenCenter) { } @Override public void onChildViewAdded(View parent, View child) { @@ -1129,9 +1112,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final int pageCount = getChildCount(); if (pageCount > 0) { int halfScreenSize = getViewportWidth() / 2; - // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. - // Otherwise it is equal to the scaled overscroll position. - int screenCenter = mOverScrollX + halfScreenSize; + int screenCenter = getScrollX() + halfScreenSize; if (screenCenter != mLastScreenCenter || mForceScreenScrolled) { // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can @@ -1171,6 +1152,47 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (getPageCount() > 0) { + if (!mEdgeGlowLeft.isFinished()) { + final int restoreCount = canvas.save(); + Rect display = mViewport; + canvas.translate(display.left, display.top); + canvas.rotate(270); + + getEdgeVerticalPostion(sTmpIntPoint); + canvas.translate(display.top - sTmpIntPoint[1], 0); + mEdgeGlowLeft.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width()); + if (mEdgeGlowLeft.draw(canvas)) { + postInvalidateOnAnimation(); + } + canvas.restoreToCount(restoreCount); + } + if (!mEdgeGlowRight.isFinished()) { + final int restoreCount = canvas.save(); + Rect display = mViewport; + canvas.translate(display.left + + display.width() * (getChildCount() - 1), display.top); + canvas.rotate(90); + + getEdgeVerticalPostion(sTmpIntPoint); + canvas.translate(sTmpIntPoint[0] - display.top, -display.width()); + mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width()); + if (mEdgeGlowRight.draw(canvas)) { + postInvalidateOnAnimation(); + } + canvas.restoreToCount(restoreCount); + } + } + } + + /** + * Returns the top and bottom position for the edge effect. + */ + protected abstract void getEdgeVerticalPostion(int[] pos); + @Override public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { int page = indexToPage(indexOfChild(child)); @@ -1303,10 +1325,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (DISABLE_TOUCH_INTERACTION) { - return false; - } - /* * This method JUST determines whether we want to intercept the motion. * If we return true, onTouchEvent will be called and we do the actual @@ -1383,19 +1401,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - // check if this can be the beginning of a tap on the side of the pages - // to scroll the current page - if (!DISABLE_TOUCH_SIDE_PAGES) { - if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { - if (getChildCount() > 0) { - if (hitsPreviousPage(x, y)) { - mTouchState = TOUCH_STATE_PREV_PAGE; - } else if (hitsNextPage(x, y)) { - mTouchState = TOUCH_STATE_NEXT_PAGE; - } - } - } - } break; } @@ -1515,49 +1520,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - // This curve determines how the effect of scrolling over the limits of the page dimishes - // as the user pulls further and further from the bounds - private float overScrollInfluenceCurve(float f) { - f -= 1.0f; - return f * f * f + 1.0f; - } - - protected float acceleratedOverFactor(float amount) { - int screenSize = getViewportWidth(); - - // We want to reach the max over scroll effect when the user has - // over scrolled half the size of the screen - float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize); - - if (f == 0) return 0; - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - return f; - } - protected void dampedOverScroll(float amount) { int screenSize = getViewportWidth(); - float f = (amount / screenSize); - - if (f == 0) return; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - - int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); - if (amount < 0) { - mOverScrollX = overScrollAmount; - super.scrollTo(mOverScrollX, getScrollY()); + if (f < 0) { + mEdgeGlowLeft.onPull(-f); + } else if (f > 0) { + mEdgeGlowRight.onPull(f); } else { - mOverScrollX = mMaxScrollX + overScrollAmount; - super.scrollTo(mOverScrollX, getScrollY()); + return; } invalidate(); } @@ -1566,14 +1537,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc dampedOverScroll(amount); } - protected float maxOverScroll() { - // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not - // exceed). Used to find out how much extra wallpaper we need for the over scroll effect - float f = 1.0f; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - return OVERSCROLL_DAMP_FACTOR * f; - } - public void enableFreeScroll() { setEnableFreeScroll(true); } @@ -1636,10 +1599,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Override public boolean onTouchEvent(MotionEvent ev) { - if (DISABLE_TOUCH_INTERACTION) { - return false; - } - super.onTouchEvent(ev); // Skip touch handling if there are no pages to swipe @@ -1912,6 +1871,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mCancelTap = false; mTouchState = TOUCH_STATE_REST; mActivePointerId = INVALID_POINTER; + mEdgeGlowLeft.onRelease(); + mEdgeGlowRight.onRelease(); } /** @@ -2020,20 +1981,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return minDistanceFromScreenCenterIndex; } - protected boolean isInOverScroll() { - return (mOverScrollX > mMaxScrollX || mOverScrollX < 0); - } - - protected int getPageSnapDuration() { - if (isInOverScroll()) { - return OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION; - } - return PAGE_SNAP_ANIMATION_DURATION; - - } - protected void snapToDestination() { - snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration()); + snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION); } private static class ScrollInterpolator implements Interpolator { @@ -2050,7 +1999,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // the screen has to travel, however, we don't want this duration to be effected in a // purely linear fashion. Instead, we use this method to moderate the effect that the distance // of travel has on the overall snap duration. - float distanceInfluenceForSnapDuration(float f) { + private float distanceInfluenceForSnapDuration(float f) { f -= 0.5f; // center the values about 0. f *= 0.3f * Math.PI / 2.0f; return (float) Math.sin(f); @@ -2061,13 +2010,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int halfScreenSize = getViewportWidth() / 2; final int newX = getScrollForPage(whichPage); - int delta = newX - mUnboundedScrollX; + int delta = newX - getScrollX(); int duration = 0; - if (Math.abs(velocity) < mMinFlingVelocity || isInOverScroll()) { + if (Math.abs(velocity) < mMinFlingVelocity) { // If the velocity is low enough, then treat this more as an automatic page advance // as opposed to an apparent physical response to flinging - snapToPage(whichPage, getPageSnapDuration()); + snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); return; } @@ -2091,11 +2040,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } public void snapToPage(int whichPage) { - snapToPage(whichPage, getPageSnapDuration()); + snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); } protected void snapToPageImmediately(int whichPage) { - snapToPage(whichPage, getPageSnapDuration(), true, null); + snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null); } protected void snapToPage(int whichPage, int duration) { @@ -2111,8 +2060,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc whichPage = validateNewPage(whichPage); int newX = getScrollForPage(whichPage); - final int sX = mUnboundedScrollX; - final int delta = newX - sX; + final int delta = newX - getScrollX(); snapToPage(whichPage, delta, duration, immediate, interpolator); } @@ -2149,7 +2097,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mScroller.setInterpolator(mDefaultInterpolator); } - mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); + mScroller.startScroll(getScrollX(), 0, delta, 0, duration); updatePageIndicator(); @@ -2221,7 +2169,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } // Animate the drag view back to the original position - void animateDragViewToOriginalPosition() { + private void animateDragViewToOriginalPosition() { if (mDragView != null) { AnimatorSet anim = new AnimatorSet(); anim.setDuration(REORDERING_DROP_REPOSITION_DURATION); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 76f872bae..fb0a54d3c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -197,7 +197,6 @@ public class Workspace extends PagedView private static final Rect sTempRect = new Rect(); private final int[] mTempXY = new int[2]; private int[] mTempVisiblePagesRange = new int[2]; - private boolean mOverscrollEffectSet; public static final int DRAG_BITMAP_PADDING = 2; private boolean mWorkspaceFadeInAdjacentScreens; @@ -257,8 +256,6 @@ public class Workspace extends PagedView private float mCurrentScale; private float mTransitionProgress; - float mOverScrollEffect = 0f; - @Thunk Runnable mDeferredAction; private boolean mDeferDropAfterUninstall; private boolean mUninstallSuccessful; @@ -443,6 +440,8 @@ public class Workspace extends PagedView // Set the wallpaper dimensions when Launcher starts up setWallpaperDimension(); + + setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color)); } private void setupLayoutTransition() { @@ -1260,9 +1259,6 @@ public class Workspace extends PagedView mLauncherOverlay.onScrollChange(progress, mIsRtl); } else if (shouldOverScroll) { dampedOverScroll(amount); - mOverScrollEffect = acceleratedOverFactor(amount); - } else { - mOverScrollEffect = 0; } if (shouldZeroOverlay) { @@ -1270,6 +1266,13 @@ public class Workspace extends PagedView } } + @Override + protected void getEdgeVerticalPostion(int[] pos) { + View child = getChildAt(getPageCount() - 1); + pos[0] = child.getTop(); + pos[1] = child.getBottom(); + } + @Override protected void notifyPageSwitchListener() { super.notifyPageSwitchListener(); @@ -1540,11 +1543,9 @@ public class Workspace extends PagedView } private void updatePageAlphaValues(int screenCenter) { - boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; if (mWorkspaceFadeInAdjacentScreens && !workspaceInModalState() && - !mIsSwitchingState && - !isInOverscroll) { + !mIsSwitchingState) { for (int i = numCustomPages(); i < getChildCount(); i++) { CellLayout child = (CellLayout) getChildAt(i); if (child != null) { @@ -1654,34 +1655,9 @@ public class Workspace extends PagedView @Override protected void screenScrolled(int screenCenter) { - super.screenScrolled(screenCenter); - updatePageAlphaValues(screenCenter); updateStateForCustomContent(screenCenter); enableHwLayersOnVisiblePages(); - - boolean shouldOverScroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; - - if (shouldOverScroll) { - int index = 0; - final int lowerIndex = 0; - final int upperIndex = getChildCount() - 1; - - final boolean isLeftPage = mOverScrollX < 0; - index = (!mIsRtl && isLeftPage) || (mIsRtl && !isLeftPage) ? lowerIndex : upperIndex; - - CellLayout cl = (CellLayout) getChildAt(index); - float effect = Math.abs(mOverScrollEffect); - cl.setOverScrollAmount(Math.abs(effect), isLeftPage); - - mOverscrollEffectSet = true; - } else { - if (mOverscrollEffectSet && getChildCount() > 0) { - mOverscrollEffectSet = false; - ((CellLayout) getChildAt(0)).setOverScrollAmount(0, false); - ((CellLayout) getChildAt(getChildCount() - 1)).setOverScrollAmount(0, false); - } - } } protected void onAttachedToWindow() { diff --git a/src/com/android/launcher3/util/LauncherEdgeEffect.java b/src/com/android/launcher3/util/LauncherEdgeEffect.java new file mode 100644 index 000000000..3e3b255ad --- /dev/null +++ b/src/com/android/launcher3/util/LauncherEdgeEffect.java @@ -0,0 +1,365 @@ +/* + * 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.util; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +/** + * This class differs from the framework {@link android.widget.EdgeEffect}: + * 1) It does not use PorterDuffXfermode + * 2) The width to radius factor is smaller (0.5 instead of 0.75) + */ +public class LauncherEdgeEffect { + + // Time it will take the effect to fully recede in ms + private static final int RECEDE_TIME = 600; + + // Time it will take before a pulled glow begins receding in ms + private static final int PULL_TIME = 167; + + // Time it will take in ms for a pulled glow to decay to partial strength before release + private static final int PULL_DECAY_TIME = 2000; + + private static final float MAX_ALPHA = 0.5f; + + private static final float MAX_GLOW_SCALE = 2.f; + + private static final float PULL_GLOW_BEGIN = 0.f; + + // Minimum velocity that will be absorbed + private static final int MIN_VELOCITY = 100; + // Maximum velocity, clamps at this value + private static final int MAX_VELOCITY = 10000; + + private static final float EPSILON = 0.001f; + + private static final double ANGLE = Math.PI / 6; + private static final float SIN = (float) Math.sin(ANGLE); + private static final float COS = (float) Math.cos(ANGLE); + + private float mGlowAlpha; + private float mGlowScaleY; + + private float mGlowAlphaStart; + private float mGlowAlphaFinish; + private float mGlowScaleYStart; + private float mGlowScaleYFinish; + + private long mStartTime; + private float mDuration; + + private final Interpolator mInterpolator; + + private static final int STATE_IDLE = 0; + private static final int STATE_PULL = 1; + private static final int STATE_ABSORB = 2; + private static final int STATE_RECEDE = 3; + private static final int STATE_PULL_DECAY = 4; + + private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f; + + private static final int VELOCITY_GLOW_FACTOR = 6; + + private int mState = STATE_IDLE; + + private float mPullDistance; + + private final Rect mBounds = new Rect(); + private final Paint mPaint = new Paint(); + private float mRadius; + private float mBaseGlowScale; + private float mDisplacement = 0.5f; + private float mTargetDisplacement = 0.5f; + + /** + * Construct a new EdgeEffect with a theme appropriate for the provided context. + */ + public LauncherEdgeEffect() { + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.FILL); + mInterpolator = new DecelerateInterpolator(); + } + + /** + * Set the size of this edge effect in pixels. + * + * @param width Effect width in pixels + * @param height Effect height in pixels + */ + public void setSize(int width, int height) { + final float r = width * 0.5f / SIN; + final float y = COS * r; + final float h = r - y; + final float or = height * 0.75f / SIN; + final float oy = COS * or; + final float oh = or - oy; + + mRadius = r; + mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f; + + mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h)); + } + + /** + * Reports if this EdgeEffect's animation is finished. If this method returns false + * after a call to {@link #draw(Canvas)} the host widget should schedule another + * drawing pass to continue the animation. + * + * @return true if animation is finished, false if drawing should continue on the next frame. + */ + public boolean isFinished() { + return mState == STATE_IDLE; + } + + /** + * Immediately finish the current animation. + * After this call {@link #isFinished()} will return true. + */ + public void finish() { + mState = STATE_IDLE; + } + + /** + * A view should call this when content is pulled away from an edge by the user. + * This will update the state of the current visual effect and its associated animation. + * The host view should always {@link android.view.View#invalidate()} after this + * and draw the results accordingly. + * + *

Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement + * of the pull point is known.

+ * + * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to + * 1.f (full length of the view) or negative values to express change + * back toward the edge reached to initiate the effect. + */ + public void onPull(float deltaDistance) { + onPull(deltaDistance, 0.5f); + } + + /** + * A view should call this when content is pulled away from an edge by the user. + * This will update the state of the current visual effect and its associated animation. + * The host view should always {@link android.view.View#invalidate()} after this + * and draw the results accordingly. + * + * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to + * 1.f (full length of the view) or negative values to express change + * back toward the edge reached to initiate the effect. + * @param displacement The displacement from the starting side of the effect of the point + * initiating the pull. In the case of touch this is the finger position. + * Values may be from 0-1. + */ + public void onPull(float deltaDistance, float displacement) { + final long now = AnimationUtils.currentAnimationTimeMillis(); + mTargetDisplacement = displacement; + if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) { + return; + } + if (mState != STATE_PULL) { + mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY); + } + mState = STATE_PULL; + + mStartTime = now; + mDuration = PULL_TIME; + + mPullDistance += deltaDistance; + + final float absdd = Math.abs(deltaDistance); + mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA, + mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); + + if (mPullDistance == 0) { + mGlowScaleY = mGlowScaleYStart = 0; + } else { + final float scale = (float) (Math.max(0, 1 - 1 / + Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d); + + mGlowScaleY = mGlowScaleYStart = scale; + } + + mGlowAlphaFinish = mGlowAlpha; + mGlowScaleYFinish = mGlowScaleY; + } + + /** + * Call when the object is released after being pulled. + * This will begin the "decay" phase of the effect. After calling this method + * the host view should {@link android.view.View#invalidate()} and thereby + * draw the results accordingly. + */ + public void onRelease() { + mPullDistance = 0; + + if (mState != STATE_PULL && mState != STATE_PULL_DECAY) { + return; + } + + mState = STATE_RECEDE; + mGlowAlphaStart = mGlowAlpha; + mGlowScaleYStart = mGlowScaleY; + + mGlowAlphaFinish = 0.f; + mGlowScaleYFinish = 0.f; + + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mDuration = RECEDE_TIME; + } + + /** + * Call when the effect absorbs an impact at the given velocity. + * Used when a fling reaches the scroll boundary. + * + *

When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller}, + * the method getCurrVelocity will provide a reasonable approximation + * to use here.

+ * + * @param velocity Velocity at impact in pixels per second. + */ + public void onAbsorb(int velocity) { + mState = STATE_ABSORB; + velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY); + + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mDuration = 0.15f + (velocity * 0.02f); + + // The glow depends more on the velocity, and therefore starts out + // nearly invisible. + mGlowAlphaStart = 0.3f; + mGlowScaleYStart = Math.max(mGlowScaleY, 0.f); + + + // Growth for the size of the glow should be quadratic to properly + // respond + // to a user's scrolling speed. The faster the scrolling speed, the more + // intense the effect should be for both the size and the saturation. + mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f); + // Alpha should change for the glow as well as size. + mGlowAlphaFinish = Math.max( + mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA)); + mTargetDisplacement = 0.5f; + } + + /** + * Set the color of this edge effect in argb. + * + * @param color Color in argb + */ + public void setColor(int color) { + mPaint.setColor(color); + } + + /** + * Return the color of this edge effect in argb. + * @return The color of this edge effect in argb + */ + public int getColor() { + return mPaint.getColor(); + } + + /** + * Draw into the provided canvas. Assumes that the canvas has been rotated + * accordingly and the size has been set. The effect will be drawn the full + * width of X=0 to X=width, beginning from Y=0 and extending to some factor < + * 1.f of height. + * + * @param canvas Canvas to draw into + * @return true if drawing should continue beyond this frame to continue the + * animation + */ + public boolean draw(Canvas canvas) { + update(); + + final float centerX = mBounds.centerX(); + final float centerY = mBounds.height() - mRadius; + + canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0); + + final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f; + float translateX = mBounds.width() * displacement / 2; + mPaint.setAlpha((int) (0xff * mGlowAlpha)); + canvas.drawCircle(centerX + translateX, centerY, mRadius, mPaint); + + boolean oneLastFrame = false; + if (mState == STATE_RECEDE && mGlowScaleY == 0) { + mState = STATE_IDLE; + oneLastFrame = true; + } + + return mState != STATE_IDLE || oneLastFrame; + } + + /** + * Return the maximum height that the edge effect will be drawn at given the original + * {@link #setSize(int, int) input size}. + * @return The maximum height of the edge effect + */ + public int getMaxHeight() { + return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f); + } + + private void update() { + final long time = AnimationUtils.currentAnimationTimeMillis(); + final float t = Math.min((time - mStartTime) / mDuration, 1.f); + + final float interp = mInterpolator.getInterpolation(t); + + mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp; + mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp; + mDisplacement = (mDisplacement + mTargetDisplacement) / 2; + + if (t >= 1.f - EPSILON) { + switch (mState) { + case STATE_ABSORB: + mState = STATE_RECEDE; + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mDuration = RECEDE_TIME; + + mGlowAlphaStart = mGlowAlpha; + mGlowScaleYStart = mGlowScaleY; + + // After absorb, the glow should fade to nothing. + mGlowAlphaFinish = 0.f; + mGlowScaleYFinish = 0.f; + break; + case STATE_PULL: + mState = STATE_PULL_DECAY; + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mDuration = PULL_DECAY_TIME; + + mGlowAlphaStart = mGlowAlpha; + mGlowScaleYStart = mGlowScaleY; + + // After pull, the glow should fade to nothing. + mGlowAlphaFinish = 0.f; + mGlowScaleYFinish = 0.f; + break; + case STATE_PULL_DECAY: + mState = STATE_RECEDE; + break; + case STATE_RECEDE: + mState = STATE_IDLE; + break; + } + } + } +} diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java index 94dc47f09..b37f44719 100644 --- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java +++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java @@ -1,10 +1,28 @@ +/* + * 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.util; +import android.annotation.TargetApi; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Build; import android.util.Log; import com.android.launcher3.FolderInfo; @@ -32,6 +50,7 @@ import java.util.Set; * Handles addition of app shortcuts for managed profiles. * Methods of class should only be called on {@link LauncherModel#sWorkerThread}. */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class ManagedProfileHeuristic { private static final String TAG = "ManagedProfileHeuristic"; -- cgit v1.2.3 From 5796b03f6d526ee775fa48f175ceee0e92200b45 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 19 Jun 2015 18:03:35 -0700 Subject: Add workspace scrim animation to the state transition to reduce jankiness. b/20956087 Note: Will work on creating a separate view for the scrim animation rather than the drag layer on a separate CL. Change-Id: Idaced5c6867abe3ed9afa984c885023676b3a141 --- .../WorkspaceStateTransitionAnimation.java | 26 ++++++++-------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 13e4a59f1..399ef9755 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -192,8 +192,7 @@ public class WorkspaceStateTransitionAnimation { @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); - // These properties refer to the background protection gradient used for AllApps and Customize - @Thunk ValueAnimator mBackgroundFadeInAnimation; + // These properties refer to the background protection gradient used for AllApps and Widget tray. @Thunk ValueAnimator mBackgroundFadeOutAnimation; @Thunk float mSpringLoadedShrinkFactor; @@ -232,6 +231,7 @@ public class WorkspaceStateTransitionAnimation { accessibilityEnabled); animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews, accessibilityEnabled); + animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION); return mStateAnimator; } @@ -473,12 +473,6 @@ public class WorkspaceStateTransitionAnimation { AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } } - - if (states.stateIsNormal) { - animateBackgroundGradient(0f, animated); - } else { - animateBackgroundGradient(mWorkspaceScrimAlpha, animated); - } } /** @@ -548,19 +542,17 @@ public class WorkspaceStateTransitionAnimation { } /** - * Animates the background scrim. - * TODO(winsonc): Is there a better place for this? + * Animates the background scrim. Add to the state animator to prevent jankiness. * * @param finalAlpha the final alpha for the background scrim * @param animated whether or not to set the background alpha immediately + * @duration duration of the animation */ - private void animateBackgroundGradient(float finalAlpha, boolean animated) { - // Cancel any running background animations - cancelAnimator(mBackgroundFadeInAnimation); - cancelAnimator(mBackgroundFadeOutAnimation); - + private void animateBackgroundGradient(TransitionStates states, boolean animated, int duration) { final DragLayer dragLayer = mLauncher.getDragLayer(); final float startAlpha = dragLayer.getBackgroundAlpha(); + float finalAlpha = states.stateIsNormal ? 0 : mWorkspaceScrimAlpha; + if (finalAlpha != startAlpha) { if (animated) { mBackgroundFadeOutAnimation = @@ -573,8 +565,8 @@ public class WorkspaceStateTransitionAnimation { } }); mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); - mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); - mBackgroundFadeOutAnimation.start(); + mBackgroundFadeOutAnimation.setDuration(duration); + mStateAnimator.play(mBackgroundFadeOutAnimation); } else { dragLayer.setBackgroundAlpha(finalAlpha); } -- cgit v1.2.3 From 2fb2f0bcfcd25eeaf423660fa30be7a2d4ace5b7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 22 Jun 2015 13:57:26 -0700 Subject: Making the provider column names public Change-Id: Idc85e34aceaaed5a1abb693a8a04d06f9c70b4b4 --- src/com/android/launcher3/LauncherSettings.java | 47 ++++++++++++++----------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index afdb3dd00..f2c85a195 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -31,7 +31,7 @@ public class LauncherSettings { * The time of the last update to this row. *

Type: INTEGER

*/ - static final String MODIFIED = "modified"; + public static final String MODIFIED = "modified"; } static interface BaseLauncherColumns extends ChangeLogColumns { @@ -39,7 +39,7 @@ public class LauncherSettings { * Descriptive name of the gesture that can be displayed to the user. *

Type: TEXT

*/ - static final String TITLE = "title"; + public static final String TITLE = "title"; /** * The Intent URL of the gesture, describing what it points to. This @@ -54,51 +54,51 @@ public class LauncherSettings { * *

Type: INTEGER

*/ - static final String ITEM_TYPE = "itemType"; + public static final String ITEM_TYPE = "itemType"; /** * The gesture is an application */ - static final int ITEM_TYPE_APPLICATION = 0; + public static final int ITEM_TYPE_APPLICATION = 0; /** * The gesture is an application created shortcut */ - static final int ITEM_TYPE_SHORTCUT = 1; + public static final int ITEM_TYPE_SHORTCUT = 1; /** * The icon type. *

Type: INTEGER

*/ - static final String ICON_TYPE = "iconType"; + public static final String ICON_TYPE = "iconType"; /** * The icon is a resource identified by a package name and an integer id. */ - static final int ICON_TYPE_RESOURCE = 0; + public static final int ICON_TYPE_RESOURCE = 0; /** * The icon is a bitmap. */ - static final int ICON_TYPE_BITMAP = 1; + public static final int ICON_TYPE_BITMAP = 1; /** * The icon package name, if icon type is ICON_TYPE_RESOURCE. *

Type: TEXT

*/ - static final String ICON_PACKAGE = "iconPackage"; + public static final String ICON_PACKAGE = "iconPackage"; /** * The icon resource id, if icon type is ICON_TYPE_RESOURCE. *

Type: TEXT

*/ - static final String ICON_RESOURCE = "iconResource"; + public static final String ICON_RESOURCE = "iconResource"; /** * The custom icon bitmap, if icon type is ICON_TYPE_BITMAP. *

Type: BLOB

*/ - static final String ICON = "icon"; + public static final String ICON = "icon"; } /** @@ -179,26 +179,26 @@ public class LauncherSettings { * (if container is CONTAINER_HOTSEAT or CONTAINER_HOTSEAT) *

Type: INTEGER

*/ - static final String CELLX = "cellX"; + public static final String CELLX = "cellX"; /** * The Y coordinate of the cell holding the favorite * (if container is CONTAINER_DESKTOP) *

Type: INTEGER

*/ - static final String CELLY = "cellY"; + public static final String CELLY = "cellY"; /** * The X span of the cell holding the favorite *

Type: INTEGER

*/ - static final String SPANX = "spanX"; + public static final String SPANX = "spanX"; /** * The Y span of the cell holding the favorite *

Type: INTEGER

*/ - static final String SPANY = "spanY"; + public static final String SPANY = "spanY"; /** * The profile id of the item in the cell. @@ -206,12 +206,12 @@ public class LauncherSettings { * Type: INTEGER *

*/ - static final String PROFILE_ID = "profileId"; + public static final String PROFILE_ID = "profileId"; /** * The favorite is a user created folder */ - static final int ITEM_TYPE_FOLDER = 2; + public static final int ITEM_TYPE_FOLDER = 2; /** * The favorite is a live folder @@ -220,6 +220,7 @@ public class LauncherSettings { * exist within the launcher database will be ignored when loading. That said, these * entries in the database may still exist, and are not automatically stripped. */ + @Deprecated static final int ITEM_TYPE_LIVE_FOLDER = 3; /** @@ -235,16 +236,19 @@ public class LauncherSettings { /** * The favorite is a clock */ + @Deprecated static final int ITEM_TYPE_WIDGET_CLOCK = 1000; /** * The favorite is a search widget */ + @Deprecated static final int ITEM_TYPE_WIDGET_SEARCH = 1001; /** * The favorite is a photo frame */ + @Deprecated static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002; /** @@ -252,7 +256,7 @@ public class LauncherSettings { * *

Type: INTEGER

*/ - static final String APPWIDGET_ID = "appWidgetId"; + public static final String APPWIDGET_ID = "appWidgetId"; /** * The ComponentName of the widget provider @@ -275,6 +279,7 @@ public class LauncherSettings { * live folders to find the content provider. *

Type: TEXT

*/ + @Deprecated static final String URI = "uri"; /** @@ -291,19 +296,19 @@ public class LauncherSettings { * Boolean indicating that his item was restored and not yet successfully bound. *

Type: INTEGER

*/ - static final String RESTORED = "restored"; + public static final String RESTORED = "restored"; /** * Indicates the position of the item inside an auto-arranged view like folder or hotseat. *

Type: INTEGER

*/ - static final String RANK = "rank"; + public static final String RANK = "rank"; /** * Stores general flag based options for {@link ItemInfo}s. *

Type: INTEGER

*/ - static final String OPTIONS = "options"; + public static final String OPTIONS = "options"; } /** -- cgit v1.2.3 From 5e41a27fdb661199525614ceaaa3cfd8c4a6c490 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 22 Jun 2015 15:11:31 -0700 Subject: Fixing paged view overscroll effect being drawn outside the screen bounds when in transposed layout. Change-Id: I93be6a003b7d28708697990cfae8c7be96f84676 --- src/com/android/launcher3/PagedView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index e3cdc29a0..873c0684e 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1173,8 +1173,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (!mEdgeGlowRight.isFinished()) { final int restoreCount = canvas.save(); Rect display = mViewport; - canvas.translate(display.left + - display.width() * (getChildCount() - 1), display.top); + canvas.translate(display.left + mPageScrolls[getChildCount() - 1], display.top); canvas.rotate(90); getEdgeVerticalPostion(sTmpIntPoint); -- cgit v1.2.3 From b1777447d9b9700b48f8060f8b318f2363c43e8d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 16 Jun 2015 13:35:04 -0700 Subject: Refactoring fast scroller. - Fixing issue with fast scroller not fitting name width. - Refactoring fast scrolling/scroll bar code out of base recycler view - Adding animations to fast scroller to match design - Smooth scrolling when jumping between app rows - Fixing issue with fast scroller jumping when you first pick it up - Fixing issue with wrong background paddings being used Bug: 21874346 Bug: 22031923 Change-Id: I9f011b1f375751f437604b900e95a2942d3f4601 --- proguard.flags | 48 ++-- res/drawable-ldrtl/all_apps_fastscroll_bg.xml | 2 +- res/drawable/all_apps_fastscroll_bg.xml | 27 -- res/drawable/all_apps_scrollbar_thumb.xml | 21 -- res/drawable/container_fastscroll_popup_bg.xml | 27 ++ res/layout/all_apps_container.xml | 4 +- res/layout/all_apps_search_bar.xml | 4 +- res/values-sw720dp/dimens.xml | 2 +- res/values/colors.xml | 6 +- res/values/dimens.xml | 24 +- src/com/android/launcher3/BaseRecyclerView.java | 289 +++++++-------------- .../launcher3/BaseRecyclerViewFastScrollBar.java | 232 +++++++++++++++++ .../launcher3/BaseRecyclerViewFastScrollPopup.java | 160 ++++++++++++ src/com/android/launcher3/BubbleTextView.java | 83 +++++- src/com/android/launcher3/Launcher.java | 21 -- src/com/android/launcher3/LauncherAppState.java | 7 - src/com/android/launcher3/LauncherCallbacks.java | 2 + src/com/android/launcher3/Utilities.java | 19 ++ .../launcher3/allapps/AllAppsContainerView.java | 32 ++- .../launcher3/allapps/AllAppsGridAdapter.java | 2 +- .../launcher3/allapps/AllAppsRecyclerView.java | 248 ++++++++++-------- .../launcher3/allapps/AlphabeticalAppsList.java | 76 +++++- .../launcher3/widget/WidgetsContainerView.java | 4 +- .../launcher3/widget/WidgetsRecyclerView.java | 65 +++-- 24 files changed, 928 insertions(+), 477 deletions(-) delete mode 100644 res/drawable/all_apps_fastscroll_bg.xml delete mode 100644 res/drawable/all_apps_scrollbar_thumb.xml create mode 100644 res/drawable/container_fastscroll_popup_bg.xml create mode 100644 src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java create mode 100644 src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java diff --git a/proguard.flags b/proguard.flags index 7ec488b2d..6a9d6f345 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,9 +1,30 @@ +-keep class com.android.launcher3.BaseRecyclerViewFastScrollBar { + public void setWidth(int); + public int getWidth(); + public void setTrackAlpha(int); + public int getTrackAlpha(); +} + +-keep class com.android.launcher3.BaseRecyclerViewFastScrollPopup { + public void setAlpha(float); + public float getAlpha(); +} + +-keep class com.android.launcher3.BubbleTextView { + public void setFastScrollFocus(float); + public float getFastScrollFocus(); +} + +-keep class com.android.launcher3.ButtonDropTarget { + public int getTextColor(); +} + -keep class com.android.launcher3.CellLayout { public float getBackgroundAlpha(); public void setBackgroundAlpha(float); } --keep class com.android.launcher3.DragLayer$LayoutParams { +-keep class com.android.launcher3.CellLayout$LayoutParams { public void setWidth(int); public int getWidth(); public void setHeight(int); @@ -14,7 +35,7 @@ public int getY(); } --keep class com.android.launcher3.CellLayout$LayoutParams { +-keep class com.android.launcher3.DragLayer$LayoutParams { public void setWidth(int); public int getWidth(); public void setHeight(int); @@ -25,9 +46,9 @@ public int getY(); } --keep class com.android.launcher3.Workspace { - public float getBackgroundAlpha(); - public void setBackgroundAlpha(float); +-keep class com.android.launcher3.FastBitmapDrawable { + public int getBrightness(); + public void setBrightness(int); } -keep class com.android.launcher3.MemoryDumpActivity { @@ -39,16 +60,7 @@ public void setAnimationProgress(float); } --keep class com.android.launcher3.FastBitmapDrawable { - public int getBrightness(); - public void setBrightness(int); -} - --keep class com.android.launcher3.BaseRecyclerView { - public void setFastScrollerAlpha(float); - public float getFastScrollerAlpha(); -} - --keep class com.android.launcher3.ButtonDropTarget { - public int getTextColor(); -} +-keep class com.android.launcher3.Workspace { + public float getBackgroundAlpha(); + public void setBackgroundAlpha(float); +} \ No newline at end of file diff --git a/res/drawable-ldrtl/all_apps_fastscroll_bg.xml b/res/drawable-ldrtl/all_apps_fastscroll_bg.xml index 4777f70bb..d79096807 100644 --- a/res/drawable-ldrtl/all_apps_fastscroll_bg.xml +++ b/res/drawable-ldrtl/all_apps_fastscroll_bg.xml @@ -16,7 +16,7 @@ --> - + diff --git a/res/drawable/all_apps_fastscroll_bg.xml b/res/drawable/all_apps_fastscroll_bg.xml deleted file mode 100644 index 6b7448459..000000000 --- a/res/drawable/all_apps_fastscroll_bg.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/drawable/all_apps_scrollbar_thumb.xml b/res/drawable/all_apps_scrollbar_thumb.xml deleted file mode 100644 index 649a963b1..000000000 --- a/res/drawable/all_apps_scrollbar_thumb.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/drawable/container_fastscroll_popup_bg.xml b/res/drawable/container_fastscroll_popup_bg.xml new file mode 100644 index 000000000..2ef07ab96 --- /dev/null +++ b/res/drawable/container_fastscroll_popup_bg.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml index 1de82019c..0b624e6a5 100644 --- a/res/layout/all_apps_container.xml +++ b/res/layout/all_apps_container.xml @@ -38,8 +38,8 @@ android:id="@+id/prediction_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/all_apps_prediction_bar_top_bottom_padding" - android:paddingBottom="@dimen/all_apps_prediction_bar_top_bottom_padding" + android:paddingTop="@dimen/all_apps_prediction_bar_top_padding" + android:paddingBottom="@dimen/all_apps_prediction_bar_bottom_padding" android:orientation="horizontal" android:focusable="true" android:descendantFocusability="afterDescendants" diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml index 8d75b15a6..cf30eac36 100644 --- a/res/layout/all_apps_search_bar.xml +++ b/res/layout/all_apps_search_bar.xml @@ -63,8 +63,8 @@ android:layout_width="wrap_content" android:layout_height="@dimen/all_apps_search_bar_height" android:layout_gravity="end|center_vertical" - android:layout_marginEnd="6dp" - android:layout_marginRight="6dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" android:contentDescription="@string/all_apps_search_bar_hint" android:paddingBottom="13dp" android:paddingTop="13dp" diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index 9d1e3529c..d48f9eed0 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -17,7 +17,7 @@ 54dp - 16dp + 14dp 8dip diff --git a/res/values/colors.xml b/res/values/colors.xml index 7d36101ef..5afc5b98d 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -39,11 +39,15 @@ #FFFFFFFF #FF374248 + + #42000000 + #009688 + - #009688 #009688 + #42FFFFFF #FFFFFF #C4C4C4 #263238 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index da56d9049..122b831b4 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -49,12 +49,20 @@ 4dip 12dip - + 8dp 4dp + 4dp + 8dp + 64dp + -24dp + 72dp + 48dp + + 0dp 8dp 24sp @@ -62,16 +70,10 @@ 8dp 8dp 24dp - 16dp - - 4dp - -24dp - 72dp - 48dp - - 4dp - 16dp - 6dp + + 0dp + 16dp + 8dp 8dp diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 140c28c0c..0fae427e8 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -16,24 +16,15 @@ package com.android.launcher3; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; - import com.android.launcher3.util.Thunk; + /** * A base {@link RecyclerView}, which does the following: *
    @@ -41,7 +32,7 @@ import com.android.launcher3.util.Thunk; *
  • Enable fast scroller. *
*/ -public class BaseRecyclerView extends RecyclerView +public abstract class BaseRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { private static final int SCROLL_DELTA_THRESHOLD_DP = 4; @@ -50,14 +41,8 @@ public class BaseRecyclerView extends RecyclerView @Thunk int mDy = 0; private float mDeltaThreshold; - // - // Keeps track of variables required for the second function of this class: fast scroller. - // - - private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - /** - * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() + * The current scroll state of the recycler view. We use this in onUpdateScrollbar() * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so * that we can calculate what the scroll bar looks like, and where to jump to from the fast * scroller. @@ -70,27 +55,12 @@ public class BaseRecyclerView extends RecyclerView // The height of a given row (they are currently all the same height) public int rowHeight; } - // Should be maintained inside overriden method #updateVerticalScrollbarBounds - public ScrollPositionState scrollPosState = new ScrollPositionState(); - public Rect verticalScrollbarBounds = new Rect(); - private boolean mDraggingFastScroller; - - private Drawable mScrollbar; - private Drawable mFastScrollerBg; - private Rect mTmpFastScrollerInvalidateRect = new Rect(); - private Rect mFastScrollerBounds = new Rect(); - - private String mFastScrollSectionName; - private Paint mFastScrollTextPaint; - private Rect mFastScrollTextBounds = new Rect(); - private float mFastScrollAlpha; + protected BaseRecyclerViewFastScrollBar mScrollbar; private int mDownX; private int mDownY; private int mLastY; - private int mScrollbarWidth; - private int mScrollbarInset; protected Rect mBackgroundPadding = new Rect(); public BaseRecyclerView(Context context) { @@ -104,25 +74,10 @@ public class BaseRecyclerView extends RecyclerView public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP; + mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources()); ScrollListener listener = new ScrollListener(); setOnScrollListener(listener); - - Resources res = context.getResources(); - int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size); - mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb); - mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg); - mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); - mFastScrollTextPaint = new Paint(); - mFastScrollTextPaint.setColor(Color.WHITE); - mFastScrollTextPaint.setAntiAlias(true); - mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( - R.dimen.all_apps_fast_scroll_text_size)); - mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); - mScrollbarInset = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); - setFastScrollerAlpha(mFastScrollAlpha); - setOverScrollMode(View.OVER_SCROLL_NEVER); } private class ScrollListener extends OnScrollListener { @@ -133,6 +88,10 @@ public class BaseRecyclerView extends RecyclerView @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { mDy = dy; + + // TODO(winsonc): If we want to animate the section heads while scrolling, we can + // initiate that here if the recycler view scroll state is not + // RecyclerView.SCROLL_STATE_IDLE. } } @@ -161,8 +120,6 @@ public class BaseRecyclerView extends RecyclerView * it is already showing). */ private boolean handleTouchEvent(MotionEvent ev) { - ViewConfiguration config = ViewConfiguration.get(getContext()); - int action = ev.getAction(); int x = (int) ev.getX(); int y = (int) ev.getY(); @@ -174,41 +131,19 @@ public class BaseRecyclerView extends RecyclerView if (shouldStopScroll(ev)) { stopScroll(); } + mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); break; case MotionEvent.ACTION_MOVE: - // Check if we are scrolling - if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && - Math.abs(y - mDownY) > config.getScaledTouchSlop()) { - getParent().requestDisallowInterceptTouchEvent(true); - mDraggingFastScroller = true; - animateFastScrollerVisibility(true); - } - if (mDraggingFastScroller) { - mLastY = y; - - // Scroll to the right position, and update the section name - int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); - int bottom = getHeight() - getPaddingBottom() - - (mFastScrollerBg.getBounds().height() / 2); - float boundedY = (float) Math.max(top, Math.min(bottom, y)); - mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / - (bottom - top)); - - // Combine the old and new fast scroller bounds to create the full invalidate - // rect - mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); - updateFastScrollerBounds(); - mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); - invalidateFastScroller(mTmpFastScrollerInvalidateRect); - } + mLastY = y; + mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mDraggingFastScroller = false; - animateFastScrollerVisibility(false); + onFastScrollCompleted(); + mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); break; } - return mDraggingFastScroller; + return mScrollbar.isDragging(); } public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { @@ -234,159 +169,117 @@ public class BaseRecyclerView extends RecyclerView mBackgroundPadding.set(padding); } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - drawVerticalScrubber(canvas); - drawFastScrollerPopup(canvas); + public Rect getBackgroundPadding() { + return mBackgroundPadding; } /** - * Draws the vertical scrollbar. + * Returns the scroll bar width when the user is scrolling. */ - private void drawVerticalScrubber(Canvas canvas) { - updateVerticalScrollbarBounds(); - - // Draw the scroll bar - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(verticalScrollbarBounds.left, verticalScrollbarBounds.top); - mScrollbar.setBounds(0, 0, mScrollbarWidth, verticalScrollbarBounds.height()); - mScrollbar.draw(canvas); - canvas.restoreToCount(restoreCount); + public int getMaxScrollbarWidth() { + return mScrollbar.getThumbMaxWidth(); } /** - * Draws the fast scroller popup. + * Returns the available scroll height: + * AvailableScrollHeight = Total height of the all items - last page height + * + * This assumes that all rows are the same height. + * + * @param yOffset the offset from the top of the recycler view to start tracking. */ - private void drawFastScrollerPopup(Canvas canvas) { - if (mFastScrollAlpha > 0f && mFastScrollSectionName != null && !mFastScrollSectionName.isEmpty()) { - // Draw the fast scroller popup - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); - mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollerBg.draw(canvas); - mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, - mFastScrollSectionName.length(), mFastScrollTextBounds); - float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); - canvas.drawText(mFastScrollSectionName, - (mFastScrollerBounds.width() - textWidth) / 2, - mFastScrollerBounds.height() - - (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, - mFastScrollTextPaint); - canvas.restoreToCount(restoreCount); - } + protected int getAvailableScrollHeight(int rowCount, int rowHeight, int yOffset) { + int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom; + int scrollHeight = getPaddingTop() + yOffset + rowCount * rowHeight + getPaddingBottom(); + int availableScrollHeight = scrollHeight - visibleHeight; + return availableScrollHeight; } /** - * Returns the scroll bar width. + * Returns the available scroll bar height: + * AvailableScrollBarHeight = Total height of the visible view - thumb height */ - public int getScrollbarWidth() { - return mScrollbarWidth; + protected int getAvailableScrollBarHeight() { + int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom; + int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight(); + return availableScrollBarHeight; } /** - * Sets the fast scroller alpha. + * Returns the track color (ignoring alpha), can be overridden by each subclass. */ - public void setFastScrollerAlpha(float alpha) { - mFastScrollAlpha = alpha; - invalidateFastScroller(mFastScrollerBounds); + public int getFastScrollerTrackColor(int defaultTrackColor) { + return defaultTrackColor; } /** - * Returns the fast scroller alpha. + * Returns the inactive thumb color, can be overridden by each subclass. */ - public float getFastScrollerAlpha() { - return mFastScrollAlpha; + public int getFastScrollerThumbInactiveColor(int defaultInactiveThumbColor) { + return defaultInactiveThumbColor; } - /** - * Maps the touch (from 0..1) to the adapter position that should be visible. - *

Override in each subclass of this base class. - */ - public String scrollToPositionAtProgress(float touchFraction) { - return null; + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + onUpdateScrollbar(); + mScrollbar.draw(canvas); } /** - * Updates the bounds for the scrollbar. - *

Override in each subclass of this base class. - */ - public void updateVerticalScrollbarBounds() {}; - - /** - * Animates the visibility of the fast scroller popup. + * Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does + * this by mapping the available scroll area of the recycler view to the available space for the + * scroll bar. + * + * @param scrollPosState the current scroll position + * @param rowCount the number of rows, used to calculate the total scroll height (assumes that + * all rows are the same height) + * @param yOffset the offset to start tracking in the recycler view (only used for all apps) */ - private void animateFastScrollerVisibility(final boolean visible) { - ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); - anim.setDuration(visible ? 200 : 150); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - if (visible) { - onFastScrollingStart(); - } - } + protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState, + int rowCount, int yOffset) { + int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight, + yOffset); + int availableScrollBarHeight = getAvailableScrollBarHeight(); + + // Only show the scrollbar if there is height to be scrolled + if (availableScrollHeight <= 0) { + mScrollbar.setScrollbarThumbOffset(-1, -1); + return; + } - @Override - public void onAnimationEnd(Animator animation) { - if (!visible) { - onFastScrollingEnd(); - } - } - }); - anim.start(); + // Calculate the current scroll position, the scrollY of the recycler view accounts for the + // view padding, while the scrollBarY is drawn right up to the background padding (ignoring + // padding) + int scrollY = getPaddingTop() + yOffset + + (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset; + int scrollBarY = mBackgroundPadding.top + + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); + + // Calculate the position and size of the scroll bar + int scrollBarX; + if (Utilities.isRtl(getResources())) { + scrollBarX = mBackgroundPadding.left; + } else { + scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getWidth(); + } + mScrollbar.setScrollbarThumbOffset(scrollBarX, scrollBarY); } /** - * To be overridden by subclasses. - */ - protected void onFastScrollingStart() {} - - /** - * To be overridden by subclasses. - */ - protected void onFastScrollingEnd() {} - - /** - * Invalidates the fast scroller popup. + * Maps the touch (from 0..1) to the adapter position that should be visible. + *

Override in each subclass of this base class. */ - protected void invalidateFastScroller(Rect bounds) { - invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); - } + public abstract String scrollToPositionAtProgress(float touchFraction); /** - * Returns whether a given point is near the scrollbar. + * Updates the bounds for the scrollbar. + *

Override in each subclass of this base class. */ - private boolean isPointNearScrollbar(int x, int y) { - // Check if we are scrolling - updateVerticalScrollbarBounds(); - verticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); - return verticalScrollbarBounds.contains(x, y); - } + public abstract void onUpdateScrollbar(); /** - * Updates the bounds for the fast scroller. + *

Override in each subclass of this base class. */ - private void updateFastScrollerBounds() { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - int x; - int y; - - // Calculate the position for the fast scroller popup - Rect bgBounds = mFastScrollerBg.getBounds(); - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left + (2 * getScrollbarWidth()); - } else { - x = getWidth() - mBackgroundPadding.right - (2 * getScrollbarWidth()) - - bgBounds.width(); - } - y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); - y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - - bgBounds.height())); - mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); - } else { - mFastScrollerBounds.setEmpty(); - } - } + public void onFastScrollCompleted() {} } \ No newline at end of file diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java new file mode 100644 index 000000000..96e994b05 --- /dev/null +++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java @@ -0,0 +1,232 @@ +/* + * 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.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +/** + * The track and scrollbar that shows when you scroll the list. + */ +public class BaseRecyclerViewFastScrollBar { + + public interface FastScrollFocusableView { + void setFastScrollFocused(boolean focused, boolean animated); + } + + private final static int MAX_TRACK_ALPHA = 30; + private final static int SCROLL_BAR_VIS_DURATION = 150; + + private BaseRecyclerView mRv; + private BaseRecyclerViewFastScrollPopup mPopup; + + private AnimatorSet mScrollbarAnimator; + + private int mThumbInactiveColor; + private int mThumbActiveColor; + private Point mThumbOffset = new Point(-1, -1); + private Paint mThumbPaint; + private Paint mTrackPaint; + private int mThumbMinWidth; + private int mThumbMaxWidth; + private int mThumbWidth; + private int mThumbHeight; + // The inset is the buffer around which a point will still register as a click on the scrollbar + private int mTouchInset; + private boolean mIsDragging; + + // This is the offset from the top of the scrollbar when the user first starts touching. To + // prevent jumping, this offset is applied as the user scrolls. + private int mTouchOffset; + + private Rect mInvalidateRect = new Rect(); + private Rect mTmpRect = new Rect(); + + public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) { + mRv = rv; + mPopup = new BaseRecyclerViewFastScrollPopup(rv, res); + mTrackPaint = new Paint(); + mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK)); + mTrackPaint.setAlpha(0); + mThumbInactiveColor = rv.getFastScrollerThumbInactiveColor( + res.getColor(R.color.container_fastscroll_thumb_inactive_color)); + mThumbActiveColor = res.getColor(R.color.container_fastscroll_thumb_active_color); + mThumbPaint = new Paint(); + mThumbPaint.setColor(mThumbInactiveColor); + mThumbWidth = mThumbMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width); + mThumbMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width); + mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height); + mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset); + } + + public void setScrollbarThumbOffset(int x, int y) { + if (mThumbOffset.x == x && mThumbOffset.y == y) { + return; + } + mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); + mThumbOffset.set(x, y); + mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, + mRv.getHeight())); + mRv.invalidate(mInvalidateRect); + } + + // Setter/getter for the search bar width for animations + public void setWidth(int width) { + mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); + mThumbWidth = width; + mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, + mRv.getHeight())); + mRv.invalidate(mInvalidateRect); + } + + public int getWidth() { + return mThumbWidth; + } + + // Setter/getter for the track background alpha for animations + public void setTrackAlpha(int alpha) { + mTrackPaint.setAlpha(alpha); + mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); + mRv.invalidate(mInvalidateRect); + } + + public int getTrackAlpha() { + return mTrackPaint.getAlpha(); + } + + public int getThumbHeight() { + return mThumbHeight; + } + + public int getThumbMaxWidth() { + return mThumbMaxWidth; + } + + public boolean isDragging() { + return mIsDragging; + } + + /** + * Handles the touch event and determines whether to show the fast scroller (or updates it if + * it is already showing). + */ + public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY) { + ViewConfiguration config = ViewConfiguration.get(mRv.getContext()); + + int action = ev.getAction(); + int y = (int) ev.getY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + if (isNearPoint(downX, downY)) { + mTouchOffset = downY - mThumbOffset.y; + } + break; + case MotionEvent.ACTION_MOVE: + // Check if we should start scrolling + if (!mIsDragging && isNearPoint(downX, downY) && + Math.abs(y - downY) > config.getScaledTouchSlop()) { + mRv.getParent().requestDisallowInterceptTouchEvent(true); + mIsDragging = true; + mTouchOffset += (lastY - downY); + mPopup.animateVisibility(true); + animateScrollbar(true); + } + if (mIsDragging) { + // Update the fastscroller section name at this touch position + int top = mRv.getBackgroundPadding().top; + int bottom = mRv.getHeight() - mRv.getBackgroundPadding().bottom - mThumbHeight; + float boundedY = (float) Math.max(top, Math.min(bottom, y - mTouchOffset)); + String sectionName = mRv.scrollToPositionAtProgress((boundedY - top) / + (bottom - top)); + mPopup.setSectionName(sectionName); + mPopup.animateVisibility(!sectionName.isEmpty()); + mRv.invalidate(mPopup.updateFastScrollerBounds(mRv, lastY)); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIsDragging = false; + mTouchOffset = 0; + mPopup.animateVisibility(false); + animateScrollbar(false); + break; + } + } + + public void draw(Canvas canvas) { + if (mThumbOffset.x < 0 || mThumbOffset.y < 0) { + return; + } + + // Draw the scroll bar track and thumb + if (mTrackPaint.getAlpha() > 0) { + canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight(), mTrackPaint); + } + canvas.drawRect(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, + mThumbOffset.y + mThumbHeight, mThumbPaint); + + // Draw the popup + mPopup.draw(canvas); + } + + /** + * Animates the width and color of the scrollbar. + */ + private void animateScrollbar(boolean isScrolling) { + if (mScrollbarAnimator != null) { + mScrollbarAnimator.cancel(); + } + ObjectAnimator trackAlphaAnim = ObjectAnimator.ofInt(this, "trackAlpha", + isScrolling ? MAX_TRACK_ALPHA : 0); + ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "width", + isScrolling ? mThumbMaxWidth : mThumbMinWidth); + ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), + mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor); + colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + mThumbPaint.setColor((Integer) animator.getAnimatedValue()); + mRv.invalidate(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, + mThumbOffset.y + mThumbHeight); + } + }); + mScrollbarAnimator = new AnimatorSet(); + mScrollbarAnimator.playTogether(trackAlphaAnim, thumbWidthAnim, colorAnimation); + mScrollbarAnimator.setDuration(SCROLL_BAR_VIS_DURATION); + mScrollbarAnimator.start(); + } + + /** + * Returns whether the specified points are near the scroll bar bounds. + */ + private boolean isNearPoint(int x, int y) { + mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, + mThumbOffset.y + mThumbHeight); + mTmpRect.inset(mTouchInset, mTouchInset); + return mTmpRect.contains(x, y); + } +} diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java new file mode 100644 index 000000000..aeeb5156d --- /dev/null +++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java @@ -0,0 +1,160 @@ +/* + * 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.animation.Animator; +import android.animation.ObjectAnimator; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +/** + * The fast scroller popup that shows the section name the list will jump to. + */ +public class BaseRecyclerViewFastScrollPopup { + + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; + + private Resources mRes; + private BaseRecyclerView mRv; + + private Drawable mBg; + // The absolute bounds of the fast scroller bg + private Rect mBgBounds = new Rect(); + private int mBgOriginalSize; + private Rect mInvalidateRect = new Rect(); + private Rect mTmpRect = new Rect(); + + private String mSectionName; + private Paint mTextPaint; + private Rect mTextBounds = new Rect(); + private float mAlpha; + + private Animator mAlphaAnimator; + private boolean mVisible; + + public BaseRecyclerViewFastScrollPopup(BaseRecyclerView rv, Resources res) { + mRes = res; + mRv = rv; + mBgOriginalSize = res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_size); + mBg = res.getDrawable(R.drawable.container_fastscroll_popup_bg); + mBg.setBounds(0, 0, mBgOriginalSize, mBgOriginalSize); + mTextPaint = new Paint(); + mTextPaint.setColor(Color.WHITE); + mTextPaint.setAntiAlias(true); + mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_text_size)); + } + + /** + * Sets the section name. + */ + public void setSectionName(String sectionName) { + if (!sectionName.equals(mSectionName)) { + mSectionName = sectionName; + mTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTextBounds); + // Update the width to use measureText since that is more accurate + mTextBounds.right = (int) (mTextBounds.left + mTextPaint.measureText(sectionName)); + } + } + + /** + * Updates the bounds for the fast scroller. + * @return the invalidation rect for this update. + */ + public Rect updateFastScrollerBounds(BaseRecyclerView rv, int lastTouchY) { + mInvalidateRect.set(mBgBounds); + + if (isVisible()) { + // Calculate the dimensions and position of the fast scroller popup + int edgePadding = rv.getMaxScrollbarWidth(); + int bgPadding = (mBgOriginalSize - mTextBounds.height()) / 2; + int bgHeight = mBgOriginalSize; + int bgWidth = Math.max(mBgOriginalSize, mTextBounds.width() + (2 * bgPadding)); + if (Utilities.isRtl(mRes)) { + mBgBounds.left = rv.getBackgroundPadding().left + (2 * rv.getMaxScrollbarWidth()); + mBgBounds.right = mBgBounds.left + bgWidth; + } else { + mBgBounds.right = rv.getWidth() - rv.getBackgroundPadding().right - + (2 * rv.getMaxScrollbarWidth()); + mBgBounds.left = mBgBounds.right - bgWidth; + } + mBgBounds.top = lastTouchY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgHeight); + mBgBounds.top = Math.max(edgePadding, + Math.min(mBgBounds.top, rv.getHeight() - edgePadding - bgHeight)); + mBgBounds.bottom = mBgBounds.top + bgHeight; + } else { + mBgBounds.setEmpty(); + } + + // Combine the old and new fast scroller bounds to create the full invalidate rect + mInvalidateRect.union(mBgBounds); + return mInvalidateRect; + } + + /** + * Animates the visibility of the fast scroller popup. + */ + public void animateVisibility(boolean visible) { + if (mVisible != visible) { + mVisible = visible; + if (mAlphaAnimator != null) { + mAlphaAnimator.cancel(); + } + mAlphaAnimator = ObjectAnimator.ofFloat(this, "alpha", visible ? 1f : 0f); + mAlphaAnimator.setDuration(visible ? 200 : 150); + mAlphaAnimator.start(); + } + } + + // Setter/getter for the popup alpha for animations + public void setAlpha(float alpha) { + mAlpha = alpha; + mRv.invalidate(mBgBounds); + } + + public float getAlpha() { + return mAlpha; + } + + public int getHeight() { + return mBgOriginalSize; + } + + public void draw(Canvas c) { + if (isVisible()) { + // Draw the fast scroller popup + int restoreCount = c.save(Canvas.MATRIX_SAVE_FLAG); + c.translate(mBgBounds.left, mBgBounds.top); + mTmpRect.set(mBgBounds); + mTmpRect.offsetTo(0, 0); + mBg.setBounds(mTmpRect); + mBg.setAlpha((int) (mAlpha * 255)); + mBg.draw(c); + mTextPaint.setAlpha((int) (mAlpha * 255)); + c.drawText(mSectionName, (mBgBounds.width() - mTextBounds.width()) / 2, + mBgBounds.height() - (mBgBounds.height() - mTextBounds.height()) / 2, + mTextPaint); + c.restoreToCount(restoreCount); + } + } + + public boolean isVisible() { + return (mAlpha > 0f) && (mSectionName != null); + } +} diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 6c13b4a9b..a0be8ea2b 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; @@ -24,6 +25,7 @@ import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.os.Build; @@ -34,6 +36,8 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.ViewParent; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; import android.widget.TextView; import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.model.PackageItemInfo; @@ -43,7 +47,8 @@ import com.android.launcher3.model.PackageItemInfo; * because we want to make the bubble taller than the text and TextView's clip is * too aggressive. */ -public class BubbleTextView extends TextView { +public class BubbleTextView extends TextView + implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView { private static SparseArray sPreloaderThemes = new SparseArray(2); @@ -56,6 +61,13 @@ public class BubbleTextView extends TextView { private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_ALL_APPS = 1; + private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f; + private static final int FAST_SCROLL_FOCUS_MODE_NONE = 0; + private static final int FAST_SCROLL_FOCUS_MODE_SCALE_ICON = 1; + private static final int FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG = 2; + private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175; + private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125; + private final Launcher mLauncher; private Drawable mIcon; private final Drawable mBackground; @@ -79,6 +91,12 @@ public class BubbleTextView extends TextView { private boolean mIgnorePressedStateChange; private boolean mDisableRelayout = false; + private ObjectAnimator mFastScrollFocusAnimator; + private Paint mFastScrollFocusBgPaint; + private float mFastScrollFocusFraction; + private boolean mFastScrollFocused; + private final int mFastScrollMode = FAST_SCROLL_FOCUS_MODE_SCALE_ICON; + private IconLoadRequest mIconLoadRequest; public BubbleTextView(Context context) { @@ -131,6 +149,13 @@ public class BubbleTextView extends TextView { setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR); } + if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG) { + mFastScrollFocusBgPaint = new Paint(); + mFastScrollFocusBgPaint.setAntiAlias(true); + mFastScrollFocusBgPaint.setColor( + getResources().getColor(R.color.container_fastscroll_thumb_active_color)); + } + setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @@ -335,7 +360,18 @@ public class BubbleTextView extends TextView { @Override public void draw(Canvas canvas) { if (!mCustomShadowsEnabled) { + // Draw the fast scroll focus bg if we have one + if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG && + mFastScrollFocusFraction > 0f) { + DeviceProfile grid = mLauncher.getDeviceProfile(); + int iconCenterX = getScrollX() + (getWidth() / 2); + int iconCenterY = getScrollY() + getPaddingTop() + (grid.iconSizePx / 2); + canvas.drawCircle(iconCenterX, iconCenterY, + mFastScrollFocusFraction * (getWidth() / 2), mFastScrollFocusBgPaint); + } + super.draw(canvas); + return; } @@ -538,6 +574,51 @@ public class BubbleTextView extends TextView { } } + // Setters & getters for the animation + public void setFastScrollFocus(float fraction) { + mFastScrollFocusFraction = fraction; + if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_SCALE_ICON) { + setScaleX(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f)); + setScaleY(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f)); + } else { + invalidate(); + } + } + + public float getFastScrollFocus() { + return mFastScrollFocusFraction; + } + + @Override + public void setFastScrollFocused(final boolean focused, boolean animated) { + if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) { + return; + } + + if (mFastScrollFocused != focused) { + mFastScrollFocused = focused; + + if (animated) { + // Clean up the previous focus animator + if (mFastScrollFocusAnimator != null) { + mFastScrollFocusAnimator.cancel(); + } + mFastScrollFocusAnimator = ObjectAnimator.ofFloat(this, "fastScrollFocus", + focused ? 1f : 0f); + if (focused) { + mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator()); + } else { + mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator()); + } + mFastScrollFocusAnimator.setDuration(focused ? + FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION); + mFastScrollFocusAnimator.start(); + } else { + mFastScrollFocusFraction = focused ? 1f : 0f; + } + } + } + /** * Interface to be implemented by the grand parent to allow click shadow effect. */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a75a09af7..77c45407f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1125,27 +1125,6 @@ public class Launcher extends Activity public void forceExitFullImmersion(); } - public interface LauncherAppsCallbacks { - /** - * Updates launcher to the available space that AllApps can take so as not to overlap with - * any other views. - */ - @Deprecated - public void onAllAppsBoundsChanged(Rect bounds); - - /** - * Called to dismiss all apps if it is showing. - */ - @Deprecated - public void dismissAllApps(); - - /** - * Sets the search manager to be used for app search. - */ - @Deprecated - public void setSearchManager(Object manager); - } - public interface LauncherSearchCallbacks { /** * Called when the search overlay is shown. diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 83d90da97..0b7b1fdc4 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -143,13 +143,6 @@ public class LauncherAppState { return mModel; } - /** - * TODO(winsonc, hyunyoungs): We need to respect this - */ - boolean shouldShowAppOrWidgetProvider(ComponentName componentName) { - return mAppFilter == null || mAppFilter.shouldShowApp(componentName); - } - static void setLauncherProvider(LauncherProvider provider) { sLauncherProvider = new WeakReference(provider); } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 56db7747c..49d6d68e5 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -70,10 +70,12 @@ public interface LauncherCallbacks { /* * Extension points for replacing the search experience */ + @Deprecated public boolean forceDisableVoiceButtonProxy(); public boolean providesSearch(); public boolean startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds); + @Deprecated public void startVoice(); public boolean hasCustomContentToLeft(); public void populateCustomContentContainer(); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 3b06c2093..af118d7f6 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -637,6 +637,25 @@ public final class Utilities { return -fm.top + fm.bottom; } + /** + * Convenience println with multiple args. + */ + public static void println(String key, Object... args) { + StringBuilder b = new StringBuilder(); + b.append(key); + b.append(": "); + boolean isFirstArgument = true; + for (Object arg : args) { + if (isFirstArgument) { + isFirstArgument = false; + } else { + b.append(", "); + } + b.append(arg); + } + System.out.println(b.toString()); + } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static boolean isRtl(Resources res) { return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) && diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index d56e9fc1e..32b7be807 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -17,7 +17,6 @@ package com.android.launcher3.allapps; import android.annotation.SuppressLint; import android.annotation.TargetApi; -import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -155,6 +154,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private int mSectionNamesMargin; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; + private int mRecyclerViewTopBottomPadding; // This coordinate is relative to this container view private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1); // This coordinate is relative to its parent @@ -189,7 +189,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx + Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) + 2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) + - 2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding)); + res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_padding) + + res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding)); mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); mApps = new AlphabeticalAppsList(context); mApps.setAdapterChangedCallback(this); @@ -199,6 +200,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mApps.setAdapter(mAdapter); mLayoutManager = mAdapter.getLayoutManager(); mItemDecoration = mAdapter.getItemDecoration(); + mRecyclerViewTopBottomPadding = + res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding); mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); @@ -414,7 +417,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f), MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE); - mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); + mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow); mAdapter.setNumAppsPerRow(mNumAppsPerRow); mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm); } @@ -431,14 +434,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) { boolean isRtl = Utilities.isRtl(getResources()); - // TODO: Use quantum_panel instead of quantum_panel_shape. + // TODO: Use quantum_panel instead of quantum_panel_shape InsetDrawable background = new InsetDrawable( getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0, padding.right, 0); + Rect bgPadding = new Rect(); + background.getPadding(bgPadding); mContainerView.setBackground(background); mRevealView.setBackground(background.getConstantState().newDrawable()); - mAppsRecyclerView.updateBackgroundPadding(padding); - mAdapter.updateBackgroundPadding(padding); + mAppsRecyclerView.updateBackgroundPadding(bgPadding); + mAdapter.updateBackgroundPadding(bgPadding); // Hack: We are going to let the recycler view take the full width, so reset the padding on // the container to zero after setting the background and apply the top-bottom padding to @@ -448,13 +453,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // Pad the recycler view by the background padding plus the start margin (for the section // names) - int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getScrollbarWidth()); + int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getMaxScrollbarWidth()); + int topBottomPadding = mRecyclerViewTopBottomPadding; if (isRtl) { - mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getScrollbarWidth(), 0, - padding.right + startInset, 0); + mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getMaxScrollbarWidth(), + topBottomPadding, padding.right + startInset, topBottomPadding); } else { - mAppsRecyclerView.setPadding(padding.left + startInset, 0, - padding.right + mAppsRecyclerView.getScrollbarWidth(), 0); + mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding, + padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), topBottomPadding); } // Inset the search bar to fit its bounds above the container @@ -474,8 +480,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // Update the prediction bar insets as well mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams(); - lp.leftMargin = padding.left + mAppsRecyclerView.getScrollbarWidth(); - lp.rightMargin = padding.right + mAppsRecyclerView.getScrollbarWidth(); + lp.leftMargin = padding.left + mAppsRecyclerView.getMaxScrollbarWidth(); + lp.rightMargin = padding.right + mAppsRecyclerView.getMaxScrollbarWidth(); mPredictionBarView.requestLayout(); } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 68407bdd5..19e2757f9 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -337,7 +337,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); - getCurScrollState(scrollPosState, items); - if (scrollPosState.rowIndex != -1) { + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex != -1) { int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) + - predictionBarHeight - scrollPosState.rowTopOffset; + return getPaddingTop() + predictionBarHeight + + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - + mScrollPosState.rowTopOffset; } return 0; } @@ -132,143 +145,159 @@ public class AllAppsRecyclerView extends BaseRecyclerView } } - @Override - protected void onFastScrollingEnd() { - mLastFastscrollPosition = -1; - } - /** * Maps the touch (from 0..1) to the adapter position that should be visible. */ @Override public String scrollToPositionAtProgress(float touchFraction) { - // Ensure that we have any sections - List fastScrollSections = - mApps.getFastScrollerSections(); - if (fastScrollSections.isEmpty()) { + int rowCount = mApps.getNumAppRows(); + if (rowCount == 0) { return ""; } // Stop the scroller if it is scrolling - LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); stopScroll(); - // If there is a prediction bar, then capture the appropriate area for the prediction bar - float predictionBarFraction = 0f; - if (!mApps.getPredictedApps().isEmpty()) { - predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize(); - if (touchFraction <= predictionBarFraction) { - // Scroll to the top of the view, where the prediction bar is - layoutManager.scrollToPositionWithOffset(0, 0); - return ""; + // Find the fastscroll section that maps to this touch fraction + List fastScrollSections = + mApps.getFastScrollerSections(); + AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0); + if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW) { + for (int i = 1; i < fastScrollSections.size(); i++) { + AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i); + if (info.touchFraction > touchFraction) { + break; + } + lastInfo = info; } + } else if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS){ + lastInfo = fastScrollSections.get((int) (touchFraction * (fastScrollSections.size() - 1))); + } else { + throw new RuntimeException("Unexpected scroll bar mode"); } - // Since the app ranges are from 0..1, we need to map the touch fraction back to 0..1 from - // predictionBarFraction..1 - touchFraction = (touchFraction - predictionBarFraction) * - (1f / (1f - predictionBarFraction)); - AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0); - for (int i = 1; i < fastScrollSections.size(); i++) { - AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i); - if (lastScrollSection.appRangeFraction <= touchFraction && - touchFraction < scrollSection.appRangeFraction) { - break; - } - lastScrollSection = scrollSection; + // Map the touch position back to the scroll of the recycler view + getCurScrollState(mScrollPosState, mApps.getAdapterItems()); + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; + int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, + predictionBarHeight); + LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); + if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { + layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); } - // Scroll to the view at the position, anchored at the top of the screen. We call the scroll - // method on the LayoutManager directly since it is not exposed by RecyclerView. - if (mLastFastscrollPosition != lastScrollSection.appItem.position) { - mLastFastscrollPosition = lastScrollSection.appItem.position; - layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); - } + if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) { + mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position; - return lastScrollSection.sectionName; - } + // Reset the last focused view + if (mLastFastScrollFocusedView != null) { + mLastFastScrollFocusedView.setFastScrollFocused(false, true); + mLastFastScrollFocusedView = null; + } - /** - * Returns the row index for a app index in the list. - */ - private int findRowForAppIndex(int index) { - List sections = mApps.getSections(); - int appIndex = 0; - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - if (appIndex + info.numApps > index) { - return rowCount + ((index - appIndex) / mNumAppsPerRow); + if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) { + smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState); + } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { + final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition); + if (vh != null && + vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) { + mLastFastScrollFocusedView = + (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView; + mLastFastScrollFocusedView.setFastScrollFocused(true, true); + } + } else { + throw new RuntimeException("Unexpected fast scroll mode"); } - appIndex += info.numApps; - rowCount += numRowsInSection; } - return appIndex; + return lastInfo.sectionName; } - /** - * Returns the total number of rows in the list. - */ - private int getNumRows() { - List sections = mApps.getSections(); - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - rowCount += numRowsInSection; + @Override + public void onFastScrollCompleted() { + super.onFastScrollCompleted(); + // Reset and clean up the last focused view + if (mLastFastScrollFocusedView != null) { + mLastFastScrollFocusedView.setFastScrollFocused(false, true); + mLastFastScrollFocusedView = null; } - return rowCount; + mPrevFastScrollFocusedPosition = -1; } - /** * Updates the bounds for the scrollbar. */ @Override - public void updateVerticalScrollbarBounds() { + public void onUpdateScrollbar() { List items = mApps.getAdapterItems(); // Skip early if there are no items or we haven't been measured if (items.isEmpty() || mNumAppsPerRow == 0) { - verticalScrollbarBounds.setEmpty(); + mScrollbar.setScrollbarThumbOffset(-1, -1); return; } // Find the index and height of the first visible row (all rows have the same height) - int x, y; + int rowCount = mApps.getNumAppRows(); + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex < 0) { + mScrollbar.setScrollbarThumbOffset(-1, -1); + return; + } + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - int rowCount = getNumRows(); - getCurScrollState(scrollPosState, items); - if (scrollPosState.rowIndex != -1) { - int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight; - if (totalScrollHeight > height) { - int scrollbarHeight = (int) (height / ((float) totalScrollHeight / height)); - - // Calculate the position and size of the scroll bar - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left; - } else { - x = getWidth() - mBackgroundPadding.right - getScrollbarWidth(); - } + synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, predictionBarHeight); + } - // To calculate the offset, we compute the percentage of the total scrollable height - // that the user has already scrolled and then map that to the scroll bar bounds - int availableY = totalScrollHeight - height; - int availableScrollY = height - scrollbarHeight; - y = (scrollPosState.rowIndex * scrollPosState.rowHeight) + predictionBarHeight - - scrollPosState.rowTopOffset; - y = getPaddingTop() + - (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); - - verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight); - return; + /** + * This runnable runs a single frame of the smooth scroll animation and posts the next frame + * if necessary. + */ + private Runnable mSmoothSnapNextFrameRunnable = new Runnable() { + @Override + public void run() { + if (mFastScrollFrameIndex < mFastScrollFrames.length) { + scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]); + mFastScrollFrameIndex++; + postOnAnimation(mSmoothSnapNextFrameRunnable); + } else { + // Animation completed, set the fast scroll state on the target view + final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition); + if (vh != null && + vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView && + mLastFastScrollFocusedView != vh.itemView) { + mLastFastScrollFocusedView = + (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView; + mLastFastScrollFocusedView.setFastScrollFocused(true, true); + } } } - verticalScrollbarBounds.setEmpty(); + }; + + /** + * Smoothly snaps to a given position. We do this manually by calculating the keyframes + * ourselves and animating the scroll on the recycler view. + */ + private void smoothSnapToPosition(final int position, ScrollPositionState scrollPosState) { + removeCallbacks(mSmoothSnapNextFrameRunnable); + + // Calculate the full animation from the current scroll position to the final scroll + // position, and then run the animation for the duration. + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; + int curScrollY = getPaddingTop() + predictionBarHeight + + (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset; + int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight); + int numFrames = mFastScrollFrames.length; + for (int i = 0; i < numFrames; i++) { + // TODO(winsonc): We can interpolate this as well. + mFastScrollFrames[i] = (newScrollY - curScrollY) / numFrames; + } + mFastScrollFrameIndex = 0; + postOnAnimation(mSmoothSnapNextFrameRunnable); } /** - * Returns the current scroll state. + * Returns the current scroll state of the apps rows, not including the prediction + * bar. */ private void getCurScrollState(ScrollPositionState stateOut, List items) { @@ -288,7 +317,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView if (position != NO_POSITION) { AlphabeticalAppsList.AdapterItem item = items.get(position); if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { - stateOut.rowIndex = findRowForAppIndex(item.appIndex); + stateOut.rowIndex = item.rowIndex; stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); stateOut.rowHeight = child.getHeight(); break; @@ -296,4 +325,17 @@ public class AllAppsRecyclerView extends BaseRecyclerView } } } + + /** + * Returns the scrollY for the given position in the adapter. + */ + private int getScrollAtPosition(int position, int rowHeight) { + AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position); + if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { + int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; + return getPaddingTop() + predictionBarHeight + item.rowIndex * rowHeight; + } else { + return 0; + } + } } diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index aa73c74cf..ea99872ed 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -62,15 +62,13 @@ public class AlphabeticalAppsList { public static class FastScrollSectionInfo { // The section name public String sectionName; - // To map the touch (from 0..1) to the index in the app list to jump to in the fast - // scroller, we use the fraction in range (0..1) of the app index / total app count. - public float appRangeFraction; // The AdapterItem to scroll to for this section - public AdapterItem appItem; + public AdapterItem fastScrollToItem; + // The touch fraction that should map to this fast scroll section info + public float touchFraction; - public FastScrollSectionInfo(String sectionName, float appRangeFraction) { + public FastScrollSectionInfo(String sectionName) { this.sectionName = sectionName; - this.appRangeFraction = appRangeFraction; } } @@ -83,6 +81,8 @@ public class AlphabeticalAppsList { public int position; // The type of this item public int viewType; + // The row that this item shows up on + public int rowIndex; /** Section & App properties */ // The section for this item @@ -94,6 +94,8 @@ public class AlphabeticalAppsList { public String sectionName = null; // The index of this app in the section public int sectionAppIndex = -1; + // The index of this app in the row + public int rowAppIndex; // The associated AppInfo for the app public AppInfo appInfo = null; // The index of this app not including sections @@ -172,6 +174,7 @@ public class AlphabeticalAppsList { private AdapterChangedCallback mAdapterChangedCallback; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; + private int mNumAppRowsInAdapter; public AlphabeticalAppsList(Context context) { mLauncher = (Launcher) context; @@ -240,6 +243,13 @@ public class AlphabeticalAppsList { return mFilteredApps.size(); } + /** + * Returns the number of rows of applications (not including predictions) + */ + public int getNumAppRows() { + return mNumAppRowsInAdapter; + } + /** * Returns whether there are is a filter set. */ @@ -419,23 +429,23 @@ public class AlphabeticalAppsList { // Create a new spacer for the prediction bar AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++); mAdapterItems.add(sectionItem); + // Add a fastscroller section for the prediction bar + lastFastScrollerSectionInfo = new FastScrollSectionInfo(""); + lastFastScrollerSectionInfo.fastScrollToItem = sectionItem; + mFastScrollerSections.add(lastFastScrollerSectionInfo); } } // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections - List apps = getFiltersAppInfos(); - int numApps = apps.size(); - for (int i = 0; i < numApps; i++) { - AppInfo info = apps.get(i); + for (AppInfo info : getFiltersAppInfos()) { String sectionName = getAndUpdateCachedSectionName(info.title); // Create a new section if the section names do not match if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) { lastSectionName = sectionName; lastSectionInfo = new SectionInfo(); - lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, - (float) appIndex / numApps); + lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName); mSections.add(lastSectionInfo); mFastScrollerSections.add(lastFastScrollerSectionInfo); @@ -451,7 +461,7 @@ public class AlphabeticalAppsList { lastSectionInfo.numApps++, info, appIndex++); if (lastSectionInfo.firstAppItem == null) { lastSectionInfo.firstAppItem = appItem; - lastFastScrollerSectionInfo.appItem = appItem; + lastFastScrollerSectionInfo.fastScrollToItem = appItem; } mAdapterItems.add(appItem); mFilteredApps.add(info); @@ -460,6 +470,45 @@ public class AlphabeticalAppsList { // Merge multiple sections together as requested by the merge strategy for this device mergeSections(); + if (mNumAppsPerRow != 0) { + // Update the number of rows in the adapter after we do all the merging (otherwise, we + // would have to shift the values again) + int numAppsInSection = 0; + int numAppsInRow = 0; + int rowIndex = -1; + for (AdapterItem item : mAdapterItems) { + item.rowIndex = 0; + if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) { + numAppsInSection = 0; + } else if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { + if (numAppsInSection % mNumAppsPerRow == 0) { + numAppsInRow = 0; + rowIndex++; + } + item.rowIndex = rowIndex; + item.rowAppIndex = numAppsInRow; + numAppsInSection++; + numAppsInRow++; + } + } + mNumAppRowsInAdapter = rowIndex + 1; + + // Pre-calculate all the fast scroller fractions based on the number of rows, if we have + // predicted apps, then we should account for that as a row in the touchFraction + float rowFraction = 1f / (mNumAppRowsInAdapter + (mPredictedApps.isEmpty() ? 0 : 1)); + float initialOffset = mPredictedApps.isEmpty() ? 0 : rowFraction; + for (FastScrollSectionInfo info : mFastScrollerSections) { + AdapterItem item = info.fastScrollToItem; + if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) { + info.touchFraction = 0f; + continue; + } + + float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow); + info.touchFraction = initialOffset + item.rowIndex * rowFraction + subRowFraction; + } + } + // Refresh the recycler view if (mAdapter != null) { mAdapter.notifyDataSetChanged(); @@ -511,6 +560,7 @@ public class AlphabeticalAppsList { // Remove the next section break mAdapterItems.remove(nextSection.sectionBreakItem); int pos = mAdapterItems.indexOf(section.firstAppItem); + // Point the section for these new apps to the merged section int nextPos = pos + section.numApps; for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) { diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 500311add..5afd7c493 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -345,9 +345,11 @@ public class WidgetsContainerView extends BaseContainerView InsetDrawable background = new InsetDrawable( getResources().getDrawable(R.drawable.quantum_panel_shape_dark), padding.left, 0, padding.right, 0); + Rect bgPadding = new Rect(); + background.getPadding(bgPadding); mView.setBackground(background); getRevealView().setBackground(background.getConstantState().newDrawable()); - mView.updateBackgroundPadding(padding); + mView.updateBackgroundPadding(bgPadding); } /** diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index fa7e2f0a2..3101f3327 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -17,14 +17,14 @@ package com.android.launcher3.widget; import android.content.Context; -import android.graphics.Rect; +import android.graphics.Color; import android.support.v7.widget.LinearLayoutManager; import android.util.AttributeSet; import android.view.View; import com.android.launcher3.BaseRecyclerView; -import com.android.launcher3.Utilities; -import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.R; import com.android.launcher3.model.PackageItemInfo; +import com.android.launcher3.model.WidgetsModel; /** * The widgets recycler view. @@ -33,6 +33,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; + private ScrollPositionState mScrollPosState = new ScrollPositionState(); public WidgetsRecyclerView(Context context) { this(context, null); @@ -58,6 +59,14 @@ public class WidgetsRecyclerView extends BaseRecyclerView { addOnItemTouchListener(this); } + public int getFastScrollerTrackColor(int defaultTrackColor) { + return Color.WHITE; + } + + public int getFastScrollerThumbInactiveColor(int defaultInactiveThumbColor) { + return getResources().getColor(R.color.widgets_view_fastscroll_thumb_inactive_color); + } + /** * Sets the widget model in this view, used to determine the fast scroll position. */ @@ -70,15 +79,21 @@ public class WidgetsRecyclerView extends BaseRecyclerView { */ @Override public String scrollToPositionAtProgress(float touchFraction) { - float pos = mWidgets.getPackageSize() * touchFraction; + int rowCount = mWidgets.getPackageSize(); + if (rowCount == 0) { + return ""; + } - int posInt = (int) pos; + // Stop the scroller if it is scrolling + stopScroll(); + + getCurScrollState(mScrollPosState); + float pos = rowCount * touchFraction; + int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 0); LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); - getCurScrollState(scrollPosState); - layoutManager.scrollToPositionWithOffset((int) pos, - (int) (scrollPosState.rowHeight * ((float) posInt - pos))); + layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); - posInt = (int) ((touchFraction == 1)? pos -1 : pos); + int posInt = (int) ((touchFraction == 1)? pos -1 : pos); PackageItemInfo p = mWidgets.getPackageItemInfo(posInt); return p.titleSectionName; } @@ -87,43 +102,23 @@ public class WidgetsRecyclerView extends BaseRecyclerView { * Updates the bounds for the scrollbar. */ @Override - public void updateVerticalScrollbarBounds() { + public void onUpdateScrollbar() { int rowCount = mWidgets.getPackageSize(); - verticalScrollbarBounds.setEmpty(); // Skip early if, there are no items. if (rowCount == 0) { + mScrollbar.setScrollbarThumbOffset(-1, -1); return; } // Skip early if, there no child laid out in the container. - getCurScrollState(scrollPosState); - if (scrollPosState.rowIndex < 0) { + getCurScrollState(mScrollPosState); + if (mScrollPosState.rowIndex < 0) { + mScrollbar.setScrollbarThumbOffset(-1, -1); return; } - int actualHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * scrollPosState.rowHeight; - // Skip early if the height of all the rows are actually less than the container height. - if (totalScrollHeight < actualHeight) { - verticalScrollbarBounds.setEmpty(); - return; - } - - int scrollbarHeight = (int) (actualHeight / ((float) totalScrollHeight / actualHeight)); - int availableY = totalScrollHeight - actualHeight; - int availableScrollY = actualHeight - scrollbarHeight; - int y = (scrollPosState.rowIndex * scrollPosState.rowHeight) - - scrollPosState.rowTopOffset; - y = getPaddingTop() + - (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); - - // Calculate the position and size of the scroll bar. - int x = getWidth() - getScrollbarWidth() - mBackgroundPadding.right; - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left; - } - verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight); + synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, 0); } /** -- cgit v1.2.3 From 5d9200f92141f157fe6ae30106ee4b644d5ebd7e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 23 Jun 2015 14:28:29 -0700 Subject: Using a full-bleed background for Set-Wallpaper button Bug: 22007160 Change-Id: I06c4ffe5b5fa4f2c18c06a6e1bf014b328de00fd --- WallpaperPicker/res/values-v21/styles.xml | 5 +++++ WallpaperPicker/res/values/styles.xml | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml index 04f39de73..582ab8fed 100644 --- a/WallpaperPicker/res/values-v21/styles.xml +++ b/WallpaperPicker/res/values-v21/styles.xml @@ -28,4 +28,9 @@ 0dp + + \ No newline at end of file diff --git a/WallpaperPicker/res/values/styles.xml b/WallpaperPicker/res/values/styles.xml index f4008f159..74aeab903 100644 --- a/WallpaperPicker/res/values/styles.xml +++ b/WallpaperPicker/res/values/styles.xml @@ -17,7 +17,7 @@ */ --> - + -- cgit v1.2.3 From 243fdd7cdf262b341b4f66177af27eec4f5cde45 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 22 Jun 2015 19:48:07 -0700 Subject: Working around overscroll issues in AllApps. - For the time being, we are going to do custom drawing to ensure that we get the touch events in the right order, while still allowing the recycler view to draw the overscroll effect on top of the prediction bar. Bug: 21335369 Change-Id: I6bf64e5c1e9aa634a953223a5decf74942e4fb57 --- res/layout/all_apps_container.xml | 1 + res/layout/widgets_view.xml | 1 + res/values/styles.xml | 7 ++++ .../launcher3/allapps/AllAppsRecyclerView.java | 19 ++++++++-- .../allapps/AllAppsRecyclerViewContainerView.java | 40 ++++++++++++++++++++++ .../launcher3/widget/WidgetsRecyclerView.java | 13 +++++++ 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml index 0b624e6a5..0221a568b 100644 --- a/res/layout/all_apps_container.xml +++ b/res/layout/all_apps_container.xml @@ -27,6 +27,7 @@ + + +