From 0cf244739654318da7a07177de4e450e316c94a9 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 13 Jul 2017 08:25:42 -0700 Subject: [PATCH 001/885] Import translations. DO NOT MERGE Change-Id: Ibb5157e832bf0cc25e9246983d3700ac1ec61de2 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-af/strings.xml | 3 +++ res/values-am/strings.xml | 3 +++ res/values-ar/strings.xml | 3 +++ res/values-az/strings.xml | 3 +++ res/values-b+sr+Latn/strings.xml | 3 +++ res/values-be/strings.xml | 6 ++++++ res/values-bg/strings.xml | 3 +++ res/values-bn/strings.xml | 6 ++++++ res/values-bs/strings.xml | 3 +++ res/values-ca/strings.xml | 6 ++++++ res/values-cs/strings.xml | 3 +++ res/values-da/strings.xml | 6 ++++++ res/values-de/strings.xml | 3 +++ res/values-el/strings.xml | 3 +++ res/values-en-rAU/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/strings.xml | 3 +++ res/values-eu/strings.xml | 6 ++++++ res/values-fa/strings.xml | 6 ++++++ res/values-fi/strings.xml | 3 +++ res/values-fr-rCA/strings.xml | 3 +++ res/values-fr/strings.xml | 3 +++ res/values-gl/strings.xml | 3 +++ res/values-gu/strings.xml | 6 ++++++ res/values-hi/strings.xml | 3 +++ res/values-hr/strings.xml | 3 +++ res/values-hu/strings.xml | 3 +++ res/values-hy/strings.xml | 3 +++ res/values-in/strings.xml | 3 +++ res/values-is/strings.xml | 6 ++++++ res/values-it/strings.xml | 3 +++ res/values-iw/strings.xml | 6 ++++++ res/values-ja/strings.xml | 3 +++ res/values-ka/strings.xml | 3 +++ res/values-kk/strings.xml | 3 +++ res/values-km/strings.xml | 6 ++++++ res/values-kn/strings.xml | 6 ++++++ res/values-ko/strings.xml | 3 +++ res/values-ky/strings.xml | 3 +++ res/values-lo/strings.xml | 6 ++++++ res/values-lt/strings.xml | 6 ++++++ res/values-lv/strings.xml | 3 +++ res/values-mk/strings.xml | 3 +++ res/values-ml/strings.xml | 6 ++++++ res/values-mn/strings.xml | 3 +++ res/values-mr/strings.xml | 6 ++++++ res/values-ms/strings.xml | 3 +++ res/values-my/strings.xml | 6 ++++++ res/values-nb/strings.xml | 6 ++++++ res/values-ne/strings.xml | 6 ++++++ res/values-nl/strings.xml | 3 +++ res/values-pa/strings.xml | 6 ++++++ res/values-pl/strings.xml | 3 +++ res/values-pt-rPT/strings.xml | 3 +++ res/values-pt/strings.xml | 6 ++++++ res/values-ro/strings.xml | 3 +++ res/values-ru/strings.xml | 3 +++ res/values-si/strings.xml | 3 +++ res/values-sk/strings.xml | 3 +++ res/values-sl/strings.xml | 6 ++++++ res/values-sq/strings.xml | 6 ++++++ res/values-sr/strings.xml | 3 +++ res/values-sv/strings.xml | 3 +++ res/values-sw/strings.xml | 3 +++ res/values-ta/strings.xml | 3 +++ res/values-te/strings.xml | 6 ++++++ res/values-th/strings.xml | 5 ++++- res/values-tl/strings.xml | 3 +++ res/values-tr/strings.xml | 3 +++ res/values-uk/strings.xml | 3 +++ res/values-ur/strings.xml | 6 ++++++ res/values-uz/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 +++ 80 files changed, 313 insertions(+), 1 deletion(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index fe00d7af82..84faebe8f5 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -79,6 +79,9 @@ "Kennisgewingkolle" "Aan" "Af" + "Kennisgewingtoegang word benodig" + "Skakel programkennisgewings vir %1$s aan om kennisgewingkolle te sien" + "Verander instellings" "Voeg ikoon by tuisskerm" "Vir nuwe programme" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index aebb428fbd..76a9734cef 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -79,6 +79,9 @@ "የማሳወቂያ ነጥቦች" "በርቷል" "ጠፍቷል" + "የማሳወቂያ መዳረሻ ያስፈልጋል" + "የማሳወቂያ ነጥቦችን ለማሳየት የመተግብሪያ ማሳወቂያዎችን ለ%1$s ያብሩ" + "ቅንብሮችን ቀይር" "አዶ ወደ የመነሻ ማያ ገጽ አክል" "ለአዲስ መተግበሪያዎች" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index a45e40ed7d..e51880aa5c 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -79,6 +79,9 @@ "نقاط الإشعارات" "قيد التشغيل" "قيد الإيقاف" + "يلزم تمكين الوصول إلى الإشعارات" + "لعرض نقاط الإشعارات، يجب تشغيل إشعارات التطبيق في %1$s" + "تغيير الإعدادات" "إضافة رمز إلى الشاشة الرئيسية" "للتطبيقات الجديدة" diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index a8791b7353..2a6615a2db 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -79,6 +79,9 @@ "Bildiriş nişanı" "Aktiv" "Deaktiv" + "Bildiriş girişi tələb edilir" + "Bildiriş Nöqtələrini göstərmək üçün %1$s bildirişlərini aktiv edin" + "Ayarları dəyişin" "Əsas ekrana ikona əlavə edin" "Yeni tətbiqlər üçün" diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index cb296f422b..f17c5f03d4 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -79,6 +79,9 @@ "Tačke za obaveštenja" "Uključeno" "Isključeno" + "Potreban je pristup za obaveštenja" + "Da biste prikazali tačke za obaveštenja, uključite obaveštenja za aplikaciju %1$s" + "Promenite podešavanja" "Dodaj ikonu na početni ekran" "Za nove aplikacije" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 0a055a4413..ca7f7ed234 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -79,6 +79,12 @@ "Значкі апавяшчэнняў" "Уключана" "Выключана" + + + + + + "Дадаць значок на Галоўны экран" "Для новых праграм" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index d5df3f231a..c2248d85c1 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -79,6 +79,9 @@ "Точки за известия" "Включено" "Изключено" + "Необходим е достъп до известията" + "За да се показват точки за известия, включете известията за приложението %1$s" + "Промяна на настройките" "Добавяне на икона към началния екран" "За нови приложения" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index d4c5210593..87b72952fc 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -79,6 +79,12 @@ "বিজ্ঞপ্তি ডট" "চালু হয়েছে" "বন্ধ আছে" + + + + + + "হোম স্ক্রিনে আইকন যোগ করুন" "নতুন অ্যাপ্লিকেশানগুলির জন্যে" "আইকনের আকৃতি পরিবর্তন করুন" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index d91106bb20..a8e7f2ec3a 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -79,6 +79,9 @@ "Tačke za obavještenja" "Uključeno" "Isključeno" + "Potreban je pristup obavještenjima" + "Za prikaz tačaka obavještenja, uključite obavještenja za aplikacije za aplikaciju %1$s" + "Promijeni postavke" "Dodajte ikonu na početni ekran" "Za nove aplikacije" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index a6821dfbe3..49e742694f 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -79,6 +79,12 @@ "Punts de notificació" "Activat" "Desactivat" + + + + + + "Afegeix la icona a la pantalla d\'inici" "Per a les aplicacions noves" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 1a80e396c8..715f279ca6 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -79,6 +79,9 @@ "Puntíky s oznámeními" "Zapnuto" "Vypnuto" + "Je třeba udělit přístup k oznámením" + "Chcete-li zobrazovat puntíky s oznámeními, zapněte oznámení z aplikace %1$s" + "Změnit nastavení" "Přidat ikonu na plochu" "Pro nové aplikace" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 17316b3b5d..b97c5e95ed 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -79,6 +79,12 @@ "Underretningscirkler" "Til" "Fra" + + + + + + "Føj ikon til startskærmen" "For nye apps" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 72f7399dc0..ce868227e7 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -79,6 +79,9 @@ "App-Benachrichtigungspunkte" "Aktiviert" "Deaktiviert" + "Benachrichtigungszugriff erforderlich" + "Um dir Benachrichtigungspunkte anzeigen zu lassen, aktiviere die Benachrichtigungen für die App \"%1$s\"" + "Einstellungen ändern" "Symbol zu Startbildschirm hinzufügen" "Für neue Apps" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 406101c738..2ac0ab3cc4 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -79,6 +79,9 @@ "Κουκκίδες ειδοποίησης" "Ενεργή" "Ανενεργή" + "Απαιτείται πρόσβαση στις ειδοποιήσεις" + "Για να εμφανιστούν οι Κουκκίδες ειδοποίησης, ενεργοποιήστε τις κουκκίδες εφαρμογής για την εφαρμογή %1$s" + "Αλλαγή ρυθμίσεων" "Προσθήκη εικονιδίου στην Αρχική οθόνη" "Για νέες εφαρμογές" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 4e0ba03cd3..2c86f05c38 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -79,6 +79,9 @@ "Notification dots" "On" "Off" + "Notification access needed" + "To show Notification Dots, turn on app notifications for %1$s" + "Change settings" "Add icon to Home screen" "For new apps" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 4e0ba03cd3..2c86f05c38 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -79,6 +79,9 @@ "Notification dots" "On" "Off" + "Notification access needed" + "To show Notification Dots, turn on app notifications for %1$s" + "Change settings" "Add icon to Home screen" "For new apps" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 4e0ba03cd3..2c86f05c38 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -79,6 +79,9 @@ "Notification dots" "On" "Off" + "Notification access needed" + "To show Notification Dots, turn on app notifications for %1$s" + "Change settings" "Add icon to Home screen" "For new apps" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index bef475787a..f57718dfa5 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -79,6 +79,9 @@ "Puntos de notificación" "Activada" "Desactivada" + "Se necesita acceso a las notificaciones" + "Para mostrar los puntos de notificación, activa las notificaciones de la app para %1$s" + "Cambiar la configuración" "Agregar ícono a la pantalla principal" "Para nuevas apps" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index e49ac3f777..a7b2ab90e5 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -79,6 +79,9 @@ "Puntos de notificación" "Activada" "Desactivada" + "Se necesita acceso a las notificaciones" + "Para mostrar burbujas de notificación, activa las notificaciones de %1$s" + "Cambiar ajustes" "Añadir icono a la pantalla de inicio" "Para nuevas aplicaciones" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index f5ea4564d2..68bf644c53 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -79,6 +79,9 @@ "Märguandetäpid" "Sees" "Väljas" + "Vaja on juurdepääsu märguannetele" + "Märguandetäppide kuvamiseks lülitage sisse rakenduse %1$s märguanded" + "Seadete muutmine" "Lisa ikoon avaekraanile" "Uute rakenduste puhul" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 59fc221566..959cec049d 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -79,6 +79,12 @@ "Jakinarazteko biribiltxoak" "Aktibatuta" "Desaktibatuta" + + + + + + "Gehitu ikonoa hasierako pantailan" "Aplikazio berrietan" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 4edc4e7b3a..d4f6a45efc 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -79,6 +79,12 @@ "نقطه‌های اعلان" "روشن" "خاموش" + + + + + + "افزودن نماد به صفحه اصلی" "برای برنامه‌های جدید" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index f24cbae1e2..8992f616f4 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -79,6 +79,9 @@ "Pistemerkit" "Käytössä" "Pois käytöstä" + "Ilmoituksien käyttöoikeus tarvitaan" + "%1$s tarvitsee ilmoitusten käyttöoikeuden, jotta pistemerkkejä voidaan näyttää." + "Muuta asetuksia" "Lisää kuvake aloitusruutuun" "Uusille sovelluksille" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 5a3bd542c0..57ef4f89ab 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -79,6 +79,9 @@ "Points de notification" "Activé" "Désactivé" + "L\'accès aux notifications est requis" + "Pour afficher les points de notification, activez les notifications d\'application pour %1$s" + "Modifier les paramètres" "Ajouter l\'icône à l\'écran d\'accueil" "Pour les nouvelles applications" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 2897af0228..5daab91319 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -79,6 +79,9 @@ "Pastilles de notification" "Activé" "Désactivé" + "Accès aux notifications requis" + "Pour afficher les pastilles de notification, activez les notifications de l\'application %1$s" + "Modifier les paramètres" "Ajouter l\'icône à l\'écran d\'accueil" "Pour les nouvelles applications" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 9211ba1a15..5df3c87a32 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -79,6 +79,9 @@ "Puntos de notificacións" "Activado" "Desactivado" + "Necesítase acceso ás notificacións" + "Para que se mostren os puntos de notificacións, activa as notificacións da aplicación %1$s" + "Cambiar configuración" "Engadir icona á pantalla de inicio" "Para novas aplicacións" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index b491e42984..7ea7517a28 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -79,6 +79,12 @@ "સૂચના બિંદુઓ" "ચાલુ" "બંધ" + + + + + + "હોમ સ્ક્રીન પર આઇકન ઉમેરો" "નવી ઍપ્લિકેશનો માટે" "આઇકનનો આકાર બદલો" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 371f6b789b..bb9aa5c0ee 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -79,6 +79,9 @@ "नोटिफ़िकेशन बिंदु" "चालू" "बंद" + "नोटिफ़िकेशन एक्‍सेस ज़रूरी है" + "नोटिफ़िकेशन बिंदु दिखाने के लिए, %1$s के ऐप्लिकेशन नोटिफ़िकेशन चालू करें" + "सेटिंग बदलें" "होम स्क्रीन में आइकन जोड़ें" "नए ऐप्लिकेशन के लिए" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 09126778ff..0e272f217f 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -79,6 +79,9 @@ "Točke obavijesti" "Uključeno" "Isključeno" + "Potreban je pristup obavijestima" + "Za prikaz točaka obavijesti uključite obavijesti aplikacije %1$s" + "Promjena postavki" "Dodaj ikonu na početni zaslon" "Za nove aplikacije" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 3ffd5088c6..0f3f3b4810 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -79,6 +79,9 @@ "Értesítési pöttyök" "Bekapcsolva" "Kikapcsolva" + "Értesítésekhez való hozzáférésre van szükség" + "Az értesítési pöttyök megjelenítéséhez kapcsolja be a(z) %1$s alkalmazás értesítéseit" + "Beállítások módosítása" "Ikon hozzáadása a kezdőképernyőhöz" "Új alkalmazásoknál" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 1646f9e33e..6d708d08db 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -79,6 +79,9 @@ "Ծանուցման կետեր" "Միացված է" "Անջատված է" + "Անհրաժեշտ է ծանուցման թույլտվություն" + "Ծանուցման կետերը ցուցադրելու համար միացրեք հավելվածի ծանուցումները %1$s-ի համար" + "Փոխել կարգավորումները" "Ավելացնել պատկերակը Հիմնական էկրանին" "Նոր հավելվածների համար" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 5634963e5a..57ffa144e4 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -79,6 +79,9 @@ "Titik notifikasi" "Aktif" "Nonaktif" + "Perlu akses notifikasi" + "Guna menampilkan Titik Notifikasi, aktifkan notifikasi aplikasi untuk %1$s" + "Ubah setelan" "Tambahkan ikon ke layar Utama" "Untuk aplikasi baru" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 853cd3b41c..127f82fff5 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -79,6 +79,12 @@ "Tilkynningapunktar" "Kveikt" "Slökkt" + + + + + + "Bæta tákni á heimaskjáinn" "Fyrir ný forrit" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 8af098db59..ac04f31975 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -79,6 +79,9 @@ "Indicatori notifica" "Attiva" "Non attiva" + "Accesso alle notifiche necessario" + "Per mostrare gli indicatori di notifica, attiva le notifiche per l\'app %1$s" + "Modifica impostazioni" "Aggiungi icone alla schermata Home" "Per le nuove app" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 2425254fec..db06896258 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -79,6 +79,12 @@ "סימני הודעות" "מופעלת" "כבויה" + + + + + + "הוספת סמל במסך דף הבית" "לאפליקציות חדשות" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index cb5fa15a23..a11304d616 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -79,6 +79,9 @@ "通知ドット" "ON" "OFF" + "通知へのアクセス権限が必要" + "通知ドットを表示するには、「%1$s」のアプリ通知を ON にしてください" + "設定を変更" "ホーム画面にアイコンを追加" "新しいアプリをダウンロードしたときに" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 2634cdf431..0f10d2be24 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -79,6 +79,9 @@ "შეტყობინების ნიშნულები" "ჩართული" "გამორთული" + "საჭიროა შეტყობინებებზე წვდომა" + "შეტყობინებათა ნიშნულების საჩვენებლად, ჩართეთ აპის შეტყობინებები %1$s-ისთვის" + "პარამეტრების შეცვლა" "ხატულას მთავარ ეკრანზე დამატება" "ახალი აპებისთვის" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 66766b6dba..5638d8bf30 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -79,6 +79,9 @@ "Хабарландыру белгілері" "Қосулы" "Өшірулі" + "Хабарландыруға кіру рұқсаты қажет" + "Хабарландыру белгілерін көрсету үшін %1$s қолданбасының қолданба хабарландыруларын қосыңыз" + "Параметрлерді өзгерту" "Негізгі экранға белгіше енгізу" "Жаңа қолданбаларға арналған" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index ee44302eaf..ebdec7457a 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -79,6 +79,12 @@ "ស្លាកជូនដំណឹង" "បើក" "បិទ" + + + + + + "បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម" "សម្រាប់កម្មវិធីថ្មី" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 6c89b7bd13..e8ebe62fe4 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -79,6 +79,12 @@ "ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳು" "ಆನ್" "ಆಫ್" + + + + + + "ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ" "ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ" "ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 26535f0d4f..24546f4acb 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -79,6 +79,9 @@ "알림 표시 점" "사용" "사용 안함" + "알림 액세스 권한 필요" + "알림 표시점을 표시하려면 %1$s의 앱 알림을 사용 설정하세요." + "설정 변경" "홈 화면에 아이콘 추가" "새로 설치한 앱에 적용" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index cc15f04479..dfc8691919 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -79,6 +79,9 @@ "Эскертме белгилери" "Күйүк" "Өчүк" + "Эскертмелерге уруксат берилиши керек" + "Эскертме белгилерин көрсөтүү максатында, %1$s үчүн колдонмонун эскертмелерин күйгүзүү керек" + "Жөндөөлөрдү өзгөртүү" "Башкы экранга сүрөтчө кошуу" "Жаңы колдонмолор үчүн" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 0b294344e7..c5a5543223 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -79,6 +79,12 @@ "ຈຸດການແຈ້ງເຕືອນ" "ເປີດ" "ປິດ" + + + + + + "ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ" "ສຳລັບແອັບໃໝ່" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 945cbf6ea4..8402e45d62 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -79,6 +79,12 @@ "Pranešimų taškai" "Įjungta" "Išjungta" + + + + + + "Pridėti piktogr. prie pagrindinio ekrano" "Skirta naujoms programoms" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index e3c3482a7a..580d5b7d19 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -79,6 +79,9 @@ "Paziņojumu punkti" "Ieslēgts" "Izslēgts" + "Nepieciešama piekļuve paziņojumiem" + "Lai tiktu rādīti paziņojumu punkti, ieslēdziet paziņojumus lietotnei %1$s." + "Mainīt iestatījumus" "Pievienot ikonu sākuma ekrānā" "Jaunām lietotnēm" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index abd1d35dde..ebc1fcc83a 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -79,6 +79,9 @@ "Точки за известување" "Вклучено" "Исклучено" + "Потребен е пристап до известувањата" + "За да се прикажуваат „Точки за известување“, вклучете ги известувањата за апликацијата %1$s" + "Промени ги поставките" "Додајте икона на почетниот екран" "За нови апликации" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index bad7e480a1..6e59b26705 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -79,6 +79,12 @@ "അറിയിപ്പ് ഡോട്ടുകൾ" "ഓൺ" "ഓഫ്" + + + + + + "ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക" "പുതിയ ആപ്പുകൾക്ക്" "ഐക്കണിന്റെ ആകാരം മാറ്റുക" diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 264e55a86e..a8d4b3cf6e 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -79,6 +79,9 @@ "Мэдэгдлийн цэг" "Асаалттай" "Унтраалттай" + "Мэдэгдлийн хандалт шаардлагатай" + "Мэдэгдлийн цэгийг харуулахын тулд %1$s-д аппын мэдэгдлийг асаана уу" + "Тохиргоог өөрчлөх" "Нүүр хуудаст дүрс тэмдэг нэмэх" "Шинэ аппад зориулсан" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index b23ea965d1..f820555696 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -79,6 +79,12 @@ "सूचना बिंदू" "चालू" "बंद" + + + + + + "मुख्य स्क्रीनवर चिन्ह जोडा" "नवीन अॅप्ससाठी" "चिन्हाचा आकार बदला" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 0928b7dc8b..5346d2bbef 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -79,6 +79,9 @@ "Titik pemberitahuan" "Hidup" "Mati" + "Akses pemberitahuan diperlukan" + "Untuk menunjukkan Titik Pemberitahuan, hidupkan pemberitahuan apl untuk %1$s" + "Tukar tetapan" "Tambahkan ikon pada Skrin Utama" "Untuk apl baharu" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index a4648b15a2..00cadd6fb7 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -79,6 +79,12 @@ "အကြောင်းကြားချက်အမှတ်အသားများ" "ဖွင့်ထားသည်" "ပိတ်ထားသည်" + + + + + + "ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်" "အက်ပ်အသစ်များအတွက်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 2b776d64ad..87afa2e7fc 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -79,6 +79,12 @@ "Varselsprikker" "På" "Av" + + + + + + "Legg til ikon på startsiden" "For nye apper" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 558b6cf8e5..2490875b89 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -79,6 +79,12 @@ "सूचनाको प्रतीक जनाउने थोप्लोहरू" "सक्रिय छ" "निष्क्रिय छ" + + + + + + "गृह स्क्रिनमा आइकन थप्नुहोस्" "नयाँ अनुप्रयोगका लागि" "आइकनको आकार परिवर्तन गर्नुहोस्" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 0180bb4c28..5370528d4c 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -79,6 +79,9 @@ "Meldingsstipjes" "Aan" "Uit" + "Toegang tot meldingen vereist" + "Als je meldingsstipjes wilt weergeven, schakel je app-meldingen in voor %1$s" + "Instellingen wijzigen" "Pictogram toevoegen aan startscherm" "Voor nieuwe apps" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index 05fe509a27..76f0b68313 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -79,6 +79,12 @@ "ਸੂਚਨਾ ਬਿੰਦੂ" "ਚਾਲੂ" "ਬੰਦ" + + + + + + "ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index f8a7f66e9f..41c6ebe6a7 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -79,6 +79,9 @@ "Plakietki z powiadomieniami" "Włączono" "Wyłączono" + "Wymagany jest dostęp do powiadomień" + "Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji %1$s" + "Zmień ustawienia" "Dodaj ikonę do ekranu głównego" "W przypadku nowych aplikacji" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 5c37eb83af..e12bc540e0 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -79,6 +79,9 @@ "Pontos de notificação" "Ativada" "Desativada" + "Acesso a notificações necessário" + "Para mostrar os Pontos de notificação, ative as notificações de aplicações para o %1$s" + "Alterar definições" "Adicionar ícone ao ecrã principal" "Para novas aplicações" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index fd24066583..61f79e2258 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -79,6 +79,12 @@ "Pontos de notificação" "Ativado" "Desativado" + + + + + + "Adicionar ícone à tela inicial" "Para novos apps" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index e7b8144254..0ce4c1f332 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -79,6 +79,9 @@ "Puncte de notificare" "Activat" "Dezactivat" + "Este necesar accesul la notificări" + "Pentru a afișa punctele de notificare, activați notificările din aplicație pentru %1$s" + "Modificați setările" "Adaugă pictograme în ecranul de pornire" "Pentru aplicații noi" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 07ff761577..71d1a72b20 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -79,6 +79,9 @@ "Значки уведомлений" "ВКЛ" "ВЫКЛ" + "Нет доступа к уведомлениям" + "Чтобы показывать значки уведомлений, включите уведомления в приложении \"%1$s\"" + "Изменить настройки" "Добавлять значки" "Добавлять значки установленных приложений на главный экран." diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 3b0425e33b..710c76c337 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -79,6 +79,9 @@ "දැනුම්දීම් තිත්" "ක්‍රියාත්මකයි" "ක්‍රියාවිරහිතයි" + "දැනුම්දීම් ප්‍රවේශය අවශ්‍යයි" + "දැනුම්දීම් තිත් පෙන්වීමට, %1$s සඳහා යෙදුම් දැනුම්දීම් සබල කරන්න" + "සැකසීම් වෙනස් කරන්න" "මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න" "නව යෙදුම් සඳහා" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index b7999f4313..502ac27d8a 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -79,6 +79,9 @@ "Bodky upozornení" "Zapnuté" "Vypnuté" + "Vyžaduje sa prístup k upozorneniam" + "Ak chcete, aby sa zobrazovali bodky upozornení, zapnite upozornenia aplikácie %1$s" + "Zmeniť nastavenia" "Pridať ikonu na plochu" "Pri inštalácii novej aplikácie" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 927b3893a8..6efa93c937 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -79,6 +79,12 @@ "Obvestilne pike" "Vklopljeno" "Izklopljeno" + + + + + + "Dodaj ikono na začetni zaslon" "Za nove aplikacije" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 1400557c0c..46b04dbbc9 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -79,6 +79,12 @@ "Pikat e njoftimeve" "Aktiv" "Joaktiv" + + + + + + "Shto ikonë në ekranin bazë" "Për aplikacionet e reja" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 2cfaee654c..7dd327958c 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -79,6 +79,9 @@ "Тачке за обавештења" "Укључено" "Искључено" + "Потребан је приступ за обавештења" + "Да бисте приказали тачке за обавештења, укључите обавештења за апликацију %1$s" + "Промените подешавања" "Додај икону на почетни екран" "За нове апликације" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index aa6e99a497..ae50060d3d 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -79,6 +79,9 @@ "Aviseringsprickar" "På" "Av" + "Åtkomst till aviseringar krävs" + "Aktivera appaviseringar för %1$s om du vill att aviseringsprickar ska visas" + "Ändra inställningar" "Lägg till ikonen på startskärmen" "För nya appar" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 3b6d373db8..2ecd0acb37 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -81,6 +81,9 @@ "Vitone vya arifa" "Imewashwa" "Imezimwa" + "Inahitaji idhini ya kufikia arifa" + "Ili kuonyesha Vitone vya Arifa, washa kipengele cha arifa za programu katika %1$s" + "Badilisha mipangilio" "Ongeza aikoni kwenye Skrini ya kwanza" "Kwa ajili ya programu mpya" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index be8e82df6c..eeca4299c5 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -79,6 +79,9 @@ "அறிவிப்புப் புள்ளிகள்" "இயக்கப்பட்டுள்ளது" "முடக்கப்பட்டுள்ளது" + "அறிவிப்பிற்கான அணுகல் தேவை" + "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்" + "அமைப்புகளை மாற்று" "முகப்புத் திரையில் ஐகானைச் சேர்" "புதிய பயன்பாடுகளுக்கு" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 15f3de7dab..e654c7322b 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -79,6 +79,12 @@ "నోటిఫికేషన్ డాట్‌లు" "ఆన్" "ఆఫ్" + + + + + + "హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు" "కొత్త అనువర్తనాల కోసం" "చిహ్న ఆకారాన్ని మార్చు" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index e03931e049..3db6db8a2e 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -76,9 +76,12 @@ "อนุญาตให้หมุนหน้าจอหลัก" "เมื่อหมุนโทรศัพท์" "การตั้งค่าการแสดงผลปัจจุบันไม่อนุญาตให้มีการหมุน" - "จุดการแจ้งเตือน" + "จุดแจ้งเตือน" "เปิด" "ปิด" + "ต้องได้รับสิทธิ์เข้าถึงการแจ้งเตือน" + "เปิดการแจ้งเตือนแอปของ %1$s เพื่อแสดงจุดแจ้งเตือน" + "เปลี่ยนการตั้งค่า" "เพิ่มไอคอนในหน้าจอหลัก" "สำหรับแอปใหม่" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 12bc0133f1..f146f9a749 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -79,6 +79,9 @@ "Mga notification dot" "Naka-on" "Naka-off" + "Kinakailangan ng access sa notification" + "Upang ipakita ang Mga Notification Dot, i-on ang mga notification ng app para sa %1$s" + "Baguhin ang mga setting" "Idagdag ang icon sa Home screen" "Para sa mga bagong app" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index b245bfff11..168d3a5449 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -79,6 +79,9 @@ "Bildirim noktaları" "Açık" "Kapalı" + "Bildirim erişimi gerekli" + "Bildirim Noktaları\'nı göstermek için %1$s uygulamasının bildirimlerini açın" + "Ayarları değiştir" "Ana ekrana simge ekle" "Yeni uygulamalar için" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 8d33910892..0d8f15c739 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -79,6 +79,9 @@ "Значки сповіщень" "Увімкнено" "Вимкнено" + "Потрібен доступ до сповіщень" + "Щоб показувати значки сповіщень, увімкніть сповіщення в додатку %1$s" + "Змінити налаштування" "Додати значок на головний екран" "Для нових додатків" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 84966b2888..769d7740e3 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -79,6 +79,12 @@ "اطلاعاتی ڈاٹس" "آن" "آف" + + + + + + "آئیکن کو ہوم اسکرین میں شامل کریں" "نئی ایپس کیلئے" "آئیکن کی شکل تبدیل کریں" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index a4725db53c..c3c48061d2 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -79,6 +79,9 @@ "Bildirishnoma nuqtalari" "Yoniq" "O‘chiq" + "Bildirishnomalarga ruxsat berilmagan" + "Bildirishnoma nuqtalarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" + "Sozlamalarni o‘zgartirish" "Bosh ekranga ikonka qo‘shish" "Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index f6cb8f23eb..fe2cfa1bb2 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -79,6 +79,9 @@ "Dấu chấm thông báo" "Đang bật" "Đã tắt" + "Cần quyền truy cập thông báo" + "Để hiển thị Dấu chấm thông báo, hãy bật thông báo ứng dụng cho %1$s" + "Thay đổi cài đặt" "Thêm biểu tượng vào màn hình chính" "Cho ứng dụng mới" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 5dcef74d12..15e24a1008 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -79,6 +79,9 @@ "通知圆点" "开启" "关闭" + "需要获取通知使用权" + "要显示通知圆点,请开启%1$s的应用通知功能" + "更改设置" "将图标添加到主屏幕" "适用于新应用" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 2bbc160f72..4087180050 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -79,6 +79,9 @@ "通知圓點" "開啟" "關閉" + "需要獲取通知存取權" + "如要顯示「通知圓點」,請開啟「%1$s」的應用程式通知功能" + "變更設定" "將圖示加到主畫面" "適用於新安裝的應用程式" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index f1605f686b..f123d4066b 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -79,6 +79,9 @@ "通知圓點" "已啟用" "已停用" + "需要取得通知存取權" + "如要顯示通知圓點,請開啟「%1$s」的應用程式通知功能" + "變更設定" "將圖示加到主螢幕" "適用於新安裝的應用程式" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 12d14bdaf9..a32093c8c9 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -79,6 +79,9 @@ "Amachashazi esaziso" "Kuvuliwe" "Kuvaliwe" + "Ukufinyelela izaziso kuyadingeka" + "Ukuze ubonisa amcashazi esaziso, vula izaziso zohlelo lokusebenza ze-%1$s" + "Shintsha izilungiselelo" "Engeza isithonjana eskrinini sasekhaya" "Kwezinhlelo zokusebenza ezintsha" From 537884b981456a46f51123ee91bbdc2c3df1d2ea Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 19 Jul 2017 23:59:17 -0700 Subject: [PATCH 002/885] Import translations. DO NOT MERGE Change-Id: I16c285f9aee55edb6053a4a916b45dd0dc0bb09c Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-be/strings.xml | 9 +++------ res/values-bn/strings.xml | 9 +++------ res/values-ca/strings.xml | 9 +++------ res/values-da/strings.xml | 9 +++------ res/values-eu/strings.xml | 9 +++------ res/values-fa/strings.xml | 9 +++------ res/values-gu/strings.xml | 9 +++------ res/values-is/strings.xml | 9 +++------ res/values-iw/strings.xml | 9 +++------ res/values-km/strings.xml | 9 +++------ res/values-kn/strings.xml | 9 +++------ res/values-lo/strings.xml | 9 +++------ res/values-lt/strings.xml | 9 +++------ res/values-ml/strings.xml | 9 +++------ res/values-mr/strings.xml | 9 +++------ res/values-my/strings.xml | 9 +++------ res/values-nb/strings.xml | 9 +++------ res/values-ne/strings.xml | 9 +++------ res/values-pa/strings.xml | 9 +++------ res/values-pt/strings.xml | 9 +++------ res/values-sl/strings.xml | 9 +++------ res/values-sq/strings.xml | 9 +++------ res/values-te/strings.xml | 9 +++------ res/values-ur/strings.xml | 9 +++------ 24 files changed, 72 insertions(+), 144 deletions(-) diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index ca7f7ed234..4b14a8c96a 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -79,12 +79,9 @@ "Значкі апавяшчэнняў" "Уключана" "Выключана" - - - - - - + "Патрабуецца доступ да апавяшчэнняў" + "Каб паказваліся значкі апавяшчэнняў, уключыце апавяшчэнні праграм для %1$s" + "Змяніць налады" "Дадаць значок на Галоўны экран" "Для новых праграм" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 87b72952fc..02a95f2365 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -79,12 +79,9 @@ "বিজ্ঞপ্তি ডট" "চালু হয়েছে" "বন্ধ আছে" - - - - - - + "বিজ্ঞপ্তিতে অ্যাক্সেস প্রয়োজন" + "বিজ্ঞপ্তির ডটগুলি দেখানোর জন্য, %1$s এর অ্যাপ বিজ্ঞপ্তি চালু করুন" + "সেটিংস পরিবর্তন করুন" "হোম স্ক্রিনে আইকন যোগ করুন" "নতুন অ্যাপ্লিকেশানগুলির জন্যে" "আইকনের আকৃতি পরিবর্তন করুন" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 49e742694f..ad6c9d107e 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -79,12 +79,9 @@ "Punts de notificació" "Activat" "Desactivat" - - - - - - + "Cal que tingui accés a les notificacions" + "Per veure els punts de notificació, activa les notificacions de l\'aplicació %1$s" + "Canvia la configuració" "Afegeix la icona a la pantalla d\'inici" "Per a les aplicacions noves" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index b97c5e95ed..04b1631312 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -79,12 +79,9 @@ "Underretningscirkler" "Til" "Fra" - - - - - - + "Kræver adgang til underretninger" + "Hvis du vil se underretningscirkler, skal du aktivere appunderretninger for %1$s" + "Skift indstillinger" "Føj ikon til startskærmen" "For nye apps" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 959cec049d..f2295863b1 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -79,12 +79,9 @@ "Jakinarazteko biribiltxoak" "Aktibatuta" "Desaktibatuta" - - - - - - + "Jakinarazpenetarako sarbidea behar da" + "Jakinarazteko biribiltxoak ikusteko, aktibatu %1$s aplikazioaren jakinarazpenak" + "Aldatu ezarpenak" "Gehitu ikonoa hasierako pantailan" "Aplikazio berrietan" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index d4f6a45efc..05c8e08cba 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -79,12 +79,9 @@ "نقطه‌های اعلان" "روشن" "خاموش" - - - - - - + "دسترسی به اعلان نیاز است" + "برای نمایش «نقطه‌های اعلان»، اعلان‌های برنامه را برای %1$s روشن کنید" + "تغییر تنظیمات" "افزودن نماد به صفحه اصلی" "برای برنامه‌های جدید" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 7ea7517a28..587d16eb4e 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -79,12 +79,9 @@ "સૂચના બિંદુઓ" "ચાલુ" "બંધ" - - - - - - + "નોટિફિકેશનનો ઍક્સેસની જરૂરી છે" + "નોટિફિકેશન માટેનું ચિહ્ન બતાવવા હેતુ, %1$s માટેની ઍપ્લિકેશન નોટિફિકેશન ચાલુ કરો" + "સેટિંગ્સ બદલો" "હોમ સ્ક્રીન પર આઇકન ઉમેરો" "નવી ઍપ્લિકેશનો માટે" "આઇકનનો આકાર બદલો" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 127f82fff5..5dcfb4e6ef 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -79,12 +79,9 @@ "Tilkynningapunktar" "Kveikt" "Slökkt" - - - - - - + "Aðgangs að tilkynningum er krafist" + "Til að sýna tilkynningarpunkta skaltu kveikja á forritstilkynningum fyrir %1$s" + "Breyta stillingum" "Bæta tákni á heimaskjáinn" "Fyrir ný forrit" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index db06896258..83c4296078 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -79,12 +79,9 @@ "סימני הודעות" "מופעלת" "כבויה" - - - - - - + "נדרשת גישה להודעות" + "כדי להציג את סימני ההודעות, יש להפעיל הודעות מהאפליקציה %1$s" + "שנה את ההגדרות" "הוספת סמל במסך דף הבית" "לאפליקציות חדשות" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index ebdec7457a..c60f09fa7a 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -79,12 +79,9 @@ "ស្លាកជូនដំណឹង" "បើក" "បិទ" - - - - - - + "តម្រូវ​ឲ្យមាន​សិទ្ធិចូល​ប្រើប្រាស់​ការជូនដំណឹង" + "ដើម្បីបង្ហាញស្លាកជូនដំណឹង សូមបើកការជូនដំណឹងកម្មវិធីសម្រាប់ %1$s" + "ប្ដូរ​ការកំណត់" "បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម" "សម្រាប់កម្មវិធីថ្មី" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index e8ebe62fe4..7566503b0b 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -79,12 +79,9 @@ "ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳು" "ಆನ್" "ಆಫ್" - - - - - - + "ಅಧಿಸೂಚನೆ ಪ್ರವೇಶ ಅಗತ್ಯವಿದೆ" + "ಅಧಿಸೂಚನೆ ಚುಕ್ಕೆಗಳನ್ನು ತೋರಿಸಲು, %1$s ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್‌ ಮಾಡಿ" + "ಸೆಟ್ಟಿಂಗ್‌‌ಗಳನ್ನು ಬದಲಾಯಿಸಿ" "ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ" "ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ" "ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index c5a5543223..66a504841b 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -79,12 +79,9 @@ "ຈຸດການແຈ້ງເຕືອນ" "ເປີດ" "ປິດ" - - - - - - + "ຕ້ອງໃຊ້ການເຂົ້າເຖິງການແຈ້ງເຕືອນ" + "ເພື່ອສະແດງຈຸດການແຈ້ງເຕືອນ, ໃຫ້ເປີດການແຈ້ງເຕືອນສຳລັບ %1$s" + "ບັນທຶກການຕັ້ງຄ່າ" "ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ" "ສຳລັບແອັບໃໝ່" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 8402e45d62..c346ed954e 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -79,12 +79,9 @@ "Pranešimų taškai" "Įjungta" "Išjungta" - - - - - - + "Reikalinga prieiga prie pranešimų" + "Kad būtų rodomi pranešimų taškai, įjunkite programos „%1$s“ pranešimus." + "Keisti nustatymus" "Pridėti piktogr. prie pagrindinio ekrano" "Skirta naujoms programoms" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 6e59b26705..62bcc8cbe2 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -79,12 +79,9 @@ "അറിയിപ്പ് ഡോട്ടുകൾ" "ഓൺ" "ഓഫ്" - - - - - - + "അറിയിപ്പിനായുള്ള ആക്‌സസ് ആവശ്യമാണ്" + "അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുന്നതിന്, %1$s എന്നയാളിനായുള്ള ആപ്പ് അറിയിപ്പുകൾ ഓണാക്കുക" + "ക്രമീകരണം മാറ്റുക" "ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക" "പുതിയ ആപ്പുകൾക്ക്" "ഐക്കണിന്റെ ആകാരം മാറ്റുക" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index f820555696..3435061b84 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -79,12 +79,9 @@ "सूचना बिंदू" "चालू" "बंद" - - - - - - + "सूचनांच्या अ‍ॅक्सेसची आवश्यकता आहे" + "सूचना बिंदू दाखवण्यासाठी, %1$s साठी अ‍ॅप सूचना चालू करा" + "सेटिंग्ज बदला" "मुख्य स्क्रीनवर चिन्ह जोडा" "नवीन अॅप्ससाठी" "चिन्हाचा आकार बदला" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index 00cadd6fb7..7dd66892c7 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -79,12 +79,9 @@ "အကြောင်းကြားချက်အမှတ်အသားများ" "ဖွင့်ထားသည်" "ပိတ်ထားသည်" - - - - - - + "အကြောင်းကြားချက် အသုံးပြုခွင့် လိုအပ်သည်" + "အကြောင်းကြားချက် အစက်များကို ပြသရန် %1$s အတွက် အက်ပ်အကြောင်းကြားချက်များကို ဖွင့်ပါ" + "ဆက်တင်များ ပြောင်းရန်" "ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်" "အက်ပ်အသစ်များအတွက်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 87afa2e7fc..21c2036ab1 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -79,12 +79,9 @@ "Varselsprikker" "På" "Av" - - - - - - + "Tilgang til varsler er nødvendig" + "Slå på appvarsler for %1$s for å vise varselsprikker" + "Endre innstillingene" "Legg til ikon på startsiden" "For nye apper" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 2490875b89..b28e0e2b46 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -79,12 +79,9 @@ "सूचनाको प्रतीक जनाउने थोप्लोहरू" "सक्रिय छ" "निष्क्रिय छ" - - - - - - + "सूचनासम्बन्धी पहुँच आवश्यक हुन्छ" + "सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउन %1$s को अनुप्रयोगसम्बन्धी सूचनाहरूलाई सक्रिय गर्नुहोस्" + "सेटिङहरू बदल्नुहोस्" "गृह स्क्रिनमा आइकन थप्नुहोस्" "नयाँ अनुप्रयोगका लागि" "आइकनको आकार परिवर्तन गर्नुहोस्" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index 76f0b68313..aaf4c92cb2 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -79,12 +79,9 @@ "ਸੂਚਨਾ ਬਿੰਦੂ" "ਚਾਲੂ" "ਬੰਦ" - - - - - - + "ਸੂਚਨਾ ਪਹੁੰਚ ਲੋੜੀਂਦੀ ਹੈ" + "ਸੂਚਨਾ ਬਿੰਦੀਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" + "ਸੈਟਿੰਗਾਂ ਬਦਲੋ" "ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 61f79e2258..ac439192e3 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -79,12 +79,9 @@ "Pontos de notificação" "Ativado" "Desativado" - - - - - - + "Acesso a notificações necessário" + "Para mostrar pontos de notificação, ative as notificações de app para %1$s" + "Alterar configurações" "Adicionar ícone à tela inicial" "Para novos apps" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 6efa93c937..a16054dd20 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -79,12 +79,9 @@ "Obvestilne pike" "Vklopljeno" "Izklopljeno" - - - - - - + "Potreben je dostop do obvestil" + "Za prikaz obvestilnih pik vklopite obvestila aplikacije %1$s" + "Spremeni nastavitve" "Dodaj ikono na začetni zaslon" "Za nove aplikacije" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 46b04dbbc9..8480288ce4 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -79,12 +79,9 @@ "Pikat e njoftimeve" "Aktiv" "Joaktiv" - - - - - - + "Nevojitet qasja në njoftime" + "Për të shfaqur \"Pikat e njoftimeve\", aktivizo njoftimet e aplikacionit për %1$s" + "Ndrysho cilësimet" "Shto ikonë në ekranin bazë" "Për aplikacionet e reja" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index e654c7322b..94e09d8660 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -79,12 +79,9 @@ "నోటిఫికేషన్ డాట్‌లు" "ఆన్" "ఆఫ్" - - - - - - + "నోటిఫికేషన్ యాక్సెస్ అవసరం" + "నోటిఫికేషన్ డాట్‌లను చూపించడానికి %1$sకు యాప్ నోటిఫికేషన్‌లను ఆన్ చేయండి" + "సెట్టింగ్‌లను మార్చు" "హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు" "కొత్త అనువర్తనాల కోసం" "చిహ్న ఆకారాన్ని మార్చు" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 769d7740e3..f3d6d3f3cb 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -79,12 +79,9 @@ "اطلاعاتی ڈاٹس" "آن" "آف" - - - - - - + "اطلاعاتی رسائی درکار ہے" + "اطلاعاتی ڈاٹس دکھانے کی خاطر %1$s کیلئے ایپ کی اطلاعات آن کریں" + "ترتیبات تبدیل کریں" "آئیکن کو ہوم اسکرین میں شامل کریں" "نئی ایپس کیلئے" "آئیکن کی شکل تبدیل کریں" From 09d8e4977ba67b6ecf16f781aca880ba3ed9d2c5 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 20 Jul 2017 14:14:01 -0700 Subject: [PATCH 003/885] Remove pulldown search feature flag Bug: 63713013 Change-Id: I81ad92678ed7aa429a7a85477121fa49502a6a92 --- src/com/android/launcher3/Workspace.java | 28 ------ .../launcher3/util/VerticalFlingDetector.java | 88 ------------------- .../launcher3/config/FeatureFlags.java | 2 - 3 files changed, 118 deletions(-) delete mode 100644 src/com/android/launcher3/util/VerticalFlingDetector.java diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index f781a3d1de..6d9e4c9237 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -82,7 +82,6 @@ import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Thunk; -import com.android.launcher3.util.VerticalFlingDetector; import com.android.launcher3.util.WallpaperOffsetInterpolator; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -568,33 +567,6 @@ public class Workspace extends PagedView } // Add the first page CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0); - if (FeatureFlags.PULLDOWN_SEARCH) { - firstPage.setOnTouchListener(new VerticalFlingDetector(mLauncher) { - // detect fling when touch started from empty space - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (workspaceInModalState()) return false; - if (shouldConsumeTouch(v)) return true; - if (super.onTouch(v, ev)) { - mLauncher.startSearch("", false, null, false); - return true; - } - return false; - } - }); - firstPage.setOnInterceptTouchListener(new VerticalFlingDetector(mLauncher) { - // detect fling when touch started from on top of the icons - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (shouldConsumeTouch(v)) return true; - if (super.onTouch(v, ev)) { - mLauncher.startSearch("", false, null, false); - return true; - } - return false; - } - }); - } // Always add a QSB on the first screen. if (qsb == null) { // In transposed layout, we add the QSB in the Grid. As workspace does not touch the diff --git a/src/com/android/launcher3/util/VerticalFlingDetector.java b/src/com/android/launcher3/util/VerticalFlingDetector.java deleted file mode 100644 index 7236c2d1bb..0000000000 --- a/src/com/android/launcher3/util/VerticalFlingDetector.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2016 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.Context; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; - -public class VerticalFlingDetector implements View.OnTouchListener { - - private static final float CUSTOM_SLOP_MULTIPLIER = 2.2f; - private static final int SEC_IN_MILLIS = 1000; - - private VelocityTracker mVelocityTracker; - private float mMinimumFlingVelocity; - private float mMaximumFlingVelocity; - private float mDownX, mDownY; - private boolean mShouldCheckFling; - private double mCustomTouchSlop; - - public VerticalFlingDetector(Context context) { - ViewConfiguration vc = ViewConfiguration.get(context); - mMinimumFlingVelocity = vc.getScaledMinimumFlingVelocity(); - mMaximumFlingVelocity = vc.getScaledMaximumFlingVelocity(); - mCustomTouchSlop = CUSTOM_SLOP_MULTIPLIER * vc.getScaledTouchSlop(); - } - - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - mDownX = ev.getX(); - mDownY = ev.getY(); - mShouldCheckFling = false; - break; - case MotionEvent.ACTION_MOVE: - if (mShouldCheckFling) { - break; - } - if (Math.abs(ev.getY() - mDownY) > mCustomTouchSlop && - Math.abs(ev.getY() - mDownY) > Math.abs(ev.getX() - mDownX)) { - mShouldCheckFling = true; - } - break; - case MotionEvent.ACTION_UP: - if (mShouldCheckFling) { - mVelocityTracker.computeCurrentVelocity(SEC_IN_MILLIS, mMaximumFlingVelocity); - // only when fling is detected in down direction - if (mVelocityTracker.getYVelocity() > mMinimumFlingVelocity) { - cleanUp(); - return true; - } - } - // fall through. - case MotionEvent.ACTION_CANCEL: - cleanUp(); - } - return false; - } - - private void cleanUp() { - if (mVelocityTracker == null) { - return; - } - mVelocityTracker.recycle(); - mVelocityTracker = null; - } -} diff --git a/src_flags/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java index 42a110cfe0..cea18ac0fe 100644 --- a/src_flags/com/android/launcher3/config/FeatureFlags.java +++ b/src_flags/com/android/launcher3/config/FeatureFlags.java @@ -49,8 +49,6 @@ public final class FeatureFlags { public static final boolean QSB_ON_FIRST_SCREEN = true; // When enabled the all-apps icon is not added to the hotseat. public static final boolean NO_ALL_APPS_ICON = true; - // When enabled fling down gesture on the first workspace triggers search. - public static final boolean PULLDOWN_SEARCH = false; // When enabled the status bar may show dark icons based on the top of the wallpaper. public static final boolean LIGHT_STATUS_BAR = false; // When enabled icons are badged with the number of notifications associated with that app. From 9e03eef6382f3b354707401d690bc64967f132ed Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 21 Jul 2017 16:49:11 -0700 Subject: [PATCH 004/885] Import translations. DO NOT MERGE Change-Id: I3a0cb9478ff172919957bd2aad510fcef50c5b71 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-af/strings.xml | 9 ++++++--- res/values-am/strings.xml | 9 ++++++--- res/values-ar/strings.xml | 9 ++++++--- res/values-az/strings.xml | 9 ++++++--- res/values-b+sr+Latn/strings.xml | 9 ++++++--- res/values-be/strings.xml | 9 ++++++--- res/values-bg/strings.xml | 9 ++++++--- res/values-bn/strings.xml | 9 ++++++--- res/values-bs/strings.xml | 9 ++++++--- res/values-ca/strings.xml | 9 ++++++--- res/values-cs/strings.xml | 9 ++++++--- res/values-da/strings.xml | 9 ++++++--- res/values-de/strings.xml | 9 ++++++--- res/values-el/strings.xml | 9 ++++++--- res/values-en-rAU/strings.xml | 9 ++++++--- res/values-en-rGB/strings.xml | 9 ++++++--- res/values-en-rIN/strings.xml | 9 ++++++--- res/values-es-rUS/strings.xml | 9 ++++++--- res/values-es/strings.xml | 9 ++++++--- res/values-et/strings.xml | 9 ++++++--- res/values-eu/strings.xml | 9 ++++++--- res/values-fa/strings.xml | 9 ++++++--- res/values-fi/strings.xml | 9 ++++++--- res/values-fr-rCA/strings.xml | 9 ++++++--- res/values-fr/strings.xml | 9 ++++++--- res/values-gl/strings.xml | 9 ++++++--- res/values-gu/strings.xml | 9 ++++++--- res/values-hi/strings.xml | 9 ++++++--- res/values-hr/strings.xml | 9 ++++++--- res/values-hu/strings.xml | 9 ++++++--- res/values-hy/strings.xml | 9 ++++++--- res/values-in/strings.xml | 9 ++++++--- res/values-is/strings.xml | 9 ++++++--- res/values-it/strings.xml | 9 ++++++--- res/values-iw/strings.xml | 9 ++++++--- res/values-ja/strings.xml | 9 ++++++--- res/values-ka/strings.xml | 9 ++++++--- res/values-kk/strings.xml | 9 ++++++--- res/values-km/strings.xml | 9 ++++++--- res/values-kn/strings.xml | 9 ++++++--- res/values-ko/strings.xml | 9 ++++++--- res/values-ky/strings.xml | 9 ++++++--- res/values-lo/strings.xml | 9 ++++++--- res/values-lt/strings.xml | 9 ++++++--- res/values-lv/strings.xml | 9 ++++++--- res/values-mk/strings.xml | 9 ++++++--- res/values-ml/strings.xml | 9 ++++++--- res/values-mn/strings.xml | 9 ++++++--- res/values-mr/strings.xml | 9 ++++++--- res/values-ms/strings.xml | 9 ++++++--- res/values-my/strings.xml | 9 ++++++--- res/values-nb/strings.xml | 9 ++++++--- res/values-ne/strings.xml | 9 ++++++--- res/values-nl/strings.xml | 9 ++++++--- res/values-pa/strings.xml | 9 ++++++--- res/values-pl/strings.xml | 9 ++++++--- res/values-pt-rPT/strings.xml | 9 ++++++--- res/values-pt/strings.xml | 9 ++++++--- res/values-ro/strings.xml | 9 ++++++--- res/values-ru/strings.xml | 9 ++++++--- res/values-si/strings.xml | 9 ++++++--- res/values-sk/strings.xml | 9 ++++++--- res/values-sl/strings.xml | 9 ++++++--- res/values-sq/strings.xml | 9 ++++++--- res/values-sr/strings.xml | 9 ++++++--- res/values-sv/strings.xml | 9 ++++++--- res/values-sw/strings.xml | 9 ++++++--- res/values-ta/strings.xml | 9 ++++++--- res/values-te/strings.xml | 9 ++++++--- res/values-th/strings.xml | 9 ++++++--- res/values-tl/strings.xml | 9 ++++++--- res/values-tr/strings.xml | 9 ++++++--- res/values-uk/strings.xml | 9 ++++++--- res/values-ur/strings.xml | 9 ++++++--- res/values-uz/strings.xml | 9 ++++++--- res/values-vi/strings.xml | 9 ++++++--- res/values-zh-rCN/strings.xml | 9 ++++++--- res/values-zh-rHK/strings.xml | 9 ++++++--- res/values-zh-rTW/strings.xml | 9 ++++++--- res/values-zu/strings.xml | 9 ++++++--- 80 files changed, 480 insertions(+), 240 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 84faebe8f5..9024427e81 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -35,9 +35,12 @@ "%1$d breed by %2$d hoog" "Raak en hou om self te plaas" "Voeg outomaties by" - "Deursoek programme" - "Laai tans programme …" - "Geen programme gevind wat met \"%1$s\" ooreenstem nie" + + + + + + "Soek meer programme" "Kennisgewings" "Niks meer spasie op die tuisskerm nie." diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 76a9734cef..88bc33f700 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -35,9 +35,12 @@ "%1$d ስፋት በ%2$d ከፍታ" "ራስዎ ለማስቀመጥ ነክተው ይያዙት" "በራስ-ሰር አክል" - "መተግበሪያዎችን ይፈልጉ" - "መተግበሪያዎችን በመጫን ላይ..." - "ከ«%1$s» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም" + + + + + + "ተጨማሪ መተግበሪያዎች ይፈልጉ" "ማሳወቂያዎች" "በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index e51880aa5c..98106c5f0f 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -35,9 +35,12 @@ "‏العرض %1$d الطول %2$d" "المس مع الاستمرار للإضافة يدويًا" "إضافة تلقائيًا" - "البحث في التطبيقات" - "جارٍ تحميل التطبيقات…" - "لم يتم العثور على أية تطبيقات تتطابق مع \"%1$s\"" + + + + + + "البحث عن مزيد من التطبيقات" "الإشعارات" "ليس هناك مساحة أخرى في هذه الشاشة الرئيسية." diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 2a6615a2db..b33d8b5e77 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -35,9 +35,12 @@ "%2$d hündürlük %1$d enində" "Manual olaraq yerləşdirmək üçün toxunaraq basıb saxlayın" "Avtomatik əlavə edin" - "Tətbiq Axtarın" - "Tətbiqlər endirilir..." - "\"%1$s\" sorğusuna uyğun Tətbiqlər tapılmadı" + + + + + + "Daha çox tətbiq üçün axtarış edin" "Bildirişlər" "Bu Əsas ekranda boş yer yoxdur." diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index f17c5f03d4..4294499ed6 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -35,9 +35,12 @@ "širina od %1$d i visina od %2$d" "Dodirnite i zadržite da biste postavili ručno" "Automatski dodaj" - "Pretražite aplikacije" - "Aplikacije se učitavaju..." - "Nije pronađena nijedna aplikacija za „%1$s“" + + + + + + "Pretraži još aplikacija" "Obaveštenja" "Nema više prostora na ovom početnom ekranu." diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 4b14a8c96a..c9bd447228 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -35,9 +35,12 @@ "Шырына: %1$d, вышыня: %2$d" "Каб размясціць уручную, дакраніцеся і ўтрымлівайце" "Дадаць аўтаматычна" - "Пошук у Праграмах" - "Ідзе загрузка праграм…" - "Праграм, якія адпавядаюць запыту \"%1$s\", не знойдзена" + + + + + + "Шукаць іншыя праграмы" "Апавяшчэнні" "На гэтым Галоўным экране больш няма месца." diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index c2248d85c1..7883d7c92f 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -35,9 +35,12 @@ "Ширина %1$d и височина %2$d" "Докоснете и задръжте, за да поставите ръчно" "Автоматично добавяне" - "Търсене в приложенията" - "Приложенията се зареждат…" - "Няма намерени приложения, съответстващи на „%1$s“" + + + + + + "Търсене на още приложения" "Известия" "На този начален екран няма повече място." diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 02a95f2365..97bb90af24 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -35,9 +35,12 @@ "%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ" "নিজে যোগ করতে টাচ করে ধরে রাখুন" "স্বয়ংক্রিয়ভাবে যোগ করুন" - "অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন" - "অ্যাপ্লিকেশানগুলি লোড হচ্ছে..." - "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি" + + + + + + "আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন" "বিজ্ঞপ্তি" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index a8e7f2ec3a..032fafb8b4 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -35,9 +35,12 @@ "Širina %1$d, visina %2$d" "Dodirnite i držite da postavite ručno" "Dodaj automatski" - "Pretraži aplikacije" - "Aplikacije se učitavaju…" - "Nije pronađena nijedna aplikacija koja odgovara upitu \"%1$s\"" + + + + + + "Pretraži više aplikacija" "Obavještenja" "Na ovom početnom ekranu nema više prostora." diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index ad6c9d107e..0685c9027f 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -35,9 +35,12 @@ "%1$d d\'amplada per %2$d d\'alçada" "Toca i mantén premut l\'element per col·locar-lo manualment" "Afegeix automàticament" - "Cerca a les aplicacions" - "S\'estan carregant les aplicacions..." - "No s\'ha trobat cap aplicació que coincideixi amb %1$s" + + + + + + "Cerca més aplicacions" "Notificacions" "Ja no queda espai en aquesta pantalla d\'inici." diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 715f279ca6..7cef876d7f 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -35,9 +35,12 @@ "šířka %1$d, výška %2$d" "Chcete-li položku umístit ručně, klepněte na ni a podržte ji" "Přidat automaticky" - "Hledat aplikace" - "Načítání aplikací…" - "Dotazu „%1$s“ neodpovídají žádné aplikace" + + + + + + "Vyhledat další aplikace" "Oznámení" "Na této ploše již není místo." diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 04b1631312..3d22280a03 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -35,9 +35,12 @@ "%1$d i bredden og %2$d i højden" "Tryk, og hold fingeren nede for at placere manuelt" "Tilføj automatisk" - "Søg i Apps" - "Indlæser apps…" - "Der blev ikke fundet nogen apps, som matcher \"%1$s\"" + + + + + + "Søg efter flere apps" "Underretninger" "Der er ikke mere plads på denne startskærm." diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index ce868227e7..c67fca7f3a 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -35,9 +35,12 @@ "%1$d breit und %2$d hoch" "Zum manuellen Hinzufügen gedrückt halten" "Automatisch hinzufügen" - "In Apps suchen" - "Apps werden geladen..." - "Keine Apps für \"%1$s\" gefunden" + + + + + + "Weitere Apps suchen" "Benachrichtigungen" "Auf diesem Startbildschirm ist kein Platz mehr vorhanden." diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 2ac0ab3cc4..1f27f2c6c2 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -35,9 +35,12 @@ "Πλάτος %1$d επί ύψος %2$d" "Αγγίξτε παρατεταμένα για μη αυτόματη τοποθέτηση" "Αυτόματη προσθήκη" - "Αναζήτηση εφαρμογών" - "Φόρτωση εφαρμογών…" - "Δεν βρέθηκαν εφαρμογές για το ερώτημα \"%1$s\"" + + + + + + "Αναζήτηση περισσότερων εφαρμογών" "Ειδοποιήσεις" "Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη." diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 2c86f05c38..affa3926ae 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -35,9 +35,12 @@ "%1$d wide by %2$d high" "Touch & hold to place manually" "Add automatically" - "Search Apps" - "Loading Apps…" - "No Apps found matching \"%1$s\"" + + + + + + "Search for more apps" "Notifications" "No more room on this Home screen." diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 2c86f05c38..affa3926ae 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -35,9 +35,12 @@ "%1$d wide by %2$d high" "Touch & hold to place manually" "Add automatically" - "Search Apps" - "Loading Apps…" - "No Apps found matching \"%1$s\"" + + + + + + "Search for more apps" "Notifications" "No more room on this Home screen." diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 2c86f05c38..affa3926ae 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -35,9 +35,12 @@ "%1$d wide by %2$d high" "Touch & hold to place manually" "Add automatically" - "Search Apps" - "Loading Apps…" - "No Apps found matching \"%1$s\"" + + + + + + "Search for more apps" "Notifications" "No more room on this Home screen." diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index f57718dfa5..1725ac64b3 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -35,9 +35,12 @@ "%1$d de ancho por %2$d de alto" "Mantén presionado para ubicarlo manualmente" "Agregar automáticamente" - "Buscar aplicaciones" - "Cargando aplicaciones…" - "No hay aplicaciones que coincidan con %1$s." + + + + + + "Buscar más apps" "Notificaciones" "No hay más espacio en esta pantalla principal." diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index a7b2ab90e5..68646aa480 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -35,9 +35,12 @@ "%1$d de ancho por %2$d de alto" "Mantenlo pulsado para añadirlo manualmente" "Añadir automáticamente" - "Busca aplicaciones" - "Cargando aplicaciones…" - "No se han encontrado aplicaciones que contengan \"%1$s\"" + + + + + + "Buscar más aplicaciones" "Notificaciones" "No queda espacio en la pantalla de inicio." diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 68bf644c53..9105883c1a 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -35,9 +35,12 @@ "%1$d lai ja %2$d kõrge" "Puudutage pikalt, et käsitsi asetada" "Lisa automaatselt" - "Otsige rakendustest" - "Rakenduste laadimine ..." - "Päringule „%1$s” ei vastanud ükski rakendus" + + + + + + "Otsi rohkem rakendusi" "Märguanded" "Sellel avaekraanil pole enam ruumi." diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index f2295863b1..ae14058ac2 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -35,9 +35,12 @@ "%1$d zabal eta %2$d luze" "Eduki sakatuta eskuz gehitzeko" "Gehitu automatikoki" - "Bilatu aplikazioetan" - "Aplikazioak kargatzen…" - "Ez da aurkitu \"%1$s\" bilaketarekin bat datorren aplikaziorik" + + + + + + "Bilatu aplikazio gehiago" "Jakinarazpenak" "Hasierako pantaila honetan ez dago toki gehiago." diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 05c8e08cba..5808a3f64b 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -35,9 +35,12 @@ "‏%1$d عرض در %2$d طول" "برای قرار دادن به‌صورت دستی لمس کنید و بکشید" "افزودن خودکار" - "جستجوی برنامه‌ها" - "در حال بارگیری برنامه‌ها..." - "هیچ برنامه‌ای مطابق با «%1$s» پیدا نشد" + + + + + + "جستجوی برنامه‌های بیشتر" "اعلان‌ها" "فضای بیشتری در این صفحه اصلی موجود نیست." diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 8992f616f4..f4ef7bfe5f 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -35,9 +35,12 @@ "Leveys: %1$d, korkeus: %2$d" "Sijoita manuaalisesti koskettamalla pitkään." "Lisää automaattisesti" - "Sovellushaku" - "Ladataan sovelluksia…" - "”%1$s” ei palauttanut sovelluksia." + + + + + + "Hae lisää sovelluksia" "Ilmoitukset" "Tässä aloitusruudussa ei ole enää tilaa." diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 57ef4f89ab..3c11d7a42e 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -35,9 +35,12 @@ "%1$d de largeur sur %2$d de hauteur" "Maintenez le doigt sur l\'élément pour le placer manuellement" "Ajouter automatiquement" - "Rechercher des applications" - "Chargement des applications en cours..." - "Aucune application trouvée correspondant à « %1$s »" + + + + + + "Rechercher plus d\'applications" "Notifications" "Pas d\'espace libre sur l\'écran d\'accueil." diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 5daab91319..4a4a40fa50 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -35,9 +35,12 @@ "%1$d de largeur et %2$d de hauteur" "Appuyez de manière prolongée pour placer l\'élément manuellement" "Ajouter automatiquement" - "Rechercher dans les applications" - "Chargement des applications en cours…" - "Aucune application ne correspond à la requête \"%1$s\"." + + + + + + "Rechercher plus d\'applications" "Notifications" "Pas d\'espace libre sur cet écran d\'accueil." diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 5df3c87a32..cb3e5c540c 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -35,9 +35,12 @@ "%1$d de largo por %2$d de alto" "Mantén premido o elemento para colocalo manualmente" "Engadir automaticamente" - "Aplicacións de busca" - "Cargando aplicacións..." - "Non se atoparon aplicacións que coincidan con \"%1$s\"" + + + + + + "Buscar máis aplicacións" "Notificacións" "Non hai máis espazo nesta pantalla de inicio." diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 587d16eb4e..00b9582155 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -35,9 +35,12 @@ "%1$d પહોળાઈ X %2$d ઊંચાઈ" "મેન્યુઅલી મૂકવા માટે ટચ કરી દબાવી રાખો" "આપમેળે ઉમેરો" - "શોધ ઍપ્લિકેશનો" - "ઍપ્લિકેશનો લોડ કરી રહ્યું છે…" - "\"%1$s\" થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" + + + + + + "વધુ ઍપ્લિકેશનો શોધો" "સૂચનાઓ" "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index bb9aa5c0ee..b59dd07b12 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -35,9 +35,12 @@ "%1$d चौड़ाई गुणा %2$d ऊंचाई" "मैन्युअल रूप से जोड़ने के लिए स्पर्श करके रखें" "अपने आप जोड़ें" - "ऐप्‍स खोजें" - "ऐप्स लोड हो रहे हैं..." - "\"%1$s\" से मिलान करने वाला कोई ऐप नहीं मिला" + + + + + + "अधिक ऐप्लिकेशन खोजें" "नोटिफ़िकेशन" "इस होम स्‍क्रीन पर स्थान शेष नहीं है." diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 0e272f217f..cfd32cac56 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -35,9 +35,12 @@ "%1$d širine i %2$d visine" "Dodirnite i zadržite stavku da biste je postavili ručno" "Dodaj automatski" - "Pretraži aplikacije" - "Učitavanje aplikacija…" - "Nema aplikacija podudarnih s upitom \"%1$s\"" + + + + + + "Traži više aplikacija" "Obavijesti" "Na ovom početnom zaslonu više nema mjesta." diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 0f3f3b4810..1c58702f9f 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -35,9 +35,12 @@ "%1$d széles és %2$d magas" "Tartsa lenyomva a manuális hozzáadáshoz" "Automatikus hozzáadás" - "Alkalmazások keresése" - "Alkalmazások betöltése…" - "Egy alkalmazás sem található a(z) „%1$s” lekérdezésre." + + + + + + "További alkalmazások keresése" "Értesítések" "Nincs több hely ezen a kezdőképernyőn." diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 6d708d08db..166ba03f91 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -35,9 +35,12 @@ "Լայնությունը՝ %1$d, բարձրությունը՝ %2$d" "Հպեք և պահեք՝ ձեռքով տեղադրելու համար" "Ավելացնել ավտոմատ կերպով" - "Հավելվածների որոնում" - "Հավելվածների բեռնում…" - %1$s» հարցմանը համապատասխանող հավելվածներ չեն գտնվել" + + + + + + "Որոնել այլ հավելվածներ" "Ծանուցումներ" "Այլևս տեղ չկա այս հիմնական էկրանին:" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 57ffa144e4..8175c1c9a0 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -35,9 +35,12 @@ "lebar %1$d x tinggi %2$d" "Sentuh & tahan untuk menempatkan secara manual" "Tambahkan otomatis" - "Telusuri Apps" - "Memuat Aplikasi..." - "Tidak ditemukan Aplikasi yang cocok dengan \"%1$s\"" + + + + + + "Telusuri aplikasi lainnya" "Notifikasi" "Tidak ada ruang lagi pada layar Utama ini." diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 5dcfb4e6ef..3af0bdd958 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -35,9 +35,12 @@ "%1$d á breidd og %2$d á hæð" "Haltu inni til að staðsetja handvirkt" "Bæta sjálfkrafa við" - "Leita í forritum" - "Hleður forrit…" - "Ekki fundust forrit sem samsvara „%1$s“" + + + + + + "Leita að fleiri forritum" "Tilkynningar" "Ekki meira pláss á þessum heimaskjá." diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index ac04f31975..4ae5b3327b 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -35,9 +35,12 @@ "%1$d di larghezza per %2$d di altezza" "Tieni premuto per posizionare l\'elemento manualmente" "Aggiungi automaticamente" - "Cerca app" - "Caricamento di app…" - "Nessuna app trovata corrispondente a \"%1$s\"" + + + + + + "Cerca altre app" "Notifiche" "Spazio nella schermata Home esaurito." diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 83c4296078..b7751fbe75 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -35,9 +35,12 @@ "‏רוחב %1$d על גובה %2$d" "גע והחזק כדי להוסיף ידנית" "הוסף באופן אוטומטי" - "חפש אפליקציות" - "טוען אפליקציות…" - "לא נמצאו אפליקציות התואמות ל-\"%1$s\"" + + + + + + "חפש אפליקציות נוספות" "הודעות" "אין עוד מקום במסך דף הבית הזה." diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index a11304d616..44b73a8650 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -35,9 +35,12 @@ "幅 %1$d、高さ %2$d" "押し続けると、手動で追加できます" "自動的に追加" - "アプリを検索" - "アプリを読み込んでいます…" - "「%1$s」に一致するアプリは見つかりませんでした" + + + + + + "他のアプリを検索" "通知" "このホーム画面に空きスペースがありません。" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 0f10d2be24..892fdd9270 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -35,9 +35,12 @@ "სიგრძე: %1$d, სიგანე: %2$d" "ხანგრძლივად შეეხეთ ხელით განსათავსებლად" "ავტომატურად დამატება" - "აპების ძიება" - "აპები იტვირთება..." - "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" + + + + + + "მეტი აპის პოვნა" "შეტყობინებები" "ამ მთავარ ეკრანზე ადგილი აღარ არის." diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 5638d8bf30..f66e0c7f24 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -35,9 +35,12 @@ "Ені: %1$d, биіктігі: %2$d" "Қолмен қою үшін басып тұрыңыз" "Автоматты енгізу" - "Қолданбаларды іздеу" - "Қолданбалар жүктелуде…" - %1$s» сұрауына сәйкес келетін қолданбалар жоқ" + + + + + + "Қосымша қолданбалар іздеу" "Хабарландырулар" "Бұл Негізгі экранда орын қалмады." diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index c60f09fa7a..ba6eebd165 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -35,9 +35,12 @@ "ទទឺង %1$d គុណនឹងកម្ពស់ %2$d" "ចុច​ឲ្យជាប់​ដើម្បី​បញ្ចូលវា​ដោយផ្ទាល់" "បញ្ចូល​ដោយ​ស្វ័យ​ប្រវត្តិ" - "ស្វែងរកកម្មវិធី" - "កំពុងដំណើរការកម្មវិធី..." - "គ្មានកម្មវិធីដែលត្រូវជាមួយ \"%1$s\" ទេ" + + + + + + "ស្វែងរកកម្មវិធីច្រើនទៀត" "ការ​ជូនដំណឹង" "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 7566503b0b..2c6fa09c3b 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -35,9 +35,12 @@ "%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ" "ಹಸ್ತಚಾಲಿತವಾಗಿ ಸೇರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ" "ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಿ" - "ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ" - "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..." - "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" + + + + + + "ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ" "ಅಧಿಸೂಚನೆಗಳು" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 24546f4acb..459e7b5fa6 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -35,9 +35,12 @@ "너비 %1$d, 높이 %2$d" "길게 터치하여 직접 장소 추가" "자동으로 추가" - "앱 검색" - "앱 로드 중..." - "\'%1$s\'와(과) 일치하는 앱이 없습니다." + + + + + + "더 많은 앱 검색" "알림" "홈 화면에 더 이상 공간이 없습니다." diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index dfc8691919..5c0f98fdd6 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -35,9 +35,12 @@ "Туурасы: %1$d, бийиктиги: %2$d" "Кол менен жайгаштыруу үчүн басып туруп, таштаңыз" "Автоматтык түрдө кошуу" - "Колдонмолорду издөө" - "Колдонмолор жүктөлүүдө…" - "\"%1$s\" дал келген колдонмолор табылган жок" + + + + + + "Көбүрөөк колдонмолорду издөө" "Эскертмелер" "Бул Үй экранында бош орун жок." diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 66a504841b..990a990a93 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -35,9 +35,12 @@ "ກວ້າງ %1$d ຄູນສູງ %2$d" "ແຕະຄ້າງໄວ້ເພື່ອວາງດ້ວຍຕົນເອງ" "ເພີ່ມໂດຍອັດຕະໂນມັດ" - "ຊອກຫາແອັບ" - "​ກຳ​ລັງ​ໂຫລດ​ແອັບ..." - "ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"%1$s\"" + + + + + + "ຊອກຫາແອັບເພີ່ມເຕີມ" "ການແຈ້ງເຕືອນ" "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index c346ed954e..f8e461723c 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -35,9 +35,12 @@ "%1$d plotis ir %2$d aukštis" "Palieskite ir palaikykite, kad padėtumėte patys" "Pridėti automatiškai" - "Ieškoti programų" - "Įkeliamos programos..." - "Nerasta jokių užklausą „%1$s“ atitinkančių programų" + + + + + + "Ieškoti daugiau programų" "Pranešimai" "Šiame pagrindiniame ekrane vietos nebėra." diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 580d5b7d19..106d15279f 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -35,9 +35,12 @@ "%1$d plats un %2$d augsts" "Pieskarieties un turiet, lai manuāli pievienotu" "Pievienot automātiski" - "Meklēt lietotnes" - "Notiek lietotņu ielāde…" - "Vaicājumam “%1$s” neatbilda neviena lietotne." + + + + + + "Meklēt citas lietotnes" "Paziņojumi" "Šajā sākuma ekrānā vairs nav vietas." diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index ebc1fcc83a..cfe763637a 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -35,9 +35,12 @@ "%1$d широк на %2$d висок" "Допрете и задржете за рачно поставување" "Додај автоматски" - "Пребарување апликации" - "Се вчитуваат апликации…" - "Не се најдени апликации што одговараат на „%1$s“" + + + + + + "Пребарај други апликации" "Известувања" "Нема повеќе простор на овој екран на почетната страница." diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 62bcc8cbe2..3f1b805631 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -35,9 +35,12 @@ "%1$d വീതിയും %2$d ഉയരവും" "സ്വമേധയാ സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക" "സ്വയമേവ ചേർക്കുക" - "ആപ്പുകളെ തിരയുക" - "ആപ്പുകൾ ലോഡുചെയ്യുന്നു..." - "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല" + + + + + + "കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക" "അറിയിപ്പുകൾ" "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index a8d4b3cf6e..a4618585ab 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -35,9 +35,12 @@ "%1$d өргөн %2$d өндөр" "Гараар байршуулахын тулд дараад хүлээнэ үү" "Автоматаар нэмэх" - "Апп хайх" - "Аппликейшныг ачаалж байна..." - "\"%1$s\"-д нийцэх апп олдсонгүй" + + + + + + "Бусад апп-г хайх" "Мэдэгдэл" "Энэ Нүүр дэлгэц зайгүй." diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 3435061b84..e048910dc8 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -35,9 +35,12 @@ "%1$d रूंद बाय %2$d उंच" "स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा" "स्वयंचलितपणे जोडा" - "अॅप्स शोधा" - "अॅप्स लोड करीत आहे..." - "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" + + + + + + "अधिक अॅप्स शोधा" "सूचना" "या मुख्य स्क्रीनवर आणखी जागा नाही." diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 5346d2bbef..27ae890031 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -35,9 +35,12 @@ "Lebar %1$d kali tinggi %2$d" "Sentuh & tahan untuk meletakkan widget/ikon secara manual" "Tambahkan secara automatik" - "Cari Apl" - "Memuatkan Apl…" - "Tiada Apl yang ditemui sepadan dengan \"%1$s\"" + + + + + + "Cari lagi apl" "Pemberitahuan" "Tiada lagi ruang pada skrin Laman Utama ini." diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index 7dd66892c7..b847b906ae 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -35,9 +35,12 @@ "အလျား %1$d နှင့် အမြင့် %2$d" "ကိုယ်တိုင်ထည့်ရန် ထိထားပါ" "အလိုအလျောက် ထည့်ရန်" - "ရှာဖွေမှု အက်ပ်များ" - "အက်ပ်များ ရယူနေစဉ်..." - "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ" + + + + + + "နောက်ထပ် အက်ပ်များကို ရှာပါ" "အကြောင်းကြားချက်များ" "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 21c2036ab1..d8cde1b00d 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -35,9 +35,12 @@ "%1$d bredde x %2$d høyde" "Trykk og hold for å plassere manuelt" "Legg til automatisk" - "Søk i apper" - "Laster inn apper …" - "Fant ingen apper som samsvarer med «%1$s»" + + + + + + "Søk etter flere apper" "Varsler" "Denne startsiden er full." diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index b28e0e2b46..3214a94c28 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -35,9 +35,12 @@ "%1$d चौडाइ गुणा %2$d उचाइ" "म्यानुअल तरिकाले थप्न छुनुहोस् र थिची राख्नुहोस्‌" "स्वतः थप्नुहोस्" - "अनुप्रयोगहरू खोज्नुहोस्" - "अनुप्रयोगहरू लोड गरिँदै..." - "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्" + + + + + + "थप अनुप्रयोगहरू खोज्नुहोस्" "सूचनाहरू" "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 5370528d4c..85bec26bbe 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -35,9 +35,12 @@ "%1$d breed en %2$d hoog" "Tik op een item en houd dit vast om het handmatig te plaatsen" "Automatisch toevoegen" - "Apps zoeken" - "Apps laden…" - "Er zijn geen apps gevonden die overeenkomen met \'%1$s\'" + + + + + + "Zoeken naar meer apps" "Meldingen" "Er is geen ruimte meer op dit startscherm." diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index aaf4c92cb2..72ba3d109e 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -35,9 +35,12 @@ "%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ" "ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ" "ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਸ਼ਾਮਲ ਕਰੋ" - "ਐਪਸ ਖੋਜੋ" - "ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." - "\"%1$s\" ਨਾਲ ਮਿਲਦੀਆਂ ਕੋਈ ਵੀ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ" + + + + + + "ਹੋਰ ਐਪਾਂ ਖੋਜੋ" "ਸੂਚਨਾਵਾਂ" "ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 41c6ebe6a7..2725407a9d 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -35,9 +35,12 @@ "Szerokość %1$d, wysokość %2$d" "Kliknij i przytrzymaj, by umieścić ręcznie" "Dodaj automatycznie" - "Szukaj w aplikacjach" - "Wczytuję aplikacje…" - "Nie znaleziono aplikacji pasujących do zapytania „%1$s”" + + + + + + "Wyszukaj więcej aplikacji" "Powiadomienia" "Brak miejsca na tym ekranie głównym." diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index e12bc540e0..4fee392696 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -35,9 +35,12 @@ "%1$d de largura por %2$d de altura" "Toque sem soltar para colocar manualmente" "Adicionar automaticamente" - "Pesquisar aplicações" - "A carregar aplicações..." - "Não foram encontradas aplic. que correspondam a \"%1$s\"" + + + + + + "Pesquisar mais aplicações" "Notificações" "Sem espaço suficiente neste Ecrã principal." diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index ac439192e3..c8fde040f8 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -35,9 +35,12 @@ "%1$d de largura por %2$d de altura" "Toque e mantenha pressionado para posicionar manualmente" "Adicionar automaticamente" - "Pesquisar apps" - "Carregando apps…" - "Nenhum app encontrado que corresponda a \"%1$s\"" + + + + + + "Pesquisar mais apps" "Notificações" "Não há mais espaço na tela inicial." diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 0ce4c1f332..43a82deefc 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -35,9 +35,12 @@ "%1$d lățime și %2$d înălțime" "Atingeți lung pentru a plasa manual" "Adăugați automat" - "Căutați aplicații" - "Se încarcă aplicațiile..." - "Nu s-a găsit nicio aplicație pentru „%1$s”" + + + + + + "Căutați mai multe aplicații" "Notificări" "Nu mai este loc pe acest Ecran de pornire." diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 71d1a72b20..4bbbc46a17 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -35,9 +35,12 @@ "Ширина %1$d, высота %2$d" "Нажмите и удерживайте, чтобы добавить вручную" "Добавить автоматически" - "Поиск приложений" - "Загрузка…" - "По запросу \"%1$s\" ничего не найдено" + + + + + + "Искать другие приложения" "Уведомления" "На этом экране все занято" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 710c76c337..5c719f8ba2 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -35,9 +35,12 @@ "පළල %1$d උස %2$d" "අතින් ස්ථානගත කිරීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න" "ස්වයංක්‍රියව එක් කරන්න" - "යෙදුම් සෙවීම" - "යෙදුම් පූරණය වෙමින්…" - "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" + + + + + + "තව යෙදුම් සඳහා සොයන්න" "දැනුම්දීම්" "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 502ac27d8a..c700a3eef5 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -35,9 +35,12 @@ "šírka %1$d, výška %2$d" "Položku umiestnite ručne klepnutím a podržaním" "Pridať automaticky" - "Hľadať aplikácie" - "Načítavajú sa aplikácie..." - "Nenašli sa žiadne aplikácie zodpovedajúce dopytu %1$s" + + + + + + "Hľadať ďalšie aplikácie" "Upozornenia" "Na tejto ploche už nie je miesto" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index a16054dd20..0f5d84c473 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -35,9 +35,12 @@ "Širina %1$d, višina %2$d" "Dotaknite se elementa in ga pridržite, da ga ročno dodate" "Samodejno dodaj" - "Iskanje po aplikacijah" - "Nalaganje aplikacij …" - "Ni aplikacij, ki bi ustrezale poizvedbi »%1$s«" + + + + + + "Iskanje več aplikacij" "Obvestila" "Na tem začetnem zaslonu ni več prostora." diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 8480288ce4..b5f573bff9 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -35,9 +35,12 @@ "%1$d i gjerë me %2$d i lartë" "Prek dhe mbaj të shtypur për të vendosur në mënyrë manuale" "Shto automatikisht" - "Kërko për aplikacione" - "Po ngarkon aplikacionet..." - "Nuk u gjet asnjë aplikacion që përputhet me \"%1$s\"" + + + + + + "Kërko për më shumë aplikacione" "Njoftimet" "Nuk ka më hapësirë në këtë ekran bazë." diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 7dd327958c..f2e4c71caf 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -35,9 +35,12 @@ "ширина од %1$d и висина од %2$d" "Додирните и задржите да бисте поставили ручно" "Аутоматски додај" - "Претражите апликације" - "Апликације се учитавају..." - "Није пронађена ниједна апликација за „%1$s“" + + + + + + "Претражи још апликација" "Обавештења" "Нема више простора на овом почетном екрану." diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index ae50060d3d..7519400431 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -35,9 +35,12 @@ "%1$d bred gånger %2$d hög" "Placera manuellt genom att trycka länge" "Lägg till automatiskt" - "Sök efter appar" - "Läser in appar …" - "Det gick inte att hitta några appar som matchar %1$s" + + + + + + "Sök efter fler appar" "Aviseringar" "Det finns inte plats för mer på den här startskärmen." diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 2ecd0acb37..b99037f533 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -35,9 +35,12 @@ "Upana wa %1$d na kimo cha %2$d" "Gusa na ushikilie ili uweke mwenyewe" "Ongeza kiotomatiki" - "Tafuta Programu" - "Inapakia Programu..." - "Haikupata programu zinazolingana na \"%1$s\"" + + + + + + "Tafuta programu zaidi" "Arifa" "Hakuna nafasi katika skrini hii ya Mwanzo." diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index eeca4299c5..4a87893a7f 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -35,9 +35,12 @@ "%1$d அகலத்திற்கு %2$d உயரம்" "நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்" "தானாகவே சேர்" - "பயன்பாடுகளில் தேடுக" - "பயன்பாடுகளை ஏற்றுகிறது..." - "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" + + + + + + "கூடுதல் பயன்பாடுகளைத் தேடு" "அறிவிப்புகள்" "முகப்புத் திரையில் இடமில்லை." diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 94e09d8660..064b71f878 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -35,9 +35,12 @@ "%1$d వెడల్పు X %2$d ఎత్తు" "మాన్యువల్‌గా ఉంచడానికి నొక్కి &amp పట్టుకోండి" "స్వయంచాలకంగా జోడించు" - "అనువర్తనాలను శోధించండి" - "అనువర్తనాలను లోడ్ చేస్తోంది…" - "\"%1$s\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు" + + + + + + "మరిన్ని అనువర్తనాల కోసం శోధించు" "నోటిఫికేషన్‌లు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 3db6db8a2e..bad494590f 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -35,9 +35,12 @@ "กว้าง %1$d x สูง %2$d" "แตะค้างไว้เพื่อวางด้วยตัวเอง" "เพิ่มโดยอัตโนมัติ" - "ค้นหาแอป" - "กำลังโหลดแอป…" - "ไม่พบแอปที่ตรงกับ \"%1$s\"" + + + + + + "ค้นหาแอปเพิ่มเติม" "การแจ้งเตือน" "ไม่มีที่ว่างในหน้าจอหลักนี้" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index f146f9a749..184a48390c 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -35,9 +35,12 @@ "%1$d ang lapad at %2$d ang taas" "Pindutin nang matagal upang manual na ilagay" "Awtomatikong idagdag" - "Mga App sa Paghahanap" - "Nilo-load ang Mga App…" - "Walang nakitang Mga App na tumutugma sa \"%1$s\"" + + + + + + "Maghanap ng higit pang mga app" "Mga Notification" "Wala nang lugar sa Home screen na ito." diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 168d3a5449..e33d71a2e8 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -35,9 +35,12 @@ "genişlik: %1$d, yükseklik: %2$d" "Manuel olarak yerleştirmek için dokunun ve basılı tutun" "Otomatik olarak ekle" - "Uygulamalarda Ara" - "Uygulamalar Yükleniyor…" - "\"%1$s\" ile eşleşen uygulama bulunamadı" + + + + + + "Başka uygulamalar ara" "Bildirimler" "Bu Ana ekranda yer kalmadı." diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 0d8f15c739..b18b8f9751 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -35,9 +35,12 @@ "Ширина – %1$d, висота – %2$d" "Натисніть і утримуйте, щоб додати вручну" "Додати автоматично" - "Пошук додатків" - "Завантаження додатків…" - "Немає додатків для запиту \"%1$s\"" + + + + + + "Шукати ще додатки" "Сповіщення" "На цьому головному екрані більше немає місця." diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index f3d6d3f3cb..10cfcd3e08 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -35,9 +35,12 @@ "‏%1$d چوڑا اور ‎%2$d اونچا" "‏دستی طور پر رکھنے کیلئے ‎& ٹچ کرکے ہولڈ کریں" "خود کار طور پر شامل کریں" - "ایپس تلاش کریں" - "ایپس لوڈ ہو رہی ہیں…" - "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" + + + + + + "مزید ایپس تلاش کریں" "اطلاعات" "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index c3c48061d2..83803f59c9 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -35,9 +35,12 @@ "Eni %1$d, bo‘yi %2$d" "Qo‘lda joylashtirish uchun bosib turing" "Avtomatik chiqarish" - "Ilovalar ichidan qidirish" - "Ilovalar yuklanmoqda…" - "“%1$s” so‘rovi bo‘yicha hech narsa topilmadi" + + + + + + "Boshqa ilovalarni qidirish" "Bildirishnomalar" "Uy ekranida bitta ham xona yo‘q." diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index fe2cfa1bb2..093cfee21d 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -35,9 +35,12 @@ "Rộng %1$d x cao %2$d" "Chạm và giữ để đặt theo cách thủ công" "Tự động thêm" - "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\"" + + + + + + "Tìm kiếm thêm ứng dụng" "Thông báo" "Không còn chỗ trên Màn hình chính này." diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 15e24a1008..a83b1f35db 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -35,9 +35,12 @@ "宽 %1$d,高 %2$d" "触摸并按住即可手动放置" "自动添加" - "搜索应用" - "正在加载应用…" - "未找到与“%1$s”相符的应用" + + + + + + "搜索更多应用" "通知" "此主屏幕上已没有空间。" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 4087180050..520b18f40d 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -35,9 +35,12 @@ "%1$d 闊,%2$d 高" "按住即可手動新增" "自動新增" - "搜尋應用程式" - "正在載入應用程式…" - "無法找到與「%1$s」相符的應用程式" + + + + + + "搜尋更多應用程式" "通知" "主畫面已無空間。" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index f123d4066b..ff79d9b20a 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -35,9 +35,12 @@ "寬度為 %1$d,高度為 %2$d" "按住即可手動放置" "自動新增" - "搜尋應用程式" - "正在載入應用程式…" - "找不到符合「%1$s」的應用程式" + + + + + + "搜尋更多應用程式" "通知" "這個主螢幕已無空間。" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index a32093c8c9..4a77c23e01 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -35,9 +35,12 @@ "%1$d ububanzi ngokungu-%2$d ukuya phezulu" "Thinta futhi ubambe ukuze ubeke ngokwenza" "Engeza ngokuzenzakalelayo" - "Sesha Izinhlelo Zokusebenza" - "Ilayisha izinhlelo zokusebenza..." - "Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"%1$s\"" + + + + + + "Sesha izinhlelo zokusebenza eziningi" "Izaziso" "Asisekho isikhala kulesi sikrini Sasekhaya." From ab5e4849d3fe0f456ba2a991eb9f0761f482e3d6 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 24 Jul 2017 23:15:48 -0700 Subject: [PATCH 005/885] Import translations. DO NOT MERGE Change-Id: I52f489d8eea3b8d3040c486341afedd9ff05e858 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-af/strings.xml | 9 +++------ res/values-am/strings.xml | 9 +++------ res/values-ar/strings.xml | 9 +++------ res/values-az/strings.xml | 9 +++------ res/values-b+sr+Latn/strings.xml | 9 +++------ res/values-bg/strings.xml | 9 +++------ res/values-bs/strings.xml | 9 +++------ res/values-ca/strings.xml | 9 +++------ res/values-da/strings.xml | 9 +++------ res/values-de/strings.xml | 9 +++------ res/values-en-rAU/strings.xml | 9 +++------ res/values-en-rGB/strings.xml | 9 +++------ res/values-en-rIN/strings.xml | 9 +++------ res/values-es-rUS/strings.xml | 9 +++------ res/values-es/strings.xml | 9 +++------ res/values-eu/strings.xml | 9 +++------ res/values-fa/strings.xml | 9 +++------ res/values-fr-rCA/strings.xml | 9 +++------ res/values-fr/strings.xml | 9 +++------ res/values-gl/strings.xml | 9 +++------ res/values-gu/strings.xml | 9 +++------ res/values-hi/strings.xml | 9 +++------ res/values-hr/strings.xml | 9 +++------ res/values-in/strings.xml | 9 +++------ res/values-is/strings.xml | 9 +++------ res/values-it/strings.xml | 9 +++------ res/values-ja/strings.xml | 9 +++------ res/values-ka/strings.xml | 9 +++------ res/values-km/strings.xml | 9 +++------ res/values-ky/strings.xml | 9 +++------ res/values-lo/strings.xml | 9 +++------ res/values-mk/strings.xml | 9 +++------ res/values-mn/strings.xml | 9 +++------ res/values-my/strings.xml | 9 +++------ res/values-nb/strings.xml | 9 +++------ res/values-nl/strings.xml | 9 +++------ res/values-pl/strings.xml | 9 +++------ res/values-pt-rPT/strings.xml | 9 +++------ res/values-pt/strings.xml | 9 +++------ res/values-ro/strings.xml | 9 +++------ res/values-si/strings.xml | 9 +++------ res/values-sk/strings.xml | 9 +++------ res/values-sr/strings.xml | 9 +++------ res/values-sv/strings.xml | 9 +++------ res/values-sw/strings.xml | 9 +++------ res/values-ta/strings.xml | 9 +++------ res/values-th/strings.xml | 9 +++------ res/values-tl/strings.xml | 9 +++------ res/values-tr/strings.xml | 9 +++------ res/values-uk/strings.xml | 9 +++------ res/values-uz/strings.xml | 9 +++------ res/values-vi/strings.xml | 9 +++------ res/values-zh-rCN/strings.xml | 9 +++------ res/values-zh-rHK/strings.xml | 9 +++------ res/values-zh-rTW/strings.xml | 9 +++------ res/values-zu/strings.xml | 9 +++------ 56 files changed, 168 insertions(+), 336 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 9024427e81..b0a12c8afb 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -35,12 +35,9 @@ "%1$d breed by %2$d hoog" "Raak en hou om self te plaas" "Voeg outomaties by" - - - - - - + "Deursoek programme" + "Laai tans programme …" + "Kon geen programme kry wat by \"%1$s\" pas nie" "Soek meer programme" "Kennisgewings" "Niks meer spasie op die tuisskerm nie." diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 88bc33f700..3517d0163a 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -35,12 +35,9 @@ "%1$d ስፋት በ%2$d ከፍታ" "ራስዎ ለማስቀመጥ ነክተው ይያዙት" "በራስ-ሰር አክል" - - - - - - + "መተግበሪያዎችን ፈልግ" + "መተግበሪያዎችን በመጫን ላይ…" + "ከ«%1$s» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም" "ተጨማሪ መተግበሪያዎች ይፈልጉ" "ማሳወቂያዎች" "በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 98106c5f0f..5b675f34c1 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -35,12 +35,9 @@ "‏العرض %1$d الطول %2$d" "المس مع الاستمرار للإضافة يدويًا" "إضافة تلقائيًا" - - - - - - + "بحث في التطبيقات" + "جارٍ تحميل التطبيقات…" + "لم يتم العثور على أي تطبيقات تتطابق مع \"%1$s\"" "البحث عن مزيد من التطبيقات" "الإشعارات" "ليس هناك مساحة أخرى في هذه الشاشة الرئيسية." diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index b33d8b5e77..4db1886167 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -35,12 +35,9 @@ "%2$d hündürlük %1$d enində" "Manual olaraq yerləşdirmək üçün toxunaraq basıb saxlayın" "Avtomatik əlavə edin" - - - - - - + "Tətbiqləri axtarın" + "Tətbiqlər yüklənir…" + "%1$s sorğusuna uyğun tətbiq tapılmadı" "Daha çox tətbiq üçün axtarış edin" "Bildirişlər" "Bu Əsas ekranda boş yer yoxdur." diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 4294499ed6..520b027ecb 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -35,12 +35,9 @@ "širina od %1$d i visina od %2$d" "Dodirnite i zadržite da biste postavili ručno" "Automatski dodaj" - - - - - - + "Pretražite aplikacije" + "Aplikacije se učitavaju…" + "Nije pronađena nijedna aplikacija za „%1$s“" "Pretraži još aplikacija" "Obaveštenja" "Nema više prostora na ovom početnom ekranu." diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 7883d7c92f..d6400ced20 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -35,12 +35,9 @@ "Ширина %1$d и височина %2$d" "Докоснете и задръжте, за да поставите ръчно" "Автоматично добавяне" - - - - - - + "Търсене в приложенията" + "Приложенията се зареждат…" + "Няма намерени приложения, съответстващи на „%1$s“" "Търсене на още приложения" "Известия" "На този начален екран няма повече място." diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 032fafb8b4..34ef0b42a0 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -35,12 +35,9 @@ "Širina %1$d, visina %2$d" "Dodirnite i držite da postavite ručno" "Dodaj automatski" - - - - - - + "Pretražite aplikacije" + "Aplikacije se učitavaju…" + "Nije pronađena nijedna aplikacija za upit \"%1$s\"" "Pretraži više aplikacija" "Obavještenja" "Na ovom početnom ekranu nema više prostora." diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 0685c9027f..05c43c61d7 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -35,12 +35,9 @@ "%1$d d\'amplada per %2$d d\'alçada" "Toca i mantén premut l\'element per col·locar-lo manualment" "Afegeix automàticament" - - - - - - + "Cerca aplicacions" + "S\'estan carregant les aplicacions…" + "No s\'ha trobat cap aplicació que coincideixi amb %1$s" "Cerca més aplicacions" "Notificacions" "Ja no queda espai en aquesta pantalla d\'inici." diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 3d22280a03..c5bf485574 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -35,12 +35,9 @@ "%1$d i bredden og %2$d i højden" "Tryk, og hold fingeren nede for at placere manuelt" "Tilføj automatisk" - - - - - - + "Søg efter apps" + "Indlæser apps…" + "Der blev ikke fundet nogen apps, som matcher \"%1$s\"" "Søg efter flere apps" "Underretninger" "Der er ikke mere plads på denne startskærm." diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index c67fca7f3a..cda1fc5325 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -35,12 +35,9 @@ "%1$d breit und %2$d hoch" "Zum manuellen Hinzufügen gedrückt halten" "Automatisch hinzufügen" - - - - - - + "Apps finden" + "Apps werden geladen…" + "Keine Apps für \"%1$s\" gefunden" "Weitere Apps suchen" "Benachrichtigungen" "Auf diesem Startbildschirm ist kein Platz mehr vorhanden." diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index affa3926ae..88b839f0d4 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -35,12 +35,9 @@ "%1$d wide by %2$d high" "Touch & hold to place manually" "Add automatically" - - - - - - + "Search apps" + "Loading apps…" + "No apps found matching \'%1$s\'" "Search for more apps" "Notifications" "No more room on this Home screen." diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index affa3926ae..88b839f0d4 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -35,12 +35,9 @@ "%1$d wide by %2$d high" "Touch & hold to place manually" "Add automatically" - - - - - - + "Search apps" + "Loading apps…" + "No apps found matching \'%1$s\'" "Search for more apps" "Notifications" "No more room on this Home screen." diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index affa3926ae..88b839f0d4 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -35,12 +35,9 @@ "%1$d wide by %2$d high" "Touch & hold to place manually" "Add automatically" - - - - - - + "Search apps" + "Loading apps…" + "No apps found matching \'%1$s\'" "Search for more apps" "Notifications" "No more room on this Home screen." diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 1725ac64b3..557c62b9bc 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -35,12 +35,9 @@ "%1$d de ancho por %2$d de alto" "Mantén presionado para ubicarlo manualmente" "Agregar automáticamente" - - - - - - + "Buscar apps" + "Cargando apps…" + "No hay apps que coincidan con \"%1$s\"" "Buscar más apps" "Notificaciones" "No hay más espacio en esta pantalla principal." diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 68646aa480..3185e7a697 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -35,12 +35,9 @@ "%1$d de ancho por %2$d de alto" "Mantenlo pulsado para añadirlo manualmente" "Añadir automáticamente" - - - - - - + "Buscar aplicaciones" + "Cargando aplicaciones…" + "No se han encontrado aplicaciones que contengan \"%1$s\"" "Buscar más aplicaciones" "Notificaciones" "No queda espacio en la pantalla de inicio." diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index ae14058ac2..96aed2efe8 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -35,12 +35,9 @@ "%1$d zabal eta %2$d luze" "Eduki sakatuta eskuz gehitzeko" "Gehitu automatikoki" - - - - - - + "Bilatu aplikazioetan" + "Aplikazioak kargatzen…" + "Ez da aurkitu \"%1$s\" bilaketaren emaitzarik" "Bilatu aplikazio gehiago" "Jakinarazpenak" "Hasierako pantaila honetan ez dago toki gehiago." diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 5808a3f64b..ec4677e09b 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -35,12 +35,9 @@ "‏%1$d عرض در %2$d طول" "برای قرار دادن به‌صورت دستی لمس کنید و بکشید" "افزودن خودکار" - - - - - - + "جستجوی برنامه‌ها" + "درحال بارگیری برنامه‌‌ها…" + "هیچ برنامه‌ای در مطابقت با «%1$s» پیدا نشد" "جستجوی برنامه‌های بیشتر" "اعلان‌ها" "فضای بیشتری در این صفحه اصلی موجود نیست." diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 3c11d7a42e..25f90b7783 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -35,12 +35,9 @@ "%1$d de largeur sur %2$d de hauteur" "Maintenez le doigt sur l\'élément pour le placer manuellement" "Ajouter automatiquement" - - - - - - + "Rechercher dans les applications" + "Chargement des applications en cours…" + "Aucune application trouvée correspondant à « %1$s »" "Rechercher plus d\'applications" "Notifications" "Pas d\'espace libre sur l\'écran d\'accueil." diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 4a4a40fa50..6efbedabf3 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -35,12 +35,9 @@ "%1$d de largeur et %2$d de hauteur" "Appuyez de manière prolongée pour placer l\'élément manuellement" "Ajouter automatiquement" - - - - - - + "Rechercher dans les applications" + "Chargement des applications…" + "Aucune application ne correspond à la requête \"%1$s\"" "Rechercher plus d\'applications" "Notifications" "Pas d\'espace libre sur cet écran d\'accueil." diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index cb3e5c540c..06be050603 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -35,12 +35,9 @@ "%1$d de largo por %2$d de alto" "Mantén premido o elemento para colocalo manualmente" "Engadir automaticamente" - - - - - - + "Buscar aplicacións" + "Cargando aplicacións…" + "Non se atoparon aplicacións que coincidan con \"%1$s\"" "Buscar máis aplicacións" "Notificacións" "Non hai máis espazo nesta pantalla de inicio." diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 00b9582155..808c04756f 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -35,12 +35,9 @@ "%1$d પહોળાઈ X %2$d ઊંચાઈ" "મેન્યુઅલી મૂકવા માટે ટચ કરી દબાવી રાખો" "આપમેળે ઉમેરો" - - - - - - + "શોધ ઍપ્લિકેશનો" + "ઍપ્લિકેશનો લોડ કરી રહ્યું છે…" + "\"%1$s\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" "વધુ ઍપ્લિકેશનો શોધો" "સૂચનાઓ" "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index b59dd07b12..b899f427f9 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -35,12 +35,9 @@ "%1$d चौड़ाई गुणा %2$d ऊंचाई" "मैन्युअल रूप से जोड़ने के लिए स्पर्श करके रखें" "अपने आप जोड़ें" - - - - - - + "ऐप्लिकेशन खोजें" + "ऐप्लिकेशन लोड हो रहे हैं…" + "\"%1$s\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला" "अधिक ऐप्लिकेशन खोजें" "नोटिफ़िकेशन" "इस होम स्‍क्रीन पर स्थान शेष नहीं है." diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index cfd32cac56..5e20333135 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -35,12 +35,9 @@ "%1$d širine i %2$d visine" "Dodirnite i zadržite stavku da biste je postavili ručno" "Dodaj automatski" - - - - - - + "Pretraži aplikacije" + "Učitavanje aplikacija…" + "Nema aplikacija podudarnih s upitom \"%1$s\"" "Traži više aplikacija" "Obavijesti" "Na ovom početnom zaslonu više nema mjesta." diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 8175c1c9a0..74806c96e8 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -35,12 +35,9 @@ "lebar %1$d x tinggi %2$d" "Sentuh & tahan untuk menempatkan secara manual" "Tambahkan otomatis" - - - - - - + "Telusuri aplikasi" + "Memuat aplikasi…" + "Tidak ditemukan aplikasi yang cocok dengan \"%1$s\"" "Telusuri aplikasi lainnya" "Notifikasi" "Tidak ada ruang lagi pada layar Utama ini." diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 3af0bdd958..598840f802 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -35,12 +35,9 @@ "%1$d á breidd og %2$d á hæð" "Haltu inni til að staðsetja handvirkt" "Bæta sjálfkrafa við" - - - - - - + "Leita í forritum" + "Hleður forrit…" + "Ekki fundust forrit sem samsvara „%1$s“" "Leita að fleiri forritum" "Tilkynningar" "Ekki meira pláss á þessum heimaskjá." diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 4ae5b3327b..ca40ed882d 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -35,12 +35,9 @@ "%1$d di larghezza per %2$d di altezza" "Tieni premuto per posizionare l\'elemento manualmente" "Aggiungi automaticamente" - - - - - - + "Cerca nelle app" + "Caricamento delle app…" + "Nessuna app trovata corrispondente a \"%1$s\"" "Cerca altre app" "Notifiche" "Spazio nella schermata Home esaurito." diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 44b73a8650..864978893d 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -35,12 +35,9 @@ "幅 %1$d、高さ %2$d" "押し続けると、手動で追加できます" "自動的に追加" - - - - - - + "アプリを検索" + "アプリを読み込んでいます…" + "「%1$s」に一致するアプリは見つかりませんでした" "他のアプリを検索" "通知" "このホーム画面に空きスペースがありません。" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 892fdd9270..443e6c1e9c 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -35,12 +35,9 @@ "სიგრძე: %1$d, სიგანე: %2$d" "ხანგრძლივად შეეხეთ ხელით განსათავსებლად" "ავტომატურად დამატება" - - - - - - + "აპების ძიება" + "აპები იტვირთება…" + "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" "მეტი აპის პოვნა" "შეტყობინებები" "ამ მთავარ ეკრანზე ადგილი აღარ არის." diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index ba6eebd165..fd10f21157 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -35,12 +35,9 @@ "ទទឺង %1$d គុណនឹងកម្ពស់ %2$d" "ចុច​ឲ្យជាប់​ដើម្បី​បញ្ចូលវា​ដោយផ្ទាល់" "បញ្ចូល​ដោយ​ស្វ័យ​ប្រវត្តិ" - - - - - - + "ស្វែងរក​កម្មវិធី" + "កំពុងផ្ទុកកម្មវិធី…" + "រកមិនឃើញកម្មវិធី​ដែលត្រូវគ្នាជាមួយ \"%1$s\" ទេ" "ស្វែងរកកម្មវិធីច្រើនទៀត" "ការ​ជូនដំណឹង" "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 5c0f98fdd6..10038c15f5 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -35,12 +35,9 @@ "Туурасы: %1$d, бийиктиги: %2$d" "Кол менен жайгаштыруу үчүн басып туруп, таштаңыз" "Автоматтык түрдө кошуу" - - - - - - + "Колдонмолорду издөө" + "Колдонмолор жүктөлүүдө…" + "\"%1$s\" сурамына дал келген колдонмолор табылган жок" "Көбүрөөк колдонмолорду издөө" "Эскертмелер" "Бул Үй экранында бош орун жок." diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 990a990a93..e91e9f9b88 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -35,12 +35,9 @@ "ກວ້າງ %1$d ຄູນສູງ %2$d" "ແຕະຄ້າງໄວ້ເພື່ອວາງດ້ວຍຕົນເອງ" "ເພີ່ມໂດຍອັດຕະໂນມັດ" - - - - - - + "ຊອກຫາແອັບ" + "ກໍາລັງໂຫຼດແອັບ…" + "ບໍ່ພົບແອັບທີ່ກົງກັບ \"%1$s\"" "ຊອກຫາແອັບເພີ່ມເຕີມ" "ການແຈ້ງເຕືອນ" "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index cfe763637a..39de6ef818 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -35,12 +35,9 @@ "%1$d широк на %2$d висок" "Допрете и задржете за рачно поставување" "Додај автоматски" - - - - - - + "Пребарувајте апликации" + "Се вчитуваат апликации…" + "Не се најдени апликации што одговараат на „%1$s“" "Пребарај други апликации" "Известувања" "Нема повеќе простор на овој екран на почетната страница." diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index a4618585ab..2b5b7beb20 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -35,12 +35,9 @@ "%1$d өргөн %2$d өндөр" "Гараар байршуулахын тулд дараад хүлээнэ үү" "Автоматаар нэмэх" - - - - - - + "Апп хайх" + "Аппыг ачааллаж байна..." + "\"%1$s\"-д тохирох апп олдсонгүй" "Бусад апп-г хайх" "Мэдэгдэл" "Энэ Нүүр дэлгэц зайгүй." diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index b847b906ae..63ca189679 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -35,12 +35,9 @@ "အလျား %1$d နှင့် အမြင့် %2$d" "ကိုယ်တိုင်ထည့်ရန် ထိထားပါ" "အလိုအလျောက် ထည့်ရန်" - - - - - - + "ရှာဖွေမှု အက်ပ်များ" + "အက်ပ်များကို ဖွင့်နေသည်…" + "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ" "နောက်ထပ် အက်ပ်များကို ရှာပါ" "အကြောင်းကြားချက်များ" "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index d8cde1b00d..0cdc2741a4 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -35,12 +35,9 @@ "%1$d bredde x %2$d høyde" "Trykk og hold for å plassere manuelt" "Legg til automatisk" - - - - - - + "Søk etter apper" + "Laster inn appene …" + "Fant ingen apper som samsvarer med «%1$s»" "Søk etter flere apper" "Varsler" "Denne startsiden er full." diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 85bec26bbe..8fcfaf1d76 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -35,12 +35,9 @@ "%1$d breed en %2$d hoog" "Tik op een item en houd dit vast om het handmatig te plaatsen" "Automatisch toevoegen" - - - - - - + "Zoeken in apps" + "Apps laden…" + "Er zijn geen apps gevonden die overeenkomen met \'%1$s\'" "Zoeken naar meer apps" "Meldingen" "Er is geen ruimte meer op dit startscherm." diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 2725407a9d..5756a18c09 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -35,12 +35,9 @@ "Szerokość %1$d, wysokość %2$d" "Kliknij i przytrzymaj, by umieścić ręcznie" "Dodaj automatycznie" - - - - - - + "Wyszukaj aplikacje" + "Ładuję aplikacje…" + "Nie znaleziono aplikacji pasujących do zapytania „%1$s”" "Wyszukaj więcej aplikacji" "Powiadomienia" "Brak miejsca na tym ekranie głównym." diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 4fee392696..e9df4c9289 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -35,12 +35,9 @@ "%1$d de largura por %2$d de altura" "Toque sem soltar para colocar manualmente" "Adicionar automaticamente" - - - - - - + "Pesquisar aplicações" + "A carregar aplicações…" + "Nenhuma aplicação correspondente a \"%1$s\"" "Pesquisar mais aplicações" "Notificações" "Sem espaço suficiente neste Ecrã principal." diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index c8fde040f8..a6ccf2eed6 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -35,12 +35,9 @@ "%1$d de largura por %2$d de altura" "Toque e mantenha pressionado para posicionar manualmente" "Adicionar automaticamente" - - - - - - + "Apps de pesquisa" + "Carregando apps…" + "Nenhum app encontrado que corresponda a \"%1$s\"" "Pesquisar mais apps" "Notificações" "Não há mais espaço na tela inicial." diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 43a82deefc..0a816f31ee 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -35,12 +35,9 @@ "%1$d lățime și %2$d înălțime" "Atingeți lung pentru a plasa manual" "Adăugați automat" - - - - - - + "Căutați aplicații" + "Se încarcă aplicații…" + "Nu s-a găsit nicio aplicație pentru „%1$s\"" "Căutați mai multe aplicații" "Notificări" "Nu mai este loc pe acest Ecran de pornire." diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 5c719f8ba2..fb1f10e5d5 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -35,12 +35,9 @@ "පළල %1$d උස %2$d" "අතින් ස්ථානගත කිරීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න" "ස්වයංක්‍රියව එක් කරන්න" - - - - - - + "යෙදුම් සොයන්න" + "යෙදුම් පූරණය වෙමින්…" + "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" "තව යෙදුම් සඳහා සොයන්න" "දැනුම්දීම්" "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index c700a3eef5..5ed92006a8 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -35,12 +35,9 @@ "šírka %1$d, výška %2$d" "Položku umiestnite ručne klepnutím a podržaním" "Pridať automaticky" - - - - - - + "Hľadať aplikácie" + "Načítavajú sa aplikácie…" + "Nenašli sa žiadne aplikácie zodpovedajúce dopytu %1$s" "Hľadať ďalšie aplikácie" "Upozornenia" "Na tejto ploche už nie je miesto" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index f2e4c71caf..d041b8ecf2 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -35,12 +35,9 @@ "ширина од %1$d и висина од %2$d" "Додирните и задржите да бисте поставили ручно" "Аутоматски додај" - - - - - - + "Претражите апликације" + "Апликације се учитавају…" + "Није пронађена ниједна апликација за „%1$s“" "Претражи још апликација" "Обавештења" "Нема више простора на овом почетном екрану." diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 7519400431..ea42245c4a 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -35,12 +35,9 @@ "%1$d bred gånger %2$d hög" "Placera manuellt genom att trycka länge" "Lägg till automatiskt" - - - - - - + "Sök efter appar" + "Läser in appar …" + "Inga appar som matchar %1$s hittades" "Sök efter fler appar" "Aviseringar" "Det finns inte plats för mer på den här startskärmen." diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index b99037f533..613ba0dde5 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -35,12 +35,9 @@ "Upana wa %1$d na kimo cha %2$d" "Gusa na ushikilie ili uweke mwenyewe" "Ongeza kiotomatiki" - - - - - - + "Tafuta programu" + "Inapakia programu..." + "Haikupata programu zozote zinazolingana na \"%1$s\"" "Tafuta programu zaidi" "Arifa" "Hakuna nafasi katika skrini hii ya Mwanzo." diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 4a87893a7f..3ddf2bcc04 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -35,12 +35,9 @@ "%1$d அகலத்திற்கு %2$d உயரம்" "நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்" "தானாகவே சேர்" - - - - - - + "பயன்பாடுகளில் தேடுக" + "பயன்பாடுகளை ஏற்றுகிறது…" + "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" "கூடுதல் பயன்பாடுகளைத் தேடு" "அறிவிப்புகள்" "முகப்புத் திரையில் இடமில்லை." diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index bad494590f..6be2b361b0 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -35,12 +35,9 @@ "กว้าง %1$d x สูง %2$d" "แตะค้างไว้เพื่อวางด้วยตัวเอง" "เพิ่มโดยอัตโนมัติ" - - - - - - + "ค้นหาแอป" + "กำลังโหลดแอป…" + "ไม่พบแอปที่ตรงกับ \"%1$s\"" "ค้นหาแอปเพิ่มเติม" "การแจ้งเตือน" "ไม่มีที่ว่างในหน้าจอหลักนี้" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 184a48390c..a91f31e80d 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -35,12 +35,9 @@ "%1$d ang lapad at %2$d ang taas" "Pindutin nang matagal upang manual na ilagay" "Awtomatikong idagdag" - - - - - - + "Maghanap ng mga app" + "Naglo-load ng mga app…" + "Walang nahanap na app na tumutugma sa \"%1$s\"" "Maghanap ng higit pang mga app" "Mga Notification" "Wala nang lugar sa Home screen na ito." diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index e33d71a2e8..d6543bb8c4 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -35,12 +35,9 @@ "genişlik: %1$d, yükseklik: %2$d" "Manuel olarak yerleştirmek için dokunun ve basılı tutun" "Otomatik olarak ekle" - - - - - - + "Uygulamalarda ara" + "Uygulamalar yükleniyor…" + "\"%1$s\" ile eşleşen uygulama bulunamadı" "Başka uygulamalar ara" "Bildirimler" "Bu Ana ekranda yer kalmadı." diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index b18b8f9751..ea58334ee6 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -35,12 +35,9 @@ "Ширина – %1$d, висота – %2$d" "Натисніть і утримуйте, щоб додати вручну" "Додати автоматично" - - - - - - + "Пошук додатків" + "Завантаження додатків…" + "Немає додатків для запиту \"%1$s\"" "Шукати ще додатки" "Сповіщення" "На цьому головному екрані більше немає місця." diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 83803f59c9..279183622f 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -35,12 +35,9 @@ "Eni %1$d, bo‘yi %2$d" "Qo‘lda joylashtirish uchun bosib turing" "Avtomatik chiqarish" - - - - - - + "Ilovalarni qidirish" + "Ilovalar yuklanmoqda…" + "“%1$s” bilan mos hech qanday ilova topilmadi" "Boshqa ilovalarni qidirish" "Bildirishnomalar" "Uy ekranida bitta ham xona yo‘q." diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 093cfee21d..3267b322f6 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -35,12 +35,9 @@ "Rộng %1$d x cao %2$d" "Chạm và giữ để đặt theo cách thủ công" "Tự động thêm" - - - - - - + "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\"" "Tìm kiếm thêm ứng dụng" "Thông báo" "Không còn chỗ trên Màn hình chính này." diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index a83b1f35db..099acb059e 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -35,12 +35,9 @@ "宽 %1$d,高 %2$d" "触摸并按住即可手动放置" "自动添加" - - - - - - + "搜索应用" + "正在加载应用…" + "未找到与“%1$s”相符的应用" "搜索更多应用" "通知" "此主屏幕上已没有空间。" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 520b18f40d..941e6f35f0 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -35,12 +35,9 @@ "%1$d 闊,%2$d 高" "按住即可手動新增" "自動新增" - - - - - - + "搜尋應用程式" + "正在載入應用程式…" + "找不到與「%1$s」相符的應用程式" "搜尋更多應用程式" "通知" "主畫面已無空間。" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index ff79d9b20a..3f1759ed65 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -35,12 +35,9 @@ "寬度為 %1$d,高度為 %2$d" "按住即可手動放置" "自動新增" - - - - - - + "搜尋應用程式" + "正在載入應用程式…" + "找不到與「%1$s」相符的應用程式" "搜尋更多應用程式" "通知" "這個主螢幕已無空間。" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 4a77c23e01..df198be273 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -35,12 +35,9 @@ "%1$d ububanzi ngokungu-%2$d ukuya phezulu" "Thinta futhi ubambe ukuze ubeke ngokwenza" "Engeza ngokuzenzakalelayo" - - - - - - + "Sesha izinhlelo zokusebenza" + "Ilayisha izinhlelo zokusebenza..." + "Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"%1$s\"" "Sesha izinhlelo zokusebenza eziningi" "Izaziso" "Asisekho isikhala kulesi sikrini Sasekhaya." From 1839f7f8c2fc2000fc8a0d0933c57813e7115ef0 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 26 Jul 2017 10:18:26 -0700 Subject: [PATCH 006/885] Import translations. DO NOT MERGE Change-Id: Ie12276d29f511c35199bb199130893e109826b83 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-pt-rPT/strings.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index e9df4c9289..100f07b66b 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -84,15 +84,13 @@ "Alterar definições" "Adicionar ícone ao ecrã principal" "Para novas aplicações" - - + "Alterar forma do ícone" "Utilizar a predefinição do sistema" "Quadrado" "Quadrado e círculo" "Círculo" "Lágrima" - - + "A aplicar alterações à forma do ícone..." "Desconhecido" "Remover" "Pesquisar" From d7f6bbc157c34d4b351869df627ec54037798ce2 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 28 Jul 2017 04:04:08 -0700 Subject: [PATCH 007/885] Import translations. DO NOT MERGE Change-Id: I8e2d5bcd2a25d60daf1802cefea210aac6a27569 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- go/res/values-af/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-am/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ar/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-az/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-b+sr+Latn/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-bs/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-da/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-en-rAU/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-en-rGB/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-en-rIN/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-fa/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-hr/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-in/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-it/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ja/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ka/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ky/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-lo/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-lt/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-nl/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-pl/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-pt-rPT/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-pt/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-si/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-sk/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-sl/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-sr/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-sv/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-sw/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-tr/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-uk/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-uz/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-zu/strings.xml | 26 ++++++++++++++++++++++++++ res/values-af/strings.xml | 6 ++---- res/values-am/strings.xml | 6 ++---- res/values-ar/strings.xml | 6 ++---- res/values-az/strings.xml | 6 ++---- res/values-b+sr+Latn/strings.xml | 6 ++---- res/values-be/strings.xml | 15 +++++---------- res/values-bg/strings.xml | 6 ++---- res/values-bn/strings.xml | 9 +++------ res/values-bs/strings.xml | 6 ++---- res/values-ca/strings.xml | 8 +++----- res/values-cs/strings.xml | 15 +++++---------- res/values-da/strings.xml | 6 ++---- res/values-de/strings.xml | 6 ++---- res/values-el/strings.xml | 15 +++++---------- res/values-en-rAU/strings.xml | 6 ++---- 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/strings.xml | 15 +++++---------- res/values-eu/strings.xml | 6 ++---- res/values-fa/strings.xml | 6 ++---- res/values-fi/strings.xml | 15 +++++---------- res/values-fr-rCA/strings.xml | 6 ++---- res/values-fr/strings.xml | 8 +++----- res/values-gl/strings.xml | 6 ++---- res/values-hi/strings.xml | 6 ++---- res/values-hr/strings.xml | 6 ++---- res/values-hu/strings.xml | 15 +++++---------- res/values-hy/strings.xml | 15 +++++---------- res/values-in/strings.xml | 6 ++---- res/values-is/strings.xml | 6 ++---- res/values-it/strings.xml | 6 ++---- res/values-iw/strings.xml | 15 +++++---------- res/values-ja/strings.xml | 6 ++---- res/values-ka/strings.xml | 6 ++---- res/values-kk/strings.xml | 15 +++++---------- res/values-km/strings.xml | 6 ++---- res/values-kn/strings.xml | 9 +++------ res/values-ko/strings.xml | 15 +++++---------- res/values-ky/strings.xml | 6 ++---- res/values-lo/strings.xml | 6 ++---- res/values-lt/strings.xml | 15 +++++---------- res/values-lv/strings.xml | 15 +++++---------- res/values-mk/strings.xml | 6 ++---- res/values-ml/strings.xml | 9 +++------ res/values-mn/strings.xml | 6 ++---- res/values-mr/strings.xml | 9 +++------ res/values-ms/strings.xml | 15 +++++---------- res/values-my/strings.xml | 6 ++---- res/values-nb/strings.xml | 6 ++---- res/values-ne/strings.xml | 9 +++------ res/values-nl/strings.xml | 8 +++----- res/values-pa/strings.xml | 9 +++------ res/values-pl/strings.xml | 6 ++---- res/values-pt/strings.xml | 6 ++---- res/values-ro/strings.xml | 6 ++---- res/values-ru/strings.xml | 15 +++++---------- res/values-si/strings.xml | 6 ++---- res/values-sk/strings.xml | 6 ++---- res/values-sl/strings.xml | 15 +++++---------- res/values-sq/strings.xml | 15 +++++---------- res/values-sr/strings.xml | 6 ++---- res/values-sv/strings.xml | 6 ++---- res/values-sw/strings.xml | 6 ++---- res/values-ta/strings.xml | 6 ++---- res/values-te/strings.xml | 9 +++------ 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-ur/strings.xml | 9 +++------ res/values-uz/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 ++---- 111 files changed, 1073 insertions(+), 427 deletions(-) create mode 100644 go/res/values-af/strings.xml create mode 100644 go/res/values-am/strings.xml create mode 100644 go/res/values-ar/strings.xml create mode 100644 go/res/values-az/strings.xml create mode 100644 go/res/values-b+sr+Latn/strings.xml create mode 100644 go/res/values-bs/strings.xml create mode 100644 go/res/values-da/strings.xml create mode 100644 go/res/values-en-rAU/strings.xml create mode 100644 go/res/values-en-rGB/strings.xml create mode 100644 go/res/values-en-rIN/strings.xml create mode 100644 go/res/values-fa/strings.xml create mode 100644 go/res/values-hr/strings.xml create mode 100644 go/res/values-in/strings.xml create mode 100644 go/res/values-it/strings.xml create mode 100644 go/res/values-ja/strings.xml create mode 100644 go/res/values-ka/strings.xml create mode 100644 go/res/values-ky/strings.xml create mode 100644 go/res/values-lo/strings.xml create mode 100644 go/res/values-lt/strings.xml create mode 100644 go/res/values-nl/strings.xml create mode 100644 go/res/values-pl/strings.xml create mode 100644 go/res/values-pt-rPT/strings.xml create mode 100644 go/res/values-pt/strings.xml create mode 100644 go/res/values-si/strings.xml create mode 100644 go/res/values-sk/strings.xml create mode 100644 go/res/values-sl/strings.xml create mode 100644 go/res/values-sr/strings.xml create mode 100644 go/res/values-sv/strings.xml create mode 100644 go/res/values-sw/strings.xml create mode 100644 go/res/values-tr/strings.xml create mode 100644 go/res/values-uk/strings.xml create mode 100644 go/res/values-uz/strings.xml create mode 100644 go/res/values-zu/strings.xml diff --git a/go/res/values-af/strings.xml b/go/res/values-af/strings.xml new file mode 100644 index 0000000000..10ac473c1f --- /dev/null +++ b/go/res/values-af/strings.xml @@ -0,0 +1,26 @@ + + + + + "Raak en hou om \'n kortpad op te tel." + "Dubbeltik en hou om \'n kortpad op te tel of gebruik gepasmaakte handelinge." + "Kortpaaie" + "%1$s-kortpaaie" + diff --git a/go/res/values-am/strings.xml b/go/res/values-am/strings.xml new file mode 100644 index 0000000000..b3b253f423 --- /dev/null +++ b/go/res/values-am/strings.xml @@ -0,0 +1,26 @@ + + + + + "አንድ አቋራጭ ለመውሰድ ነክተው ይያዙ" + "አንድ አቋራጭ ለመውሰድ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።" + "አቋራጮች" + "%1$s አቋራጮች" + diff --git a/go/res/values-ar/strings.xml b/go/res/values-ar/strings.xml new file mode 100644 index 0000000000..2b3b80746f --- /dev/null +++ b/go/res/values-ar/strings.xml @@ -0,0 +1,26 @@ + + + + + "المس مع الاستمرار لاختيار اختصار." + "يمكنك النقر نقرًا مزدوجًا مع الاستمرار لاختيار اختصار أو استخدام الإجراءات المخصصة." + "الاختصارات" + "اختصارات %1$s" + diff --git a/go/res/values-az/strings.xml b/go/res/values-az/strings.xml new file mode 100644 index 0000000000..c4b8cb7801 --- /dev/null +++ b/go/res/values-az/strings.xml @@ -0,0 +1,26 @@ + + + + + "Qısayolu seçmək üçün toxunub saxlayın." + "Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin." + "Qısayollar" + "%1$s qısayolları" + diff --git a/go/res/values-b+sr+Latn/strings.xml b/go/res/values-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..0da5699410 --- /dev/null +++ b/go/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,26 @@ + + + + + "Dodirnite i zadržite da biste izabrali prečicu." + "Dvaput dodirnite i zadržite da biste izabrali prečicu ili koristite prilagođene radnje." + "Prečice" + "Prečice za %1$s" + diff --git a/go/res/values-bs/strings.xml b/go/res/values-bs/strings.xml new file mode 100644 index 0000000000..7042468b47 --- /dev/null +++ b/go/res/values-bs/strings.xml @@ -0,0 +1,26 @@ + + + + + "Dodirnite i držite da uzmete prečicu." + "Dvaput dodirnite i držite da uzmete prečicu ili koristite prilagođene akcije." + "Prečice" + "Prečice aplikacije %1$s" + diff --git a/go/res/values-da/strings.xml b/go/res/values-da/strings.xml new file mode 100644 index 0000000000..821d36a7da --- /dev/null +++ b/go/res/values-da/strings.xml @@ -0,0 +1,26 @@ + + + + + "Hold en genvej nede for at samle den op." + "Tryk to gange, og hold en genvej nede for at samle den op og bruge tilpassede handlinger." + "Genveje" + "%1$s-genveje" + diff --git a/go/res/values-en-rAU/strings.xml b/go/res/values-en-rAU/strings.xml new file mode 100644 index 0000000000..2ee2c26457 --- /dev/null +++ b/go/res/values-en-rAU/strings.xml @@ -0,0 +1,26 @@ + + + + + "Touch & hold to pick up a shortcut." + "Double-tap & hold to pick up a shortcut or use custom actions." + "Shortcuts" + "%1$s shortcuts" + diff --git a/go/res/values-en-rGB/strings.xml b/go/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..2ee2c26457 --- /dev/null +++ b/go/res/values-en-rGB/strings.xml @@ -0,0 +1,26 @@ + + + + + "Touch & hold to pick up a shortcut." + "Double-tap & hold to pick up a shortcut or use custom actions." + "Shortcuts" + "%1$s shortcuts" + diff --git a/go/res/values-en-rIN/strings.xml b/go/res/values-en-rIN/strings.xml new file mode 100644 index 0000000000..2ee2c26457 --- /dev/null +++ b/go/res/values-en-rIN/strings.xml @@ -0,0 +1,26 @@ + + + + + "Touch & hold to pick up a shortcut." + "Double-tap & hold to pick up a shortcut or use custom actions." + "Shortcuts" + "%1$s shortcuts" + diff --git a/go/res/values-fa/strings.xml b/go/res/values-fa/strings.xml new file mode 100644 index 0000000000..8bc5256d16 --- /dev/null +++ b/go/res/values-fa/strings.xml @@ -0,0 +1,26 @@ + + + + + "برای انتخاب یک میان‌بر، لمس کنید و نگه‌دارید." + "برای انتخاب یک میان‌بر، دو ضربه سریع بزنید و نگه‌دارید یا از اقدام‌های سفارشی استفاده کنید." + "میان‌برها" + "میان‌برهای %1$s" + diff --git a/go/res/values-hr/strings.xml b/go/res/values-hr/strings.xml new file mode 100644 index 0000000000..fccdeb59d7 --- /dev/null +++ b/go/res/values-hr/strings.xml @@ -0,0 +1,26 @@ + + + + + "Dodirnite i zadržite kako biste podigli prečac." + "Dvaput dodirnite i zadržite pritisak kako biste podigli prečac ili pokušajte prilagođenim radnjama." + "Prečaci" + "Prečaci za aplikaciju %1$s" + diff --git a/go/res/values-in/strings.xml b/go/res/values-in/strings.xml new file mode 100644 index 0000000000..c8b9da306d --- /dev/null +++ b/go/res/values-in/strings.xml @@ -0,0 +1,26 @@ + + + + + "Tap lama untuk memilih pintasan." + "Tap dua kali & tahan untuk memilih pintasan atau menggunakan tindakan khusus." + "Pintasan" + "Pintasan %1$s" + diff --git a/go/res/values-it/strings.xml b/go/res/values-it/strings.xml new file mode 100644 index 0000000000..bc5d99863d --- /dev/null +++ b/go/res/values-it/strings.xml @@ -0,0 +1,26 @@ + + + + + "Tocca e tieni premuto per scegliere la scorciatoia" + "Tocca due volte e tieni premuto per scegliere una scorciatoia o per usare azioni personalizzate." + "Scorciatoie" + "Scorciatoie di %1$s" + diff --git a/go/res/values-ja/strings.xml b/go/res/values-ja/strings.xml new file mode 100644 index 0000000000..8f02d7f4eb --- /dev/null +++ b/go/res/values-ja/strings.xml @@ -0,0 +1,26 @@ + + + + + "ショートカットを追加するには押し続けます。" + "ダブルタップ後に押し続けてショートカットを選択するか、カスタム操作を使用してください。" + "ショートカット" + "「%1$s」のショートカット" + diff --git a/go/res/values-ka/strings.xml b/go/res/values-ka/strings.xml new file mode 100644 index 0000000000..1b46534788 --- /dev/null +++ b/go/res/values-ka/strings.xml @@ -0,0 +1,26 @@ + + + + + "შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად." + "ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად." + "მალსახმობები" + "%1$s-ის მალსახმობები" + diff --git a/go/res/values-ky/strings.xml b/go/res/values-ky/strings.xml new file mode 100644 index 0000000000..4c7e973cef --- /dev/null +++ b/go/res/values-ky/strings.xml @@ -0,0 +1,26 @@ + + + + + "Кыска жолду тандоо үчүн басып туруңуз." + "Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз." + "Кыска жолдор" + "%1$s кыска жол" + diff --git a/go/res/values-lo/strings.xml b/go/res/values-lo/strings.xml new file mode 100644 index 0000000000..7864884acc --- /dev/null +++ b/go/res/values-lo/strings.xml @@ -0,0 +1,26 @@ + + + + + "ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ." + "ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ." + "ປຸ່ມລັດ" + "ປຸ່ມລັດ %1$s" + diff --git a/go/res/values-lt/strings.xml b/go/res/values-lt/strings.xml new file mode 100644 index 0000000000..8f49032cfb --- /dev/null +++ b/go/res/values-lt/strings.xml @@ -0,0 +1,26 @@ + + + + + "Dukart pal. ir palaik., kad pasir. spart. klav." + "Dukart palieskite ir palaikykite, kad pasirinkt. spartųjį klavišą ar naudotumėte tinkintus veiksmus." + "Spartieji klavišai" + "„%1$s“ spartieji klavišai" + diff --git a/go/res/values-nl/strings.xml b/go/res/values-nl/strings.xml new file mode 100644 index 0000000000..5bcd016b4a --- /dev/null +++ b/go/res/values-nl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Tik en houd vast om snelkoppeling toe te voegen." + "Dubbeltik en houd vast om een snelkoppeling toe te voegen of aangepaste acties te gebruiken." + "Snelkoppelingen" + "%1$s-snelkoppelingen" + diff --git a/go/res/values-pl/strings.xml b/go/res/values-pl/strings.xml new file mode 100644 index 0000000000..45a1dc2cac --- /dev/null +++ b/go/res/values-pl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Kliknij i przytrzymaj, by wybrać skrót." + "Kliknij dwukrotnie i przytrzymaj, by wybrać skrót lub użyć działań niestandardowych." + "Skróty" + "%1$s – skróty" + diff --git a/go/res/values-pt-rPT/strings.xml b/go/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..7a75a05456 --- /dev/null +++ b/go/res/values-pt-rPT/strings.xml @@ -0,0 +1,26 @@ + + + + + "Toque sem soltar para escolher um atalho." + "Toque duas vezes sem soltar para escolher um atalho ou utilize ações personalizadas." + "Atalhos" + "Atalhos da aplicação %1$s" + diff --git a/go/res/values-pt/strings.xml b/go/res/values-pt/strings.xml new file mode 100644 index 0000000000..53bbfc41a6 --- /dev/null +++ b/go/res/values-pt/strings.xml @@ -0,0 +1,26 @@ + + + + + "Toque e segure para selecionar um atalho." + "Toque duas vezes na tela e segure para selecionar um atalho ou usar ações personalizadas." + "Atalhos" + "Atalhos do app %1$s" + diff --git a/go/res/values-si/strings.xml b/go/res/values-si/strings.xml new file mode 100644 index 0000000000..4b25c90b0b --- /dev/null +++ b/go/res/values-si/strings.xml @@ -0,0 +1,26 @@ + + + + + "කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න." + "විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න." + "කෙටි මං" + "කෙටි මං %1$s" + diff --git a/go/res/values-sk/strings.xml b/go/res/values-sk/strings.xml new file mode 100644 index 0000000000..fc02933734 --- /dev/null +++ b/go/res/values-sk/strings.xml @@ -0,0 +1,26 @@ + + + + + "Skratku pridáte pridržaním." + "Skratku pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií." + "Skratky" + "Skratky aplikácie %1$s" + diff --git a/go/res/values-sl/strings.xml b/go/res/values-sl/strings.xml new file mode 100644 index 0000000000..6ecedfb166 --- /dev/null +++ b/go/res/values-sl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Pridržite bližnjico, da jo izberete." + "Dvakrat se dotaknite bližnjice in jo pridržite, da jo izberete, ali pa uporabite dejanja po meri." + "Bližnjice" + "Bližnjice za %1$s" + diff --git a/go/res/values-sr/strings.xml b/go/res/values-sr/strings.xml new file mode 100644 index 0000000000..0b9aea2f9a --- /dev/null +++ b/go/res/values-sr/strings.xml @@ -0,0 +1,26 @@ + + + + + "Додирните и задржите да бисте изабрали пречицу." + "Двапут додирните и задржите да бисте изабрали пречицу или користите прилагођене радње." + "Пречице" + "Пречице за %1$s" + diff --git a/go/res/values-sv/strings.xml b/go/res/values-sv/strings.xml new file mode 100644 index 0000000000..c3f434c276 --- /dev/null +++ b/go/res/values-sv/strings.xml @@ -0,0 +1,26 @@ + + + + + "Tryck länge om du vill ta upp en genväg." + "Tryck snabbt två gånger och håll kvar om du vill ta upp en genväg eller använda anpassade åtgärder." + "Genvägar" + "Genvägar för %1$s" + diff --git a/go/res/values-sw/strings.xml b/go/res/values-sw/strings.xml new file mode 100644 index 0000000000..0379ed012a --- /dev/null +++ b/go/res/values-sw/strings.xml @@ -0,0 +1,26 @@ + + + + + "Gusa na ushikilie ili uchague njia ya mkato." + "Gonga mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum." + "Njia za mkato" + "Njia za mkato za %1$s" + diff --git a/go/res/values-tr/strings.xml b/go/res/values-tr/strings.xml new file mode 100644 index 0000000000..f0f3cce6d1 --- /dev/null +++ b/go/res/values-tr/strings.xml @@ -0,0 +1,26 @@ + + + + + "Kısayol seçmek için dokunun ve basılı tutun." + "Bir kısayolu seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun." + "Kısayollar" + "%1$s kısayolları" + diff --git a/go/res/values-uk/strings.xml b/go/res/values-uk/strings.xml new file mode 100644 index 0000000000..8d1f58395b --- /dev/null +++ b/go/res/values-uk/strings.xml @@ -0,0 +1,26 @@ + + + + + "Натисніть і утримуйте, щоб вибрати ярлик." + "Двічі натисніть і утримуйте, щоб вибрати ярлик, або виконайте іншу дію." + "Ярлики" + "Ярлики додатка %1$s" + diff --git a/go/res/values-uz/strings.xml b/go/res/values-uz/strings.xml new file mode 100644 index 0000000000..318bc15722 --- /dev/null +++ b/go/res/values-uz/strings.xml @@ -0,0 +1,26 @@ + + + + + "Yorliqni tanlab olish uchun bosib turing." + "Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning." + "Yorliqlar" + "%1$s ilovasi yorliqlari" + diff --git a/go/res/values-zu/strings.xml b/go/res/values-zu/strings.xml new file mode 100644 index 0000000000..e5051df1f1 --- /dev/null +++ b/go/res/values-zu/strings.xml @@ -0,0 +1,26 @@ + + + + + "Thinta futhi ubambe ukuze ukhethe isinqamuleli." + "Thepha kabulu futhi ubambe ukuze ukhethe isinqamuleli noma usebenzise izenzo zangokwezifiso." + "Izinqamuleli" + "%1$s izinqamuleli" + diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index b0a12c8afb..d71b4c78b3 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -84,15 +84,13 @@ "Verander instellings" "Voeg ikoon by tuisskerm" "Vir nuwe programme" - - + "Verander ikoon se vorm" "Gebruik stelselverstek" "Vierkant" "Sirkelvierkant" "Sirkel" "Traandruppel" - - + "Pas ikoonvormveranderings toe" "Onbekend" "Verwyder" "Soek" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 3517d0163a..293b452f68 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -84,15 +84,13 @@ "ቅንብሮችን ቀይር" "አዶ ወደ የመነሻ ማያ ገጽ አክል" "ለአዲስ መተግበሪያዎች" - - + "የአዶ ቅርፅ ለውጥ" "የሥርዓቱን ነባሪ ተጠቀም" "ካሬ" "Squircle" "ክብ" "የእንባ ጠብታ" - - + "የአዶ ቅርች ለውጦች" "የማይታወቅ" "አስወግድ" "ፈልግ" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 5b675f34c1..6447bf0150 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -84,15 +84,13 @@ "تغيير الإعدادات" "إضافة رمز إلى الشاشة الرئيسية" "للتطبيقات الجديدة" - - + "تغيير شكل الرمز" "استخدام الإعداد الافتراضي للنظام" "مربّع" "رمز دائري مربّع" "دائرة" "رمز على شكل دمعة" - - + "جارٍ تطبيق تغييرات شكل الرمز" "غير معروفة" "إزالة" "بحث" diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 4db1886167..18f87bcf33 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -84,15 +84,13 @@ "Ayarları dəyişin" "Əsas ekrana ikona əlavə edin" "Yeni tətbiqlər üçün" - - + "İkona formasını dəyişin" "Sistem defoltu istifadə edin" "Kvadrat" "Kənarları dairəvi kvadrat" "Çevrə" "Gözyaşı damlası" - - + "İkona formasına etdiyiniz dəyişikliklər tətbiq edilir" "Naməlum" "Yığışdır" "Axtarış" diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 520b027ecb..777512a966 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -84,15 +84,13 @@ "Promenite podešavanja" "Dodaj ikonu na početni ekran" "Za nove aplikacije" - - + "Promenite oblik ikona" "Koristi podrazumevano sistemsko podešavanje" "Kvadrat" "Zaobljeni kvadrat" "Krug" "Suza" - - + "Primenjuju se promene oblika ikona" "Nepoznato" "Ukloni" "Pretraži" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index c9bd447228..0a31ddf7c1 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -35,12 +35,9 @@ "Шырына: %1$d, вышыня: %2$d" "Каб размясціць уручную, дакраніцеся і ўтрымлівайце" "Дадаць аўтаматычна" - - - - - - + "Пошук праграм" + "Праграмы загружаюцца…" + "Праграм, якія адпавядаюць запыту \"%1$s\", не знойдзена" "Шукаць іншыя праграмы" "Апавяшчэнні" "На гэтым Галоўным экране больш няма месца." @@ -87,15 +84,13 @@ "Змяніць налады" "Дадаць значок на Галоўны экран" "Для новых праграм" - - + "Змяніць форму значка" "Выкарыстоўваць стандартныя формы" "Квадрат" "Прамавугольнік са скругленымі вугламі" "Круг" "Сляза" - - + "Змены формы значка прымяняюцца" "Невядома" "Выдаліць" "Шукаць" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index d6400ced20..3e05e0be57 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -84,15 +84,13 @@ "Промяна на настройките" "Добавяне на икона към началния екран" "За нови приложения" - - + "Промяна на формата на иконите" "Използване на стандартната системна настройка" "Квадрат" "Комбинация от кръг и квадрат" "Кръг" "Сълза" - - + "Промените във формата на иконите се прилагат" "Няма информация" "Премахване" "Търсене" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 97bb90af24..92f1affb69 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -35,12 +35,9 @@ "%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ" "নিজে যোগ করতে টাচ করে ধরে রাখুন" "স্বয়ংক্রিয়ভাবে যোগ করুন" - - - - - - + "অ্যাপ অনুসন্ধান করুন" + "অ্যাপ লোড হচ্ছে…" + "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি" "আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন" "বিজ্ঞপ্তি" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 34ef0b42a0..49c862a8cd 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -84,15 +84,13 @@ "Promijeni postavke" "Dodajte ikonu na početni ekran" "Za nove aplikacije" - - + "Promjena oblika ikona" "Koristite sistemski zadano" "Kvadrat" "Zaobljeni kvadrat" "Krug" "Suza" - - + "Primjenjivanje promjena oblika ikona" "Nepoznato" "Ukloni" "Traži" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 05c43c61d7..b1a1f85e06 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -37,7 +37,7 @@ "Afegeix automàticament" "Cerca aplicacions" "S\'estan carregant les aplicacions…" - "No s\'ha trobat cap aplicació que coincideixi amb %1$s" + "No s\'ha trobat cap aplicació que coincideixi amb \"%1$s\"" "Cerca més aplicacions" "Notificacions" "Ja no queda espai en aquesta pantalla d\'inici." @@ -84,15 +84,13 @@ "Canvia la configuració" "Afegeix la icona a la pantalla d\'inici" "Per a les aplicacions noves" - - + "Canvia la forma de les icones" "Utilitza l\'opció predeterminada del sistema" "Quadrat" "Quadrat arrodonit" "Cercle" "Llàgrima" - - + "S\'estan aplicant els canvis de forma de les icones" "Desconegut" "Suprimeix" "Cerca" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 7cef876d7f..abc44c7142 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -35,12 +35,9 @@ "šířka %1$d, výška %2$d" "Chcete-li položku umístit ručně, klepněte na ni a podržte ji" "Přidat automaticky" - - - - - - + "Hledat v aplikacích" + "Načítání aplikací…" + "Dotazu „%1$s“ neodpovídají žádné aplikace" "Vyhledat další aplikace" "Oznámení" "Na této ploše již není místo." @@ -87,15 +84,13 @@ "Změnit nastavení" "Přidat ikonu na plochu" "Pro nové aplikace" - - + "Změnit tvar ikony" "Použít výchozí nastavení systému" "Čtverec" "Kruh/čtverec" "Kruh" "Slza" - - + "Tvar ikony se mění" "Neznámé" "Odstranit" "Hledat" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index c5bf485574..653105e058 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -84,15 +84,13 @@ "Skift indstillinger" "Føj ikon til startskærmen" "For nye apps" - - + "Skift ikonform" "Brug systemstandarden" "Kvadrat" "Kvadrat med runde hjørner" "Cirkel" "Dråbeform" - - + "Anvender ændringer af ikonform" "Ukendt" "Fjern" "Søg" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index cda1fc5325..1c4cd7a7ca 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -84,15 +84,13 @@ "Einstellungen ändern" "Symbol zu Startbildschirm hinzufügen" "Für neue Apps" - - + "Form des Symbols ändern" "Systemstandardeinstellung verwenden" "Quadrat" "Superkreis" "Kreis" "Träne" - - + "Änderungen an der Form des Symbols werden übernommen" "Unbekannt" "Entfernen" "Suchen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 1f27f2c6c2..25c8866b85 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -35,12 +35,9 @@ "Πλάτος %1$d επί ύψος %2$d" "Αγγίξτε παρατεταμένα για μη αυτόματη τοποθέτηση" "Αυτόματη προσθήκη" - - - - - - + "Αναζήτηση εφαρμογών" + "Φόρτωση εφαρμογών…" + "Δεν βρέθηκαν εφαρμογές αντιστοίχισης για \"%1$s\"" "Αναζήτηση περισσότερων εφαρμογών" "Ειδοποιήσεις" "Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη." @@ -87,15 +84,13 @@ "Αλλαγή ρυθμίσεων" "Προσθήκη εικονιδίου στην Αρχική οθόνη" "Για νέες εφαρμογές" - - + "Αλλαγή σχήματος εικονιδίου" "Χρήση προεπιλογής συστήματος" "Τετράγωνο" "Στρογγυλεμένο τετράγωνο" "Κύκλος" "Δάκρυ" - - + "Εφαρμογή αλλαγών σχήματος εικονιδίων" "Άγνωστο" "Κατάργηση" "Αναζήτηση" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 88b839f0d4..f3f10cc82a 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -84,15 +84,13 @@ "Change settings" "Add icon to Home screen" "For new apps" - - + "Change icon shape" "Use system default" "Square" "Squircle" "Circle" "Teardrop" - - + "Applying icon shape changes" "Unknown" "Remove" "Search" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 88b839f0d4..f3f10cc82a 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -84,15 +84,13 @@ "Change settings" "Add icon to Home screen" "For new apps" - - + "Change icon shape" "Use system default" "Square" "Squircle" "Circle" "Teardrop" - - + "Applying icon shape changes" "Unknown" "Remove" "Search" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 88b839f0d4..f3f10cc82a 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -84,15 +84,13 @@ "Change settings" "Add icon to Home screen" "For new apps" - - + "Change icon shape" "Use system default" "Square" "Squircle" "Circle" "Teardrop" - - + "Applying icon shape changes" "Unknown" "Remove" "Search" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 557c62b9bc..f60e1862ae 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -84,15 +84,13 @@ "Cambiar la configuración" "Agregar ícono a la pantalla principal" "Para nuevas apps" - - + "Cambiar forma de los íconos" "Usar el sistema predeterminado" "Cuadrado" "Cuadrado con esquinas redondeadas" "Círculo" "Gota" - - + "Aplicando cambio en la forma de los íconos" "Desconocido" "Eliminar" "Buscar" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 3185e7a697..3ab41caf69 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -84,15 +84,13 @@ "Cambiar ajustes" "Añadir icono a la pantalla de inicio" "Para nuevas aplicaciones" - - + "Cambiar forma de los iconos" "Usar opción predeterminada del sistema" "Cuadrado" "Cuadrado con esquinas redondeadas" "Círculo" "Lágrima" - - + "Aplicando cambios en la forma de los iconos" "Desconocido" "Quitar" "Buscar" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 9105883c1a..9d9991ddce 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -35,12 +35,9 @@ "%1$d lai ja %2$d kõrge" "Puudutage pikalt, et käsitsi asetada" "Lisa automaatselt" - - - - - - + "Otsige rakendusi" + "Rakenduste laadimine …" + "Päringule „%1$s” ei vastanud ükski rakendus" "Otsi rohkem rakendusi" "Märguanded" "Sellel avaekraanil pole enam ruumi." @@ -87,15 +84,13 @@ "Seadete muutmine" "Lisa ikoon avaekraanile" "Uute rakenduste puhul" - - + "Ikooni kuju muutmine" "Kasuta süsteemi vaikeseadet" "Ruut" "Ümarate nurkadega ruut" "Ring" "Tilk" - - + "Ikooni kuju muudatuste rakendamine" "Teadmata" "Eemalda" "Otsing" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 96aed2efe8..b6082fadab 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -84,15 +84,13 @@ "Aldatu ezarpenak" "Gehitu ikonoa hasierako pantailan" "Aplikazio berrietan" - - + "Aldatu ikonoaren forma" "Erabili sistemaren balio lehenetsiak" "Karratua" "Ertz biribilduko karratua" "Zirkulua" "Malkoa" - - + "Ikonoaren forman egindako aldaketak aplikatzen" "Ezezaguna" "Kendu" "Bilatu" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index ec4677e09b..8fe874b321 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -84,15 +84,13 @@ "تغییر تنظیمات" "افزودن نماد به صفحه اصلی" "برای برنامه‌های جدید" - - + "تغییر شکل نماد" "استفاده از پیش‌فرض سیستم" "مربع" "مربع با گوشه‌های گرد" "دایره" "قطره اشک" - - + "درحال اعمال کردن تغییرات شکل نماد" "نامشخص" "حذف" "جستجو" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index f4ef7bfe5f..016dbbce37 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -35,12 +35,9 @@ "Leveys: %1$d, korkeus: %2$d" "Sijoita manuaalisesti koskettamalla pitkään." "Lisää automaattisesti" - - - - - - + "Hae sovelluksia" + "Ladataan sovelluksia…" + "%1$s ei palauttanut sovelluksia." "Hae lisää sovelluksia" "Ilmoitukset" "Tässä aloitusruudussa ei ole enää tilaa." @@ -87,15 +84,13 @@ "Muuta asetuksia" "Lisää kuvake aloitusruutuun" "Uusille sovelluksille" - - + "Muuta kuvakkeen muotoa" "Käytä järjestelmän oletusarvoa" "Neliö" "Ympyräneliö" "Ympyrä" "Pisara" - - + "Sovelletaan kuvakkeiden muotojen muutoksia" "Tuntematon" "Poista" "Haku" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 25f90b7783..e57433d0c3 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -84,15 +84,13 @@ "Modifier les paramètres" "Ajouter l\'icône à l\'écran d\'accueil" "Pour les nouvelles applications" - - + "Modifier la forme de l\'icône" "Utiliser les valeurs système par défaut" "Carré" "Carré aux coins ronds" "Cercle" "Goutte" - - + "Application des changements à la forme de l\'icône en cours…" "Inconnu" "Supprimer" "Rechercher" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 6efbedabf3..dde439288c 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -70,7 +70,7 @@ "Dossier \"%1$s\"" "Widgets" "Fonds d\'écran" - "Paramètres du domicile" + "Paramètres de l\'écran d\'accueil" "Désactivé par votre administrateur" "Vue d\'ensemble" "Autoriser la rotation de l\'écran d\'accueil" @@ -84,15 +84,13 @@ "Modifier les paramètres" "Ajouter l\'icône à l\'écran d\'accueil" "Pour les nouvelles applications" - - + "Modifier la forme des icônes" "Utiliser la valeur système par défaut" "Carré" "Squircle" "Cercle" "Goutte" - - + "Application des modifications de forme des icônes…" "Inconnu" "Supprimer" "Rechercher" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 06be050603..d55514d1e9 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -84,15 +84,13 @@ "Cambiar configuración" "Engadir icona á pantalla de inicio" "Para novas aplicacións" - - + "Cambiar forma das iconas" "Usar valores predeterminados do sistema" "Cadrado" "Cadrado de bordos redondeados" "Círculo" "Bágoa" - - + "Aplicando cambios na forma das iconas" "Descoñecido" "Eliminar" "Buscar" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index b899f427f9..02c470ba07 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -84,15 +84,13 @@ "सेटिंग बदलें" "होम स्क्रीन में आइकन जोड़ें" "नए ऐप्लिकेशन के लिए" - - + "आइकन का आकार बदलें" "सिस्टम डिफ़ॉल्ट का उपयोग करें" "वर्ग" "गोल कोनों वाला वर्ग" "मंडली" "आंसू की बूंद" - - + "आइकन के आकार में बदलाव लागू किए जा रहे हैं" "अज्ञात" "निकालें" "खोजें" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 5e20333135..aaae258b80 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -84,15 +84,13 @@ "Promjena postavki" "Dodaj ikonu na početni zaslon" "Za nove aplikacije" - - + "Promijeni oblik ikona" "Upotrijebi zadane postavke sustava" "Kvadrat" "Zaobljeni kvadrat" "Krug" "Suza" - - + "Primjena promjena oblika ikona" "Nepoznato" "Ukloni" "Traži" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 1c58702f9f..2da64b06bc 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -35,12 +35,9 @@ "%1$d széles és %2$d magas" "Tartsa lenyomva a manuális hozzáadáshoz" "Automatikus hozzáadás" - - - - - - + "Alkalmazások keresése" + "Alkalmazások betöltése…" + "Nem található alkalmazás a(z) „%1$s” lekérdezésre" "További alkalmazások keresése" "Értesítések" "Nincs több hely ezen a kezdőképernyőn." @@ -87,15 +84,13 @@ "Beállítások módosítása" "Ikon hozzáadása a kezdőképernyőhöz" "Új alkalmazásoknál" - - + "Ikon formájának módosítása" "Alapértelmezett érték használata" "Négyzet" "Squircle" "Kör" "Könnycsepp" - - + "Ikonforma módosításainak alkalmazása…" "Ismeretlen" "Eltávolítás" "Keresés" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 166ba03f91..2b55e966a4 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -35,12 +35,9 @@ "Լայնությունը՝ %1$d, բարձրությունը՝ %2$d" "Հպեք և պահեք՝ ձեռքով տեղադրելու համար" "Ավելացնել ավտոմատ կերպով" - - - - - - + "Որոնել հավելվածներ" + "Հավելվածների բեռնում…" + %1$s» հարցմանը համապատասխանող հավելվածներ չեն գտնվել" "Որոնել այլ հավելվածներ" "Ծանուցումներ" "Այլևս տեղ չկա այս հիմնական էկրանին:" @@ -87,15 +84,13 @@ "Փոխել կարգավորումները" "Ավելացնել պատկերակը Հիմնական էկրանին" "Նոր հավելվածների համար" - - + "Փոխել պատկերակների տեսքը" "Օգտագործել համակարգի կանխադրված կարգավորումը" "Քառակուսի" "Քառանկյուն" "Օղակ" "Արցունքաձև" - - + "Պատկերակների տեսքի փոփոխությունները կիրառվում են" "Անհայտ է" "Հեռացնել" "Գտնել" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 74806c96e8..379a960d05 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -84,15 +84,13 @@ "Ubah setelan" "Tambahkan ikon ke layar Utama" "Untuk aplikasi baru" - - + "Ubah bentuk ikon" "Gunakan default sistem" "Persegi" "Persegi bundar" "Lingkaran" "Butiran Air" - - + "Menerapkan perubahan bentuk ikon" "Tidak dikenal" "Buang" "Telusuri" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 598840f802..cf178a56ad 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -84,15 +84,13 @@ "Breyta stillingum" "Bæta tákni á heimaskjáinn" "Fyrir ný forrit" - - + "Breyta formi tákns" "Nota sjálfgildi kerfis" "Ferningur" "Ferhringur" "Hringur" "Dropi" - - + "Breytir formi tákns" "Óþekkt" "Fjarlægja" "Leita" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index ca40ed882d..a52aee4295 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -84,15 +84,13 @@ "Modifica impostazioni" "Aggiungi icone alla schermata Home" "Per le nuove app" - - + "Cambia la forma delle icone" "Usa impostazione predefinita di sistema" "Quadrato" "Supercerchio" "Cerchio" "Goccia" - - + "Applicazione delle modifiche alla forma delle icone" "Sconosciuto" "Rimuovi" "Cerca" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index b7751fbe75..650a90db81 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -35,12 +35,9 @@ "‏רוחב %1$d על גובה %2$d" "גע והחזק כדי להוסיף ידנית" "הוסף באופן אוטומטי" - - - - - - + "חיפוש אפליקציות" + "טוען אפליקציות…" + "לא נמצאו אפליקציות התואמות ל-\"%1$s\"" "חפש אפליקציות נוספות" "הודעות" "אין עוד מקום במסך דף הבית הזה." @@ -87,15 +84,13 @@ "שנה את ההגדרות" "הוספת סמל במסך דף הבית" "לאפליקציות חדשות" - - + "שינוי הצורה של הסמלים" "השתמש בברירת המחדל של המערכת" "ריבוע" "ריבוע בעל פינות מעוגלות" "מעגל" "טיפה" - - + "משנה את הצורה של הסמלים" "לא ידוע" "הסר" "חפש" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 864978893d..4071a6cc99 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -84,15 +84,13 @@ "設定を変更" "ホーム画面にアイコンを追加" "新しいアプリをダウンロードしたときに" - - + "アイコンの形の変更" "システムのデフォルトを使用" "スクエア" "スクワークル" "サークル" "ティアドロップ" - - + "アイコンの形の変更を適用しています" "不明" "削除" "検索" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 443e6c1e9c..7e8b46ce88 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -84,15 +84,13 @@ "პარამეტრების შეცვლა" "ხატულას მთავარ ეკრანზე დამატება" "ახალი აპებისთვის" - - + "ხატულას ფორმის შეცვლა" "ნაგულისხმევი სისტემური პარამეტრების გამოყენება" "კვადრატი" "წრეკუთხედი" "წრე" "წვეთი" - - + "მიმდინარეობს ხატულას ფორმის ცვლილებების მიყენება" "უცნობი" "ამოშლა" "ძიება" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index f66e0c7f24..50ed9aa680 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -35,12 +35,9 @@ "Ені: %1$d, биіктігі: %2$d" "Қолмен қою үшін басып тұрыңыз" "Автоматты енгізу" - - - - - - + "Қолданбаларды іздеу" + "Қолданбалар жүктелуде…" + "\"%1$s\" сұрауына сәйкес келетін қолданбалар жоқ" "Қосымша қолданбалар іздеу" "Хабарландырулар" "Бұл Негізгі экранда орын қалмады." @@ -87,15 +84,13 @@ "Параметрлерді өзгерту" "Негізгі экранға белгіше енгізу" "Жаңа қолданбаларға арналған" - - + "Белгіше пішінін өзгерту" "Жүйенің әдепкі параметрін пайдалану" "Шаршы" "Жұмыр төртбұрыш" "Шеңбер" "Тамшы" - - + "Белгіше пішіні өзгерістері күшіне енуде" "Белгісіз" "Алып тастау" "Іздеу" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index fd10f21157..7028f7e3ae 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -84,15 +84,13 @@ "ប្ដូរ​ការកំណត់" "បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម" "សម្រាប់កម្មវិធីថ្មី" - - + "ប្តូររូបរាងរូបតំណាង" "ប្រើលំនាំដើមរបស់ប្រព័ន្ធ" "ការ៉េ" "ការ៉េជ្រុងកោង" "រង្វង់" "តំណក់​ទឹកភ្នែក" - - + "កំពុងអនុវត្ត​ការប្តូររូបរាងរូបតំណាង" "មិន​ស្គាល់" "លុបចេញ" "ស្វែងរក" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 2c6fa09c3b..9a31649be3 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -35,12 +35,9 @@ "%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ" "ಹಸ್ತಚಾಲಿತವಾಗಿ ಸೇರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ" "ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಿ" - - - - - - + "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ" + "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..." + "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" "ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ" "ಅಧಿಸೂಚನೆಗಳು" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 459e7b5fa6..e3511d4413 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -35,12 +35,9 @@ "너비 %1$d, 높이 %2$d" "길게 터치하여 직접 장소 추가" "자동으로 추가" - - - - - - + "앱 검색" + "앱 로드 중…" + "\'%1$s\'과(와) 일치하는 앱이 없습니다." "더 많은 앱 검색" "알림" "홈 화면에 더 이상 공간이 없습니다." @@ -87,15 +84,13 @@ "설정 변경" "홈 화면에 아이콘 추가" "새로 설치한 앱에 적용" - - + "아이콘 모양 변경" "시스템 기본값 사용" "정사각형" "모서리가 둥근 정사각형" "원" "눈물방울" - - + "아이콘 모양 변경사항을 적용하는 중입니다." "알 수 없음" "삭제" "검색" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 10038c15f5..85ba8be3c3 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -84,15 +84,13 @@ "Жөндөөлөрдү өзгөртүү" "Башкы экранга сүрөтчө кошуу" "Жаңы колдонмолор үчүн" - - + "Сүрөтчөнүн формасын өзгөртүү" "Тутум сушунтаган демейкисин колдонуу" "Чарчы" "Бурчтары жумуру төрт бурчтук" "Тегерек" "Тамчы" - - + "Өзгөртүлгөн сүрөтчөнүн формасы колдонулууда" "Белгисиз" "Алып салуу" "Издөө" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index e91e9f9b88..9a50c4b517 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -84,15 +84,13 @@ "ບັນທຶກການຕັ້ງຄ່າ" "ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ" "ສຳລັບແອັບໃໝ່" - - + "ປ່ຽນຮູບຮ່າງໄອຄອນ" "ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ" "ສີ່ຫຼ່ຽມຈັດຕຸລັດ" "ສີ່ຫຼ່ຽມຂອບມົນ" "ວົງມົນ" "ນ້ຳຢອດ" - - + "ນຳໃຊ້ການປ່ຽນແປງຮູບຮ່າງໄອຄອນ" "​ບໍ່​ຮູ້​ຈັກ" "ລຶບ​" "ຊອກຫາ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index f8e461723c..d415e4df1b 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -35,12 +35,9 @@ "%1$d plotis ir %2$d aukštis" "Palieskite ir palaikykite, kad padėtumėte patys" "Pridėti automatiškai" - - - - - - + "Paieškos programos" + "Įkeliamos programos…" + "Nerasta jokių užklausą „%1$s“ atitinkančių programų" "Ieškoti daugiau programų" "Pranešimai" "Šiame pagrindiniame ekrane vietos nebėra." @@ -87,15 +84,13 @@ "Keisti nustatymus" "Pridėti piktogr. prie pagrindinio ekrano" "Skirta naujoms programoms" - - + "Pakeisti piktogramos formą" "Naudoti numatytuosius sistemos nustatymus" "Kvadratas" "Kvadratais suapvalintais kampais" "Apskritimas" "Ašara" - - + "Taikomi piktogramos formos pakeitimai" "Nežinoma" "Pašalinti" "Ieškoti" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 106d15279f..2d8bfd83a3 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -35,12 +35,9 @@ "%1$d plats un %2$d augsts" "Pieskarieties un turiet, lai manuāli pievienotu" "Pievienot automātiski" - - - - - - + "Meklēt lietotnes" + "Notiek lietotņu ielāde…" + "Vaicājumam “%1$s” neatbilda neviena lietotne" "Meklēt citas lietotnes" "Paziņojumi" "Šajā sākuma ekrānā vairs nav vietas." @@ -87,15 +84,13 @@ "Mainīt iestatījumus" "Pievienot ikonu sākuma ekrānā" "Jaunām lietotnēm" - - + "Mainīt ikonu formu" "Izmantot sistēmas noklusējumu" "Kvadrāts" "Kvadrāts ar noapaļotiem stūriem" "Aplis" "Lāse" - - + "Tiek piemērotas ikonu formas izmaiņas" "Nezināma" "Noņemt" "Meklēt" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 39de6ef818..7d95a23a6c 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -84,15 +84,13 @@ "Промени ги поставките" "Додајте икона на почетниот екран" "За нови апликации" - - + "Променете ја формата на иконата" "Користи ја стандардната поставка на системот" "Квадрат" "Заоблен квадрат" "Круг" "Солза" - - + "Се применуваат промените на формата на иконата" "Непознато" "Отстрани" "Барај" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 3f1b805631..b6087546cd 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -35,12 +35,9 @@ "%1$d വീതിയും %2$d ഉയരവും" "സ്വമേധയാ സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക" "സ്വയമേവ ചേർക്കുക" - - - - - - + "ആപ്പുകൾ തിരയുക" + "ആപ്പുകൾ ലോഡുചെയ്യുന്നു..." + "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല" "കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക" "അറിയിപ്പുകൾ" "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 2b5b7beb20..22479cc3e4 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -84,15 +84,13 @@ "Тохиргоог өөрчлөх" "Нүүр хуудаст дүрс тэмдэг нэмэх" "Шинэ аппад зориулсан" - - + "Дүрс тэмдгийн хэлбэрийг өөрчлөх" "Системийн өгөгдмөл тохиргоог ашиглах" "Дөрвөлжин" "Мохоо өнцөгтэй дөрвөлжин" "Дугуй" "Дусал" - - + "Дүрс тэмдгийн хэлбэрийг өөрчилж байна" "Тодорхойгүй" "Устгах" "Хайх" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index e048910dc8..4fa8d49fe3 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -35,12 +35,9 @@ "%1$d रूंद बाय %2$d उंच" "स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा" "स्वयंचलितपणे जोडा" - - - - - - + "अॅप्स शोधा" + "अॅप्स लोड करत आहे…" + "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" "अधिक अॅप्स शोधा" "सूचना" "या मुख्य स्क्रीनवर आणखी जागा नाही." diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 27ae890031..f94b167a69 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -35,12 +35,9 @@ "Lebar %1$d kali tinggi %2$d" "Sentuh & tahan untuk meletakkan widget/ikon secara manual" "Tambahkan secara automatik" - - - - - - + "Cari apl" + "Memuatkan apl…" + "Tiada apl yang ditemui sepadan dengan \"%1$s\"" "Cari lagi apl" "Pemberitahuan" "Tiada lagi ruang pada skrin Laman Utama ini." @@ -87,15 +84,13 @@ "Tukar tetapan" "Tambahkan ikon pada Skrin Utama" "Untuk apl baharu" - - + "Tukar bentuk ikon" "Gunakan lalai sistem" "Segi empat sama" "Segi empat berbucu bulat" "Bulatan" "Titisan air mata" - - + "Menggunakan perubahan bentuk ikon" "Tidak diketahui" "Alih keluar" "Carian" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index 63ca189679..74574b9b23 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -84,15 +84,13 @@ "ဆက်တင်များ ပြောင်းရန်" "ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်" "အက်ပ်အသစ်များအတွက်" - - + "သင်္ကေတပုံစံကို ပြောင်းရန်" "စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်" "လေးထောင့်" "စတုရန်းမကျ စက်ဝိုင်းမကျပုံ" "စက်ဝိုင်း" "မျက်ရည်စက်ပုံ" - - + "သင်္ကေတပုံစံကို ပြောင်းလဲနေသည်" "မသိရ" "ဖယ်ရှားရန်" "ရှာဖွေရန်" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 0cdc2741a4..bd348b0aab 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -84,15 +84,13 @@ "Endre innstillingene" "Legg til ikon på startsiden" "For nye apper" - - + "Endre ikonets form" "Bruk systemstandard" "Kvadrat" "Superellipse" "Sirkel" "Dråpe" - - + "Aktiverer endringer av ikonets form" "Ukjent" "Fjern" "Søk" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 3214a94c28..904dad45e2 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -35,12 +35,9 @@ "%1$d चौडाइ गुणा %2$d उचाइ" "म्यानुअल तरिकाले थप्न छुनुहोस् र थिची राख्नुहोस्‌" "स्वतः थप्नुहोस्" - - - - - - + "खोजसम्बन्धी अनुप्रयोगहरू" + "अनुप्रयोगहरू लोड गर्दै…" + "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोग भेटिएन" "थप अनुप्रयोगहरू खोज्नुहोस्" "सूचनाहरू" "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 8fcfaf1d76..7e8def4ca8 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -35,7 +35,7 @@ "%1$d breed en %2$d hoog" "Tik op een item en houd dit vast om het handmatig te plaatsen" "Automatisch toevoegen" - "Zoeken in apps" + "Apps zoeken" "Apps laden…" "Er zijn geen apps gevonden die overeenkomen met \'%1$s\'" "Zoeken naar meer apps" @@ -84,15 +84,13 @@ "Instellingen wijzigen" "Pictogram toevoegen aan startscherm" "Voor nieuwe apps" - - + "Vorm van pictogram wijzigen" "Systeemstandaard gebruiken" "Vierkant" "Squircle" "Cirkel" "Traan" - - + "Wijzigingen in vorm van pictogram toepassen" "Onbekend" "Verwijderen" "Zoeken" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index 72ba3d109e..e4cff7b8c7 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -35,12 +35,9 @@ "%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ" "ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ" "ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਸ਼ਾਮਲ ਕਰੋ" - - - - - - + "ਐਪਾਂ ਖੋਜੋ" + "ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." + "\"%1$s\" ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਕੋਈ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ" "ਹੋਰ ਐਪਾਂ ਖੋਜੋ" "ਸੂਚਨਾਵਾਂ" "ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 5756a18c09..c07f7defb3 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -84,15 +84,13 @@ "Zmień ustawienia" "Dodaj ikonę do ekranu głównego" "W przypadku nowych aplikacji" - - + "Zmień kształt ikon" "Użyj ustawienia domyślnego" "Kwadrat" "Zaokrąglony kwadrat" "Okrąg" "Łza" - - + "Zmieniam kształt ikon" "Brak informacji" "Usuń" "Szukaj" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index a6ccf2eed6..d75159f94a 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -84,15 +84,13 @@ "Alterar configurações" "Adicionar ícone à tela inicial" "Para novos apps" - - + "Alterar forma de ícones" "Usar padrão do sistema" "Quadrado" "Quadrado arredondado" "Círculo" "Lágrima" - - + "Aplicando alterações na forma dos ícones" "Desconhecido" "Remover" "Pesquisar" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 0a816f31ee..512d96d6b8 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -84,15 +84,13 @@ "Modificați setările" "Adaugă pictograme în ecranul de pornire" "Pentru aplicații noi" - - + "Schimbați forma pictogramei" "Folosiți setarea prestabilită a sistemului" "Pătrat" "Pătrat cu colțuri rotunjite" "Cerc" "Lacrimă" - - + "Se aplică modificările aduse formei pictogramei" "Necunoscut" "Eliminați" "Căutați" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 4bbbc46a17..aed8f18e00 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -35,12 +35,9 @@ "Ширина %1$d, высота %2$d" "Нажмите и удерживайте, чтобы добавить вручную" "Добавить автоматически" - - - - - - + "Поиск приложений" + "Загрузка приложений…" + "По запросу \"%1$s\" ничего не найдено" "Искать другие приложения" "Уведомления" "На этом экране все занято" @@ -87,15 +84,13 @@ "Изменить настройки" "Добавлять значки" "Добавлять значки установленных приложений на главный экран." - - + "Изменить форму значков" "Использовать системные настройки по умолчанию" "Квадрат" "Квадрат с закругленными краями" "Круг" "Капля" - - + "Применение изменений..." "Неизвестно" "Удалить" "Найти" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index fb1f10e5d5..f6c42b2d9a 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -84,15 +84,13 @@ "සැකසීම් වෙනස් කරන්න" "මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න" "නව යෙදුම් සඳහා" - - + "නිරූපක හැඩය වෙනස් කරන්න" "පද්ධති පෙරනිමි භාවිත කරන්න" "සමචතුරස්‍රය" "හතරැස් කවය" "කවය" "කඳුළු බිංදුව" - - + "නිරූපක හැඩය වෙනස් කිරීම් යොදමින්" "නොදනී" "ඉවත් කරන්න" "සොයන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 5ed92006a8..490f823fe2 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -84,15 +84,13 @@ "Zmeniť nastavenia" "Pridať ikonu na plochu" "Pri inštalácii novej aplikácie" - - + "Zmeniť tvar ikony" "Použiť predvolené nastavenie systému" "Štvorec" "Okrúhly štvorec" "Kruh" "Slza" - - + "Tvar ikony sa mení" "Neznáme" "Odstrániť" "Vyhľadať" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 0f5d84c473..0b7d36ef8b 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -35,12 +35,9 @@ "Širina %1$d, višina %2$d" "Dotaknite se elementa in ga pridržite, da ga ročno dodate" "Samodejno dodaj" - - - - - - + "Iskanje programov" + "Nalaganje aplikacij …" + "Ni aplikacij, ki bi ustrezale poizvedbi »%1$s«" "Iskanje več aplikacij" "Obvestila" "Na tem začetnem zaslonu ni več prostora." @@ -87,15 +84,13 @@ "Spremeni nastavitve" "Dodaj ikono na začetni zaslon" "Za nove aplikacije" - - + "Spremeni obliko ikon" "Uporabi privzeto nastavitev sistema" "Kvadrat" "Zaobljen kvadrat" "Krog" "Solza" - - + "Uveljavljanje spremenjene oblike ikon" "Neznano" "Odstrani" "Iskanje" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index b5f573bff9..3e6afee609 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -35,12 +35,9 @@ "%1$d i gjerë me %2$d i lartë" "Prek dhe mbaj të shtypur për të vendosur në mënyrë manuale" "Shto automatikisht" - - - - - - + "Kërko për aplikacione" + "Po ngarkon aplikacionet..." + "Nuk u gjet asnjë aplikacion që përputhet me \"%1$s\"" "Kërko për më shumë aplikacione" "Njoftimet" "Nuk ka më hapësirë në këtë ekran bazë." @@ -87,15 +84,13 @@ "Ndrysho cilësimet" "Shto ikonë në ekranin bazë" "Për aplikacionet e reja" - - + "Ndrysho formën e ikonës" "Përdor parazgjedhjen e sisteit" "Katror" "Katror me kënde të rrumbullakëta" "Rreth" "Pikë loti" - - + "Po zbatohen ndryshimet e formës së ikonës" "I panjohur" "Hiq" "Kërko" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index d041b8ecf2..f41e02d33f 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -84,15 +84,13 @@ "Промените подешавања" "Додај икону на почетни екран" "За нове апликације" - - + "Промените облик икона" "Користи подразумевано системско подешавање" "Квадрат" "Заобљени квадрат" "Круг" "Суза" - - + "Примењују се промене облика икона" "Непознато" "Уклони" "Претражи" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index ea42245c4a..6c598ff5dc 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -84,15 +84,13 @@ "Ändra inställningar" "Lägg till ikonen på startskärmen" "För nya appar" - - + "Ändra form på ikoner" "Använd systemstandard" "Kvadrat" "Kvirkel" "Cirkel" "Droppe" - - + "Ikonernas form ändras" "Okänt" "Ta bort" "Sök" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 613ba0dde5..3de28e011d 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -86,15 +86,13 @@ "Badilisha mipangilio" "Ongeza aikoni kwenye Skrini ya kwanza" "Kwa ajili ya programu mpya" - - + "Badilisha umbo la aikoni" "Tumia umbo chaguo-msingi la mfumo" "Mraba" "Mstatili wenye pembe duara" "Mduara" "Umbo la chozi" - - + "Inabadilisha umbo la aikoni" "Yasiyojulikana" "Ondoa" "Tafuta" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 3ddf2bcc04..1d92581a05 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -84,15 +84,13 @@ "அமைப்புகளை மாற்று" "முகப்புத் திரையில் ஐகானைச் சேர்" "புதிய பயன்பாடுகளுக்கு" - - + "ஐகான் வடிவத்தை மாற்று" "அமைப்பின் இயல்புநிலையைப் பயன்படுத்து" "சதுரம்" "சதுரவட்டம்" "வட்டம்" "கண்ணீர்துளி" - - + "ஐகான் வடிவத்தை மாற்றுகிறது" "தெரியாதது" "அகற்று" "தேடு" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 064b71f878..7fa666c677 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -35,12 +35,9 @@ "%1$d వెడల్పు X %2$d ఎత్తు" "మాన్యువల్‌గా ఉంచడానికి నొక్కి &amp పట్టుకోండి" "స్వయంచాలకంగా జోడించు" - - - - - - + "అప్లికేషన్‌లను శోధించండి" + "అప్లికేషన్‌లను లోడ్ చేస్తోంది…" + "\"%1$s\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు" "మరిన్ని అనువర్తనాల కోసం శోధించు" "నోటిఫికేషన్‌లు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 6be2b361b0..b370099889 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -84,15 +84,13 @@ "เปลี่ยนการตั้งค่า" "เพิ่มไอคอนในหน้าจอหลัก" "สำหรับแอปใหม่" - - + "เปลี่ยนรูปร่างไอคอน" "ใช้ค่าเริ่มต้นของระบบ" "สี่เหลี่ยมจัตุรัส" "สี่เหลี่ยมขอบมน" "วงกลม" "หยดน้ำตา" - - + "กำลังนำการเปลี่ยนรูปร่างไอคอนไปใช้" "ไม่รู้จัก" "ลบ" "ค้นหา" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index a91f31e80d..54135c9d58 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -84,15 +84,13 @@ "Baguhin ang mga setting" "Idagdag ang icon sa Home screen" "Para sa mga bagong app" - - + "Baguhin ang hugis ng icon" "Gamitin ang default ng system" "Parisukat" "Squircle" "Bilog" "Teardrop" - - + "Inilalapat ang mga pagbabago sa hugis ng icon" "Hindi kilala" "Alisin" "Maghanap" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index d6543bb8c4..cb8b50af20 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -84,15 +84,13 @@ "Ayarları değiştir" "Ana ekrana simge ekle" "Yeni uygulamalar için" - - + "Simge şeklini değiştir" "Sistem varsayılanını kullan" "Kare" "Kare-daire" "Daire" "Gözyaşı damlası" - - + "Simge şekli değişiklikleri uygulanıyor" "Bilinmiyor" "Kaldır" "Ara" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index ea58334ee6..a9e010921f 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -84,15 +84,13 @@ "Змінити налаштування" "Додати значок на головний екран" "Для нових додатків" - - + "Змінити форму значка" "Використовувати налаштування системи за умовчанням" "Квадрат" "Квадрат із заокругленими кутами" "Круг" "Сльоза" - - + "Змінюється форма значка" "Невідомо" "Видалити" "Шукати" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 10cfcd3e08..a704fab4ca 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -35,12 +35,9 @@ "‏%1$d چوڑا اور ‎%2$d اونچا" "‏دستی طور پر رکھنے کیلئے ‎& ٹچ کرکے ہولڈ کریں" "خود کار طور پر شامل کریں" - - - - - - + "ایپس تلاش کریں" + "ایپس لوڈ کی جا رہی ہیں…" + "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" "مزید ایپس تلاش کریں" "اطلاعات" "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 279183622f..7e31889138 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -84,15 +84,13 @@ "Sozlamalarni o‘zgartirish" "Bosh ekranga ikonka qo‘shish" "Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish" - - + "Ikonka shaklini o‘zgartirish" "Standart tizim parametrlaridan foydalanish" "Kvadrat" "Qirralari aylana kvadrat" "Aylana" "Tomchi" - - + "Ikonka shakli o‘zgartirilmoqda" "Noma’lum" "O‘chirish" "Qidirish" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 3267b322f6..83e1ceabaa 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -84,15 +84,13 @@ "Thay đổi cài đặt" "Thêm biểu tượng vào màn hình chính" "Cho ứng dụng mới" - - + "Thay đổi hình dạng biểu tượng" "Sử dụng mặc định của hệ thống" "Hình vuông" "Hình vuông cạnh bo tròn" "Hình tròn" "Hình giọt nước" - - + "Đang áp dụng các thay đổi hình dạng biểu tượng" "Không xác định" "Xóa" "Tìm kiếm" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 099acb059e..bef12fb61b 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -84,15 +84,13 @@ "更改设置" "将图标添加到主屏幕" "适用于新应用" - - + "更改图标形状" "使用系统默认设置" "方形" "方圆形" "圆形" "泪珠形" - - + "正在应用图标形状更改" "未知" "移除" "搜索" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 941e6f35f0..7ac5553957 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -84,15 +84,13 @@ "變更設定" "將圖示加到主畫面" "適用於新安裝的應用程式" - - + "變更圖示形狀" "使用系統預設設定" "正方形" "方圓形" "圓形" "淚珠形" - - + "正在套用圖示形狀變更" "不明" "移除" "搜尋" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 3f1759ed65..e0c4c9973e 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -84,15 +84,13 @@ "變更設定" "將圖示加到主螢幕" "適用於新安裝的應用程式" - - + "變更圖示形狀" "使用系統預設值" "正方形" "方圓形" "圓形" "淚珠形" - - + "正在套用圖示形狀變更" "不明" "移除" "搜尋" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index df198be273..ef6fdeb10a 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -84,15 +84,13 @@ "Shintsha izilungiselelo" "Engeza isithonjana eskrinini sasekhaya" "Kwezinhlelo zokusebenza ezintsha" - - + "Shintsha isimo sesithonjana" "Sebenzisa okuzenzakalelayo kwesistimu" "Isikwele" "I-Squircle" "Indingiliza" "I-Teardrop" - - + "Ifaka izinguquko zesimo sesithonjana" "Akwaziwa" "Susa" "Sesha" From 5fd0aed6c4608765c40ec7f3043d2a3d49e61d3b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 1 Aug 2017 15:24:23 -0700 Subject: [PATCH 008/885] Import translations. DO NOT MERGE Change-Id: Ia09f002c4374578b83fd1c3648101f68558b4f27 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- go/res/values-be/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-bg/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-bn/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ca/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-cs/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-de/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-el/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-es-rUS/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-es/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-et/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-eu/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-fi/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-fr-rCA/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-fr/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-gl/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-gu/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-hi/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-hu/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-hy/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-is/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-iw/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-kk/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-km/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-kn/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ko/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-lv/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-mk/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ml/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-mn/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-mr/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ms/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-my/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-nb/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ne/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-pa/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ro/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ru/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-sq/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ta/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-te/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-th/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-tl/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-ur/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-vi/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-zh-rCN/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-zh-rHK/strings.xml | 26 ++++++++++++++++++++++++++ go/res/values-zh-rTW/strings.xml | 26 ++++++++++++++++++++++++++ res/values-nb/strings.xml | 4 ++-- res/values-ru/strings.xml | 6 +++--- 49 files changed, 1227 insertions(+), 5 deletions(-) create mode 100644 go/res/values-be/strings.xml create mode 100644 go/res/values-bg/strings.xml create mode 100644 go/res/values-bn/strings.xml create mode 100644 go/res/values-ca/strings.xml create mode 100644 go/res/values-cs/strings.xml create mode 100644 go/res/values-de/strings.xml create mode 100644 go/res/values-el/strings.xml create mode 100644 go/res/values-es-rUS/strings.xml create mode 100644 go/res/values-es/strings.xml create mode 100644 go/res/values-et/strings.xml create mode 100644 go/res/values-eu/strings.xml create mode 100644 go/res/values-fi/strings.xml create mode 100644 go/res/values-fr-rCA/strings.xml create mode 100644 go/res/values-fr/strings.xml create mode 100644 go/res/values-gl/strings.xml create mode 100644 go/res/values-gu/strings.xml create mode 100644 go/res/values-hi/strings.xml create mode 100644 go/res/values-hu/strings.xml create mode 100644 go/res/values-hy/strings.xml create mode 100644 go/res/values-is/strings.xml create mode 100644 go/res/values-iw/strings.xml create mode 100644 go/res/values-kk/strings.xml create mode 100644 go/res/values-km/strings.xml create mode 100644 go/res/values-kn/strings.xml create mode 100644 go/res/values-ko/strings.xml create mode 100644 go/res/values-lv/strings.xml create mode 100644 go/res/values-mk/strings.xml create mode 100644 go/res/values-ml/strings.xml create mode 100644 go/res/values-mn/strings.xml create mode 100644 go/res/values-mr/strings.xml create mode 100644 go/res/values-ms/strings.xml create mode 100644 go/res/values-my/strings.xml create mode 100644 go/res/values-nb/strings.xml create mode 100644 go/res/values-ne/strings.xml create mode 100644 go/res/values-pa/strings.xml create mode 100644 go/res/values-ro/strings.xml create mode 100644 go/res/values-ru/strings.xml create mode 100644 go/res/values-sq/strings.xml create mode 100644 go/res/values-ta/strings.xml create mode 100644 go/res/values-te/strings.xml create mode 100644 go/res/values-th/strings.xml create mode 100644 go/res/values-tl/strings.xml create mode 100644 go/res/values-ur/strings.xml create mode 100644 go/res/values-vi/strings.xml create mode 100644 go/res/values-zh-rCN/strings.xml create mode 100644 go/res/values-zh-rHK/strings.xml create mode 100644 go/res/values-zh-rTW/strings.xml diff --git a/go/res/values-be/strings.xml b/go/res/values-be/strings.xml new file mode 100644 index 0000000000..4189e35fa0 --- /dev/null +++ b/go/res/values-be/strings.xml @@ -0,0 +1,26 @@ + + + + + "Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго." + "Дакраніцеся двойчы і ўтрымлівайце, каб выбраць ярлык або выкарыстоўваць спецыяльныя дзеянні." + "Ярлыкі" + "Ярлыкі %1$s" + diff --git a/go/res/values-bg/strings.xml b/go/res/values-bg/strings.xml new file mode 100644 index 0000000000..1b85992d99 --- /dev/null +++ b/go/res/values-bg/strings.xml @@ -0,0 +1,26 @@ + + + + + "Докоснете и задръжте за избор на пряк път." + "Докоснете двукратно и задръжте за избор на пряк път или използвайте персонализирани действия." + "Преки пътища" + "Преки пътища за %1$s" + diff --git a/go/res/values-bn/strings.xml b/go/res/values-bn/strings.xml new file mode 100644 index 0000000000..c56c925a29 --- /dev/null +++ b/go/res/values-bn/strings.xml @@ -0,0 +1,26 @@ + + + + + "কোনও শর্টকাট বেছে নিতে টাচ করে ধরে রাখুন।" + "কোনও শর্টকাট বেছে নিতে ডাবল ট্যাপ করে ধরে রাখুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন।" + "শর্টকাট" + "%1$s এর শর্টকাট" + diff --git a/go/res/values-ca/strings.xml b/go/res/values-ca/strings.xml new file mode 100644 index 0000000000..3b5c3f7b56 --- /dev/null +++ b/go/res/values-ca/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén premuda una drecera per seleccionar-la." + "Fes doble toc i mantén premut per seleccionar una drecera o per utilitzar accions personalitzades." + "Dreceres" + "Dreceres de l\'aplicació %1$s" + diff --git a/go/res/values-cs/strings.xml b/go/res/values-cs/strings.xml new file mode 100644 index 0000000000..e4018f2c0f --- /dev/null +++ b/go/res/values-cs/strings.xml @@ -0,0 +1,26 @@ + + + + + "Zkratku vyberete podržením." + "Dvojitým klepnutím a podržením vyberte zkratku, případně použijte vlastní akce." + "Zkratky" + "Zkratky aplikace %1$s" + diff --git a/go/res/values-de/strings.xml b/go/res/values-de/strings.xml new file mode 100644 index 0000000000..43a1b3a9e1 --- /dev/null +++ b/go/res/values-de/strings.xml @@ -0,0 +1,26 @@ + + + + + "Doppeltippen und halten, um eine Verknüpfung auszuwählen." + "Doppeltippen und halten, um eine Verknüpfung auszuwählen oder benutzerdefinierte Aktionen zu nutzen." + "Verknüpfungen" + "%1$s-Verknüpfungen" + diff --git a/go/res/values-el/strings.xml b/go/res/values-el/strings.xml new file mode 100644 index 0000000000..ae59907d16 --- /dev/null +++ b/go/res/values-el/strings.xml @@ -0,0 +1,26 @@ + + + + + "Αγγίξτε παρατεταμένα για να σηκώσετε μια συντόμευση." + "Πατήσετε δύο φορές παρατεταμένα για να σηκώσετε μια συντόμευση ή για να χρησιμοποιήσετε προσαρμοσμένες ενέργειες." + "Συντομεύσεις" + "Συντομεύσεις %1$s" + diff --git a/go/res/values-es-rUS/strings.xml b/go/res/values-es-rUS/strings.xml new file mode 100644 index 0000000000..5212c035d3 --- /dev/null +++ b/go/res/values-es-rUS/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén presionado para elegir un acceso directo." + "Presiona dos veces y mantén presionado para elegir un acceso directo o usar acciones personalizadas." + "Accesos directos" + "Accesos directos de %1$s" + diff --git a/go/res/values-es/strings.xml b/go/res/values-es/strings.xml new file mode 100644 index 0000000000..3ae45889cf --- /dev/null +++ b/go/res/values-es/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén pulsado el acceso directo que quieras." + "Toca dos veces y mantén pulsado el acceso directo o utiliza acciones personalizadas." + "Accesos directos" + "Accesos directos de %1$s" + diff --git a/go/res/values-et/strings.xml b/go/res/values-et/strings.xml new file mode 100644 index 0000000000..2513e65a33 --- /dev/null +++ b/go/res/values-et/strings.xml @@ -0,0 +1,26 @@ + + + + + "Otsetee valimiseks puudutage seda pikalt." + "Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks." + "Otseteed" + "Rakenduse %1$s otseteed" + diff --git a/go/res/values-eu/strings.xml b/go/res/values-eu/strings.xml new file mode 100644 index 0000000000..9949ef091d --- /dev/null +++ b/go/res/values-eu/strings.xml @@ -0,0 +1,26 @@ + + + + + "Eduki sakatuta lasterbide bat aukeratzeko." + "Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko." + "Lasterbideak" + "%1$s aplikazioaren lasterbidea" + diff --git a/go/res/values-fi/strings.xml b/go/res/values-fi/strings.xml new file mode 100644 index 0000000000..da9b0e1c42 --- /dev/null +++ b/go/res/values-fi/strings.xml @@ -0,0 +1,26 @@ + + + + + "Valitse pikakuvake painamalla sitä pitkään." + "Valitse pikakuvake tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla pitkään." + "Pikakuvakkeet" + "Kohteen %1$s pikakuvakkeet" + diff --git a/go/res/values-fr-rCA/strings.xml b/go/res/values-fr-rCA/strings.xml new file mode 100644 index 0000000000..c7fd6d6423 --- /dev/null +++ b/go/res/values-fr-rCA/strings.xml @@ -0,0 +1,26 @@ + + + + + "Maintenez un doigt sur le raccourci pour l\'ajouter" + "Touchez 2x un raccourci et maintenez doigt dessus pour l’aj. ou utiliser des actions personnalisées." + "Raccourcis" + "Raccourcis : %1$s" + diff --git a/go/res/values-fr/strings.xml b/go/res/values-fr/strings.xml new file mode 100644 index 0000000000..238fe73d75 --- /dev/null +++ b/go/res/values-fr/strings.xml @@ -0,0 +1,26 @@ + + + + + "Appui prolongé pour sélectionner raccourci." + "Appuyez 2 fois et maintenez pression pour sélectionner raccourci ou utilisez actions personnalisées." + "Raccourcis" + "Raccourcis %1$s" + diff --git a/go/res/values-gl/strings.xml b/go/res/values-gl/strings.xml new file mode 100644 index 0000000000..31621d5c2c --- /dev/null +++ b/go/res/values-gl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Mantén premido un atallo para seleccionalo." + "Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas." + "Atallos" + "Atallos da aplicación %1$s" + diff --git a/go/res/values-gu/strings.xml b/go/res/values-gu/strings.xml new file mode 100644 index 0000000000..bdb549ff7f --- /dev/null +++ b/go/res/values-gu/strings.xml @@ -0,0 +1,26 @@ + + + + + "એક શૉર્ટકટ ચૂંટવા ટૅપ કરી રાખો." + "એક શૉર્ટકટ ચૂંટવા અથવા કોઈ કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટૅપ કરી રાખો." + "શૉર્ટકટ" + "%1$s શૉર્ટકટ" + diff --git a/go/res/values-hi/strings.xml b/go/res/values-hi/strings.xml new file mode 100644 index 0000000000..bc057760fb --- /dev/null +++ b/go/res/values-hi/strings.xml @@ -0,0 +1,26 @@ + + + + + "शॉर्टकट चुनने के लिए छूकर रखें." + "शॉर्टकट चुनने के लिए डबल टैप करके रखें या कस्टम कार्रवाइयों का उपयोग करें." + "शॉर्टकट" + "%1$s शॉर्टकट" + diff --git a/go/res/values-hu/strings.xml b/go/res/values-hu/strings.xml new file mode 100644 index 0000000000..369c22745a --- /dev/null +++ b/go/res/values-hu/strings.xml @@ -0,0 +1,26 @@ + + + + + "Felvételhez tartsa nyomva a parancsikont." + "Parancsikon felvételéhez koppintson rá duplán és tartsa nyomva, vagy használjon egyéni műveleteket." + "Parancsikonok" + "%1$s-parancsikonok" + diff --git a/go/res/values-hy/strings.xml b/go/res/values-hy/strings.xml new file mode 100644 index 0000000000..4747f6df35 --- /dev/null +++ b/go/res/values-hy/strings.xml @@ -0,0 +1,26 @@ + + + + + "Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար։" + "Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար կամ օգտվեք հարմարեցրած գործողություններից:" + "Դյուրանցումներ" + "%1$s դյուրանցումներ" + diff --git a/go/res/values-is/strings.xml b/go/res/values-is/strings.xml new file mode 100644 index 0000000000..b8bb923746 --- /dev/null +++ b/go/res/values-is/strings.xml @@ -0,0 +1,26 @@ + + + + + "Haltu fingri á flýtileið til að grípa hana." + "Ýttu tvisvar og haltu fingri á flýtileið til að grípa hana eða notaðu sérsniðnar aðgerðir." + "Flýtileiðir" + "%1$s flýtileiðir" + diff --git a/go/res/values-iw/strings.xml b/go/res/values-iw/strings.xml new file mode 100644 index 0000000000..f541d4d316 --- /dev/null +++ b/go/res/values-iw/strings.xml @@ -0,0 +1,26 @@ + + + + + "כדי להוסיף קיצור דרך, מקישים עליו פעמיים ומחזיקים." + "כדי להוסיף קיצור דרך או להשתמש בפעולות מותאמות אישית, מקישים על קיצור הדרך פעמיים ומחזיקים." + "קיצורי דרך" + "קיצורי דרך לאפליקציה %1$s" + diff --git a/go/res/values-kk/strings.xml b/go/res/values-kk/strings.xml new file mode 100644 index 0000000000..e909818afc --- /dev/null +++ b/go/res/values-kk/strings.xml @@ -0,0 +1,26 @@ + + + + + "Таңбашаны таңдау үшін оны түртіп, ұстап тұрыңыз." + "Таңбашаны таңдау немесе арнаулы әрекеттерді пайдалану үшін екі рет түртіп, ұстап тұрыңыз." + "Таңбашалар" + "%1$s таңбаша" + diff --git a/go/res/values-km/strings.xml b/go/res/values-km/strings.xml new file mode 100644 index 0000000000..40082a4b9d --- /dev/null +++ b/go/res/values-km/strings.xml @@ -0,0 +1,26 @@ + + + + + "ប៉ះ ហើយចុចឲ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ។" + "ប៉ះពីរដង ហើយចុចឱ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។" + "ផ្លូវកាត់" + "ផ្លូវកាត់សម្រាប់ %1$s" + diff --git a/go/res/values-kn/strings.xml b/go/res/values-kn/strings.xml new file mode 100644 index 0000000000..9c121fd294 --- /dev/null +++ b/go/res/values-kn/strings.xml @@ -0,0 +1,26 @@ + + + + + "ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ." + "ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ." + "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು" + "%1$s ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು" + diff --git a/go/res/values-ko/strings.xml b/go/res/values-ko/strings.xml new file mode 100644 index 0000000000..60f925e36b --- /dev/null +++ b/go/res/values-ko/strings.xml @@ -0,0 +1,26 @@ + + + + + "바로가기를 선택하려면 길게 터치하세요." + "바로가기를 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다." + "바로가기" + "%1$s 바로가기" + diff --git a/go/res/values-lv/strings.xml b/go/res/values-lv/strings.xml new file mode 100644 index 0000000000..04315ebaa0 --- /dev/null +++ b/go/res/values-lv/strings.xml @@ -0,0 +1,26 @@ + + + + + "Lai izvēlētos saīsni, pieskarieties un turiet to." + "Lai atlasītu saīsni, veiciet dubultskārienu uz tās un turiet to vai arī veiciet pielāgotas darbības." + "Saīsnes" + "Lietotnes %1$s saīsnes" + diff --git a/go/res/values-mk/strings.xml b/go/res/values-mk/strings.xml new file mode 100644 index 0000000000..52d66b5e28 --- /dev/null +++ b/go/res/values-mk/strings.xml @@ -0,0 +1,26 @@ + + + + + "Допрете двапати и задржете за да изберете кратенка." + "Допрете двапати и задржете за да изберете кратенка или да користите приспособени дејства." + "Кратенки" + "Кратенки за %1$s" + diff --git a/go/res/values-ml/strings.xml b/go/res/values-ml/strings.xml new file mode 100644 index 0000000000..b3c12e16e1 --- /dev/null +++ b/go/res/values-ml/strings.xml @@ -0,0 +1,26 @@ + + + + + "ഒരു കുറുക്കുവഴി ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക." + "ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക." + "കുറുക്കുവഴികൾ" + "%1$s കുറുക്കുവഴികൾ" + diff --git a/go/res/values-mn/strings.xml b/go/res/values-mn/strings.xml new file mode 100644 index 0000000000..c89dfd1bdc --- /dev/null +++ b/go/res/values-mn/strings.xml @@ -0,0 +1,26 @@ + + + + + "Товчлол авах бол удаан дарна уу." + "Товчлол авах болон тохируулсан үйлдлийг ашиглахын тулд хоёр товшоод хүлээнэ үү." + "Товчлол" + "%1$s-н товчлол" + diff --git a/go/res/values-mr/strings.xml b/go/res/values-mr/strings.xml new file mode 100644 index 0000000000..2c767b4f19 --- /dev/null +++ b/go/res/values-mr/strings.xml @@ -0,0 +1,26 @@ + + + + + "शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." + "शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा." + "शॉर्टकट" + "%1$s शॉर्टकट" + diff --git a/go/res/values-ms/strings.xml b/go/res/values-ms/strings.xml new file mode 100644 index 0000000000..42add9a197 --- /dev/null +++ b/go/res/values-ms/strings.xml @@ -0,0 +1,26 @@ + + + + + "Sentuh & tahan untuk mengambil pintasan." + "Ketik dua kali & tahan untuk mengambil pintasan atau menggunakan tindakan tersuai." + "Pintasan" + "Pintasan %1$s" + diff --git a/go/res/values-my/strings.xml b/go/res/values-my/strings.xml new file mode 100644 index 0000000000..5784df63bf --- /dev/null +++ b/go/res/values-my/strings.xml @@ -0,0 +1,26 @@ + + + + + "လက်ကွက်ဖြတ်လမ်းတစ်ခုကို ရွေးရန် ထိပြီး ဖိထားပါ" + "လက်ကွက်ဖြတ်လမ်းကို ရွေးရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန်နှစ်ချက်တို့ပြီး ဖိထားပါ။" + "ဖြတ်လမ်းလင့်ခ်များ" + "%1$s ဖြတ်လမ်းလင့်ခ်များ" + diff --git a/go/res/values-nb/strings.xml b/go/res/values-nb/strings.xml new file mode 100644 index 0000000000..2a5ffb6e7c --- /dev/null +++ b/go/res/values-nb/strings.xml @@ -0,0 +1,26 @@ + + + + + "Trykk og hold for å velge en snarvei." + "Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger." + "Snarveier" + "%1$s-snarveier" + diff --git a/go/res/values-ne/strings.xml b/go/res/values-ne/strings.xml new file mode 100644 index 0000000000..0be0375f3a --- /dev/null +++ b/go/res/values-ne/strings.xml @@ -0,0 +1,26 @@ + + + + + "कुनै सटकर्ट छनौट गर्न छोइराख्नुहोस्।" + "कुनै सर्टकट छनौट गर्न दुईपटक ट्याप गरेर होल्ड गर्नुहोस् वा रोजेका कारबाहीहरू प्रयोग गर्नुहोस्।" + "सर्टकटहरू" + "%1$s सर्टकटहरू" + diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml new file mode 100644 index 0000000000..fdb3c74152 --- /dev/null +++ b/go/res/values-pa/strings.xml @@ -0,0 +1,26 @@ + + + + + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤਣ ਲਈ ਡਬਲ-ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" + "ਸ਼ਾਰਟਕੱਟ" + "%1$s ਸ਼ਾਰਟਕੱਟ" + diff --git a/go/res/values-ro/strings.xml b/go/res/values-ro/strings.xml new file mode 100644 index 0000000000..75d1796b57 --- /dev/null +++ b/go/res/values-ro/strings.xml @@ -0,0 +1,26 @@ + + + + + "Atingeți și țineți apăsat pentru a selecta o comandă rapidă." + "Atingeți de două ori și țineți apăsat pentru comandă rapidă sau folosiți acțiuni personalizate." + "Comenzi rapide" + "Comenzi rapide pentru %1$s" + diff --git a/go/res/values-ru/strings.xml b/go/res/values-ru/strings.xml new file mode 100644 index 0000000000..9c5c8cd340 --- /dev/null +++ b/go/res/values-ru/strings.xml @@ -0,0 +1,26 @@ + + + + + "Чтобы выбрать ярлык, нажмите на него и удерживайте." + "Чтобы выбрать ярлык или использовать специальные действия, нажмите на него дважды и не отпускайте." + "Ярлыки" + "%1$s: ярлыки" + diff --git a/go/res/values-sq/strings.xml b/go/res/values-sq/strings.xml new file mode 100644 index 0000000000..bb74db6b55 --- /dev/null +++ b/go/res/values-sq/strings.xml @@ -0,0 +1,26 @@ + + + + + "Prek dhe mbaj prekur për të zgjedhur një shkurtore." + "Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara." + "Shkurtoret" + "%1$s shkurtore" + diff --git a/go/res/values-ta/strings.xml b/go/res/values-ta/strings.xml new file mode 100644 index 0000000000..50059b65f9 --- /dev/null +++ b/go/res/values-ta/strings.xml @@ -0,0 +1,26 @@ + + + + + "குறுக்குவழியைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்." + "குறுக்குவழியை சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்." + "குறுக்குவழிகள்" + "%1$s குறுக்குவழிகள்" + diff --git a/go/res/values-te/strings.xml b/go/res/values-te/strings.xml new file mode 100644 index 0000000000..0bdf743342 --- /dev/null +++ b/go/res/values-te/strings.xml @@ -0,0 +1,26 @@ + + + + + "సత్వరమార్గాన్ని ఎంచుకోవడానికి తాకి & నొక్కి ఉంచండి." + "సత్వరమార్గాన్ని ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి &ఉంచండి." + "సత్వరమార్గాలు" + "%1$s సత్వరమార్గాలు" + diff --git a/go/res/values-th/strings.xml b/go/res/values-th/strings.xml new file mode 100644 index 0000000000..e73d89fb0e --- /dev/null +++ b/go/res/values-th/strings.xml @@ -0,0 +1,26 @@ + + + + + "แตะค้างไว้เพื่อเลือกทางลัด" + "แตะสองครั้งค้างไว้เพื่อเลือกทางลัดหรือใช้การกระทำที่กำหนดเอง" + "ทางลัด" + "ทางลัด %1$s" + diff --git a/go/res/values-tl/strings.xml b/go/res/values-tl/strings.xml new file mode 100644 index 0000000000..8f44ec5742 --- /dev/null +++ b/go/res/values-tl/strings.xml @@ -0,0 +1,26 @@ + + + + + "Pindutin nang matagal upang kumuha ng shortcut." + "I-double tap nang matagal upang kumuha ng shortcut o gumamit ng mga custom na pagkilos." + "Mga Shortcut" + "Mga shortcut sa %1$s" + diff --git a/go/res/values-ur/strings.xml b/go/res/values-ur/strings.xml new file mode 100644 index 0000000000..46bd823d8e --- /dev/null +++ b/go/res/values-ur/strings.xml @@ -0,0 +1,26 @@ + + + + + "کوئی شارٹ کٹ منتخب کرنے کیلئے ٹچ کریں اور دبائے رکھیں۔" + "کوئی شارٹ کٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور دبائے رکھیں۔" + "شارٹ کٹس" + "%1$s شارٹ کٹس" + diff --git a/go/res/values-vi/strings.xml b/go/res/values-vi/strings.xml new file mode 100644 index 0000000000..84ed627964 --- /dev/null +++ b/go/res/values-vi/strings.xml @@ -0,0 +1,26 @@ + + + + + "Chạm và giữ để chọn lối tắt." + "Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng tác vụ tùy chỉnh." + "Lối tắt" + "Lối tắt %1$s" + diff --git a/go/res/values-zh-rCN/strings.xml b/go/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..57351d37f5 --- /dev/null +++ b/go/res/values-zh-rCN/strings.xml @@ -0,0 +1,26 @@ + + + + + "触摸并按住快捷方式即可选择快捷方式。" + "点按两次并按住快捷方式即可选择快捷方式,您也可以使用自定义操作。" + "快捷方式" + "%1$s快捷方式" + diff --git a/go/res/values-zh-rHK/strings.xml b/go/res/values-zh-rHK/strings.xml new file mode 100644 index 0000000000..dea7749f68 --- /dev/null +++ b/go/res/values-zh-rHK/strings.xml @@ -0,0 +1,26 @@ + + + + + "按住捷徑即可選取捷徑。" + "扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。" + "捷徑" + "%1$s 捷徑" + diff --git a/go/res/values-zh-rTW/strings.xml b/go/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..07ae2ed5b4 --- /dev/null +++ b/go/res/values-zh-rTW/strings.xml @@ -0,0 +1,26 @@ + + + + + "按住捷徑即可選取。" + "輕觸兩下並按住捷徑即可選取,你也可以使用自訂動作。" + "捷徑" + "「%1$s」捷徑" + diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index bd348b0aab..65247c168d 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -84,13 +84,13 @@ "Endre innstillingene" "Legg til ikon på startsiden" "For nye apper" - "Endre ikonets form" + "Endre formen på ikonet" "Bruk systemstandard" "Kvadrat" "Superellipse" "Sirkel" "Dråpe" - "Aktiverer endringer av ikonets form" + "Aktiverer endringer av formen på ikonet" "Ukjent" "Fjern" "Søk" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index aed8f18e00..923f3573b3 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -28,9 +28,9 @@ "Виджеты отключены в безопасном режиме" "Ярлык недоступен" "Главный экран" - "Пользовательские действия" + "Специальные действия" "Чтобы выбрать виджет, нажмите на значок и удерживайте его." - "Чтобы выбрать виджет, нажмите на него дважды и не отпускайте или выполните предложенные действия." + "Чтобы выбрать виджет или использовать специальные действия, нажмите на него дважды и не отпускайте." "%1$d x %2$d" "Ширина %1$d, высота %2$d" "Нажмите и удерживайте, чтобы добавить вручную" @@ -90,7 +90,7 @@ "Квадрат с закругленными краями" "Круг" "Капля" - "Применение изменений..." + "Применение изменений…" "Неизвестно" "Удалить" "Найти" From 5a492ea801178f71e8e4234b68a6976536816d9e Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 9 Aug 2017 04:25:44 -0700 Subject: [PATCH 009/885] Import translations. DO NOT MERGE Change-Id: Ieb766acaf2da7f21bf8b9fded3313bcc1ab59c31 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- go/res/values-pa/strings.xml | 2 +- go/res/values-vi/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml index fdb3c74152..f3982ab71b 100644 --- a/go/res/values-pa/strings.xml +++ b/go/res/values-pa/strings.xml @@ -20,7 +20,7 @@ "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" - "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤਣ ਲਈ ਡਬਲ-ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਡਬਲ-ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" "ਸ਼ਾਰਟਕੱਟ" "%1$s ਸ਼ਾਰਟਕੱਟ" diff --git a/go/res/values-vi/strings.xml b/go/res/values-vi/strings.xml index 84ed627964..1197619c57 100644 --- a/go/res/values-vi/strings.xml +++ b/go/res/values-vi/strings.xml @@ -20,7 +20,7 @@ "Chạm và giữ để chọn lối tắt." - "Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng tác vụ tùy chỉnh." + "Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng hành động tùy chỉnh." "Lối tắt" "Lối tắt %1$s" From e5131a98bc390d8be688d55349aafcc776f056a8 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 11 Aug 2017 14:06:54 -0700 Subject: [PATCH 010/885] Import translations. DO NOT MERGE Change-Id: I0b935be107781369fa9d3b446a8cd896fe6c7af7 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-bn/strings.xml | 4 ++-- res/values-cs/strings.xml | 4 ++-- res/values-gu/strings.xml | 4 ++-- res/values-hi/strings.xml | 2 +- res/values-ml/strings.xml | 2 +- res/values-mr/strings.xml | 16 ++++++++-------- res/values-pa/strings.xml | 6 +++--- res/values-sk/strings.xml | 2 +- res/values-te/strings.xml | 12 ++++++------ 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 92f1affb69..471602f47d 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -27,7 +27,7 @@ "ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে" "সুরক্ষিত মোডে উইজেট নিষ্ক্রিয় থাকে" "শর্টকাটগুলি অনুপলব্ধ" - "হোম স্ক্রীন" + "হোম স্ক্রিন" "কাস্টম অ্যাকশন" "একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷" "কোনো উইজেট বেছে নিতে দুবার-আলতো চেপে ধরে থাকুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন৷" @@ -60,7 +60,7 @@ "নামবিহীন ফোল্ডার" "%1$s অক্ষম করা হয়েছে" "%2$dটির মধ্যে %1$dটি পৃষ্ঠা" - "%2$dটির %1$d নম্বর হোম স্ক্রীন" + "%2$dটির %1$d নম্বর হোম স্ক্রিন" "নতুন হোম স্ক্রীনের পৃষ্ঠা" "ফোল্ডার খোলা হয়েছে, %1$d বাই %2$d" "ফোল্ডার বন্ধ করতে আলতো চাপ দিন" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index abc44c7142..e6dee3c3b3 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -76,11 +76,11 @@ "Povolit otáčení plochy" "Při otočení telefonu" "Aktuální nastavení displeje neumožňuje otáčení" - "Puntíky s oznámeními" + "Puntíky s oznámením" "Zapnuto" "Vypnuto" "Je třeba udělit přístup k oznámením" - "Chcete-li zobrazovat puntíky s oznámeními, zapněte oznámení z aplikace %1$s" + "Chcete-li zobrazovat puntíky s oznámením, zapněte oznámení z aplikace %1$s" "Změnit nastavení" "Přidat ikonu na plochu" "Pro nové aplikace" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 808c04756f..7096a3ee0a 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -29,7 +29,7 @@ "શૉર્ટકટ ઉપલબ્ધ નથી" "હોમ સ્ક્રીન" "કસ્ટમ ક્રિયાઓ" - "વિજેટ ચૂંટવા માટે ટચ કરો અને પકડી રાખો." + "વિજેટ ચૂંટવા માટે સ્પર્શ કરો અને પકડી રાખો." "વિજેટ ચૂંટવા અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટેપ કરો અને પકડી રાખો." "%1$d × %2$d" "%1$d પહોળાઈ X %2$d ઊંચાઈ" @@ -72,7 +72,7 @@ "વૉલપેપર્સ" "હોમ સેટિંગ્સ" "તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ" - "વિહંગાવલોકન" + "ઝલક" "હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો" "જ્યારે ફોન ફેરવવામાં આવે ત્યારે" "વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 02c470ba07..f1ee0f2366 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -72,7 +72,7 @@ "वॉलपेपर" "होम सेटिंग" "आपके व्यवस्थापक द्वारा अक्षम" - "अवलोकन" + "खास जानकारी" "होमस्क्रीन घुमाने की अनुमति दें" "फ़ोन घुुमाए जाने पर" "वर्तमान प्रदर्शन सेटिंग घुमाने की अनुमति नहीं देती" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index b6087546cd..0cc7aa023c 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -72,7 +72,7 @@ "വാൾപേപ്പർ" "ഹോം ക്രമീകരണം" "അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു" - "കാഴ്ച" + "അവലോകനം" "ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക" "ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ" "നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 4fa8d49fe3..751ad227ab 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -27,14 +27,14 @@ "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" "विजेट सुरक्षित मोडमध्ये अक्षम झाले" "शॉर्टकट उपलब्ध नाही" - "मुख्यपृष्ठ" + "होम स्क्रीन" "सानुकूल क्रिया" "विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." "एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा सानुकूल क्रिया वापरा." "%1$d × %2$d" "%1$d रूंद बाय %2$d उंच" "स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा" - "स्वयंचलितपणे जोडा" + "अापोआप जोडा" "अॅप्स शोधा" "अॅप्स लोड करत आहे…" "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" @@ -43,15 +43,15 @@ "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" "अॅप्स सूची" - "मुख्‍यपृष्‍ठ" + "होम" "काढा" "विस्थापित करा" "अॅप माहिती" "शॉर्टकट स्‍थापित करा" "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते." - "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा" + "होम सेटिग्ज आणि शॉर्टकट वाचा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते." - "मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा" + "होम स्क्रीन सेटिंग्ज आणि शॉर्टकट लिहा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते." "%1$s ला फोन कॉल करण्याची अनुमती नाही" "विजेट लोड करण्यात समस्या" @@ -72,7 +72,7 @@ "वॉलपेपर" "होम सेटिंग्‍ज" "आपल्या प्रशासकाने अक्षम केले" - "विहंगावलोकन" + "अवलोकन" "मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या" "फोन फिरविला जातो तेव्हा" "वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही" @@ -82,7 +82,7 @@ "सूचनांच्या अ‍ॅक्सेसची आवश्यकता आहे" "सूचना बिंदू दाखवण्यासाठी, %1$s साठी अ‍ॅप सूचना चालू करा" "सेटिंग्ज बदला" - "मुख्य स्क्रीनवर चिन्ह जोडा" + "होम स्क्रीनवर आयकन जोडा" "नवीन अॅप्ससाठी" "चिन्हाचा आकार बदला" "सिस्‍टमचे डीफॉल्‍ट वापरा" @@ -99,7 +99,7 @@ "%1$s डाउनलोड होत आहे , %2$s पूर्ण झाले" "%1$s स्थापित करण्याची प्रतिक्षा करीत आहे" "%1$s विजेट" - "मुख्य स्क्रीनवर जोडा" + "होम स्क्रीनवर जोडा" "आयटम येथे हलवा" "आयटम मुख्य स्क्रीनवर जोडला" "आयटम काढला" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index e4cff7b8c7..cf5351c7a7 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -25,7 +25,7 @@ "ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।" "ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" - "ਵਿਜਿਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" + "ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਮੁੱਖ ਸਕ੍ਰੀਨ" "ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ" @@ -68,7 +68,7 @@ "ਫੋਲਡਰ ਬੰਦ ਕੀਤਾ" "ਫੋਲਡਰ ਨੂੰ %1$s ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ" "ਫੋਲਡਰ: %1$s" - "ਵਿਜਿਟ" + "ਵਿਜੇਟ" "ਵਾਲਪੇਪਰ" "ਹੋਮ ਸੈਟਿੰਗਾਂ" "ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ" @@ -98,7 +98,7 @@ "ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਇੰਸਟੌਲ ਕਰ ਸਕਦੇ ਹੋ।" "%1$s ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, %2$s ਸੰਪੂਰਣ" "%1$s ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" - "%1$s ਵਿਜਿਟ" + "%1$s ਵਿਜੇਟ" "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ" "ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 490f823fe2..4dbc407b9c 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -58,7 +58,7 @@ "Nastavenie" "Toto je systémová aplikácia a nedá sa odinštalovať." "Nepomenovaný priečinok" - "Aplikácia %1$s je zakázaná" + "Aplikácia %1$s je deaktivovaná" "Stránka %1$d z %2$d" "Plocha %1$d z %2$d" "Nová stránka plochy" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 7fa666c677..6c6669938b 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -22,9 +22,9 @@ "Launcher3" "కార్యాలయం" - "అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు." - "అనువర్తనం అందుబాటులో లేదు" - "డౌన్‌లోడ్ చేసిన అనువర్తనం సురక్షిత మోడ్‌లో నిలిపివేయబడింది" + "యాప్ ఇన్‌స్టాల్ చేయబడలేదు." + "యాప్ అందుబాటులో లేదు" + "డౌన్‌లోడ్ చేసిన యాప్ సురక్షిత మోడ్‌లో నిలిపివేయబడింది" "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" "సత్వరమార్గం అందుబాటులో లేదు" "హోమ్ స్క్రీన్" @@ -56,7 +56,7 @@ "ఫోన్ కాల్‌లను చేసేందుకు %1$sకి అనుమతి లేదు" "విడ్జెట్‌ను లోడ్ చేయడంలో సమస్య" "సెటప్ చేయి" - "ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." + "ఇది సిస్టమ్ యాప్ మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు." "పేరు లేని ఫోల్డర్" "%1$s నిలిపివేయబడింది" "%2$dలో %1$dవ పేజీ" @@ -94,8 +94,8 @@ "తెలియదు" "తీసివేయి" "శోధించు" - "ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు" - "ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." + "ఈ యాప్ ఇన్‌స్టాల్ చేయబడలేదు" + "ఈ చిహ్నం యొక్క యాప్ ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ యాప్ కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." "%1$s డౌన్‌లోడ్ అవుతోంది, %2$s పూర్తయింది" "%1$s ఇన్‌స్టాల్ కావడానికి వేచి ఉంది" "%1$s విడ్జెట్‌లు" From f41edbe8751bdd92ec42cfa3b69ab9e33aac5b26 Mon Sep 17 00:00:00 2001 From: Mario Bertschler Date: Thu, 31 Aug 2017 11:47:53 -0700 Subject: [PATCH 011/885] Install long-click action for apps that are not installed. Bug: 65059282 Change-Id: Ifec3ac15c9cf52c456e695846eb91024b07fe333 --- res/drawable/ic_install_no_shadow.xml | 27 ++++++++++++++ res/values/strings.xml | 2 ++ .../launcher3/popup/PopupDataProvider.java | 1 + .../launcher3/popup/SystemShortcut.java | 35 +++++++++++++++++++ .../launcher3/util/InstantAppResolver.java | 5 +++ 5 files changed, 70 insertions(+) create mode 100644 res/drawable/ic_install_no_shadow.xml diff --git a/res/drawable/ic_install_no_shadow.xml b/res/drawable/ic_install_no_shadow.xml new file mode 100644 index 0000000000..ffce22aac1 --- /dev/null +++ b/res/drawable/ic_install_no_shadow.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 1197b1cf2c..e9b00f6faf 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -90,6 +90,8 @@ Uninstall App info + + Install diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index de9f25e12f..c921b4b827 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -53,6 +53,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan private static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] { new SystemShortcut.AppInfo(), new SystemShortcut.Widgets(), + new SystemShortcut.Install() }; private final Launcher mLauncher; diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 6254d2d008..a342500760 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -1,6 +1,7 @@ package com.android.launcher3.popup; import android.content.Context; +import android.content.Intent; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -11,7 +12,10 @@ import com.android.launcher3.InfoDropTarget; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; import com.android.launcher3.model.WidgetItem; +import com.android.launcher3.util.InstantAppResolver; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.WidgetsBottomSheet; @@ -95,4 +99,35 @@ public abstract class SystemShortcut extends ItemInfo { }; } } + + public static class Install extends SystemShortcut { + public Install() { + super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label); + } + + @Override + public View.OnClickListener getOnClickListener(final Launcher launcher, + final ItemInfo itemInfo) { + boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) && + ((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI); + boolean isInstantApp = false; + if (itemInfo instanceof com.android.launcher3.AppInfo) { + com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo; + isInstantApp = InstantAppResolver.newInstance(launcher).isInstantApp(appInfo); + } + boolean enabled = supportsWebUI || isInstantApp; + if (!enabled) { + return null; + } + return new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = PackageManagerHelper.getMarketIntent(itemInfo + .getTargetComponent().getPackageName()); + launcher.startActivitySafely(view, intent, itemInfo); + AbstractFloatingView.closeAllOpenViews(launcher); + } + }; + } + } } diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java index e60d76808a..99ce7ca90f 100644 --- a/src/com/android/launcher3/util/InstantAppResolver.java +++ b/src/com/android/launcher3/util/InstantAppResolver.java @@ -19,6 +19,7 @@ package com.android.launcher3.util; import android.content.Context; import android.content.pm.ApplicationInfo; +import com.android.launcher3.AppInfo; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -39,6 +40,10 @@ public class InstantAppResolver { return false; } + public boolean isInstantApp(AppInfo info) { + return false; + } + public List getInstantApps() { return Collections.emptyList(); } From 94fe9fabae5eff850a0142d76969c39d7d1783ba Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 5 Sep 2017 13:38:07 -0700 Subject: [PATCH 012/885] Add REQUEST_DELETE_PACKAGES permission to manifest. "Since O all apps requesting uninstalls should have this permission. There is a bug in the platform code that we do not enforce this. We enforce this starting in P." Bug: 65375213 Change-Id: If86e43a32d6a4553345fb1e285888e748376b818 --- AndroidManifest-common.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index ad404c09a9..94098e16e1 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -43,6 +43,7 @@ + Date: Tue, 5 Sep 2017 11:32:13 -0700 Subject: [PATCH 013/885] Add springs when user clicks on caret / all apps snaps to top. Bug: 65373058 Change-Id: I9cc5c4c98fd3e5b53d597c4493f2dcef6d9be94a --- .../launcher3/allapps/AllAppsGridAdapter.java | 5 ++++- .../allapps/AllAppsTransitionController.java | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 1f60fcc735..f7ce8c11e2 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -424,6 +424,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter Date: Wed, 6 Sep 2017 09:04:56 -0700 Subject: [PATCH 014/885] Import translations. DO NOT MERGE Change-Id: I5c47a9fba63d1c6b565528a79e253a06c8cca937 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-af/strings.xml | 2 ++ res/values-am/strings.xml | 2 ++ res/values-ar/strings.xml | 2 ++ res/values-az/strings.xml | 1 + res/values-b+sr+Latn/strings.xml | 2 ++ res/values-be/strings.xml | 2 ++ res/values-bg/strings.xml | 2 ++ res/values-bn/strings.xml | 8 +++-- res/values-bs/strings.xml | 6 ++-- res/values-ca/strings.xml | 2 ++ res/values-cs/strings.xml | 4 ++- res/values-da/strings.xml | 4 ++- res/values-de/strings.xml | 2 ++ res/values-el/strings.xml | 2 ++ res/values-en-rAU/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/strings.xml | 2 ++ res/values-eu/strings.xml | 1 + 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/strings.xml | 2 ++ res/values-gu/strings.xml | 4 ++- res/values-hi/strings.xml | 59 ++++++++++++++++---------------- res/values-hr/strings.xml | 2 ++ res/values-hu/strings.xml | 2 ++ res/values-hy/strings.xml | 6 ++-- res/values-in/strings.xml | 4 ++- res/values-is/strings.xml | 2 ++ res/values-it/strings.xml | 2 ++ res/values-iw/strings.xml | 4 ++- res/values-ja/strings.xml | 4 ++- res/values-ka/strings.xml | 2 ++ res/values-kk/strings.xml | 2 ++ res/values-km/strings.xml | 2 ++ res/values-kn/strings.xml | 4 ++- res/values-ko/strings.xml | 2 ++ res/values-ky/strings.xml | 2 ++ res/values-lo/strings.xml | 2 ++ res/values-lt/strings.xml | 2 ++ res/values-lv/strings.xml | 4 ++- res/values-mk/strings.xml | 8 +++-- res/values-ml/strings.xml | 2 ++ res/values-mn/strings.xml | 2 ++ res/values-mr/strings.xml | 20 ++++++----- res/values-ms/strings.xml | 2 ++ res/values-my/strings.xml | 1 + res/values-nb/strings.xml | 2 ++ res/values-ne/strings.xml | 2 ++ res/values-nl/strings.xml | 1 + res/values-pa/strings.xml | 18 +++++----- 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/strings.xml | 2 ++ res/values-sk/strings.xml | 2 ++ res/values-sl/strings.xml | 2 ++ res/values-sq/strings.xml | 2 ++ res/values-sr/strings.xml | 2 ++ res/values-sv/strings.xml | 2 ++ res/values-sw/strings.xml | 2 ++ res/values-ta/strings.xml | 6 ++-- res/values-te/strings.xml | 12 ++++--- 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/strings.xml | 2 ++ res/values-uz/strings.xml | 6 ++-- 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 ++ 80 files changed, 228 insertions(+), 73 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index d71b4c78b3..a79c2d4b57 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -47,6 +47,8 @@ "Verwyder" "Deïnstalleer" "Programinligting" + + "installeer kortpaaie" "Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging." "lees Tuis-instellings en -kortpaaie" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 293b452f68..c7d2958d0c 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -47,6 +47,8 @@ "አስወግድ" "አራግፍ" "የመተግበሪያ መረጃ" + + "አቋራጮችን ይጭናል" "መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።" "የመነሻ ቅንብሮች እና አቋራጮችን ያነባል" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 6447bf0150..a15bb76736 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -47,6 +47,8 @@ "إزالة" "إلغاء التثبيت" "معلومات عن التطبيق" + + "تثبيت اختصارات" "للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم." "قراءة إعدادات واختصارات الشاشة الرئيسية" diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 18f87bcf33..dddb6faeff 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -47,6 +47,7 @@ "Silin" "Sistemdən sil" "Tətbiq məlumatı" + "Quraşdırın" "qısayolları quraşdır" "Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir." "Əsas Səhifə ayarlarını və qısayolları oxuyun" diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 777512a966..b583865e77 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -47,6 +47,8 @@ "Ukloni" "Deinstaliraj" "Inform. o aplikaciji" + + "instaliranje prečica" "Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika." "čitanje podešavanja i prečica na početnom ekranu" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 0a31ddf7c1..eae036e55f 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -47,6 +47,8 @@ "Выдаліць" "Выдаліць" "Звесткі пра праграмы" + + "усталёўваць ярлыкі" "Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка." "счытваць налады і ярлыкі на Галоўнай старонцы" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 3e05e0be57..8ef3df8e6a 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -47,6 +47,8 @@ "Премахване" "Деинсталиране" "Данни за прилож." + + "инсталиране на преки пътища" "Разрешава на приложението да добавя преки пътища без намеса на потребителя." "четене на настройките и преките пътища в Начало" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 471602f47d..b6c4226059 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -35,10 +35,10 @@ "%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ" "নিজে যোগ করতে টাচ করে ধরে রাখুন" "স্বয়ংক্রিয়ভাবে যোগ করুন" - "অ্যাপ অনুসন্ধান করুন" + "অ্যাপ খুঁজুন" "অ্যাপ লোড হচ্ছে…" "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি" - "আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন" + "আরও অ্যাপ্লিকেশানের জন্য খুঁজুন" "বিজ্ঞপ্তি" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" "পছন্দসই ট্রে-তে আর কোনো জায়গা নেই" @@ -46,7 +46,9 @@ "হোম" "সরান" "আনইনস্টল করুন" - "অ্যাপ্লিকেশানের তথ্য" + "অ্যাপের তথ্য" + + "শর্টকাটগুলি ইনস্টল করে" "একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷" "হোম সেটিংস এবং শর্টকাটগুলি পড়ে" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 49c862a8cd..773fe4af70 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -47,6 +47,8 @@ "Ukloni" "Deinstaliraj" "Informacije o aplikaciji" + + "instaliraj prečice" "Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika." "čitaj postavke na početnom ekranu i prečice" @@ -64,7 +66,7 @@ "Nova stranica početnog ekrana" "Folder je otvoren, (š) %1$d (v) %2$d" "Dodirnite da zatvorite folder" - "Dodirnite da sačuvate promjenu imena" + "Dodirnite da sačuvate promjenu naziva" "Folder je zatvoren" "Ime foldera je promijenjeno u %1$s" "Folder: %1$s" @@ -82,7 +84,7 @@ "Potreban je pristup obavještenjima" "Za prikaz tačaka obavještenja, uključite obavještenja za aplikacije za aplikaciju %1$s" "Promijeni postavke" - "Dodajte ikonu na početni ekran" + "Dodaj ikonu na početni ekran" "Za nove aplikacije" "Promjena oblika ikona" "Koristite sistemski zadano" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index b1a1f85e06..acaf997250 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -47,6 +47,8 @@ "Suprimeix" "Desinstal·la" "Dades de l\'aplicació" + + "instal·la dreceres" "Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari." "llegeix la configuració i les dreceres de la pantalla d\'inici" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index e6dee3c3b3..db7f7bbeed 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -46,7 +46,9 @@ "Plocha" "Odstranit" "Odinstalovat" - "Informace o aplikaci" + "O aplikaci" + + "instalace zástupce" "Umožňuje aplikaci přidat zástupce bez zásahu uživatele." "čtení nastavení a odkazů plochy" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 653105e058..8e5448696c 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -46,7 +46,9 @@ "Startskærm" "Fjern" "Afinstaller" - "Oplysninger om appen" + "Appinfo" + + "installere genveje" "Tillader, at en app tilføjer genveje uden brugerens indgriben." "læs indstillinger og genveje for startskærmen" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 1c4cd7a7ca..b32cdf4574 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -47,6 +47,8 @@ "Entfernen" "Deinstallieren" "App-Details" + + "Verknüpfungen installieren" "Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers" "Einstellungen und Verknüpfungen auf dem Startbildschirm lesen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 25c8866b85..421907ffc1 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -47,6 +47,8 @@ "Κατάργηση" "Απεγκατάσταση" "Πληροφορίες εφαρμογής" + + "εγκατάσταση συντομεύσεων" "Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη." "ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index f3f10cc82a..b4823f3a3e 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -47,6 +47,8 @@ "Remove" "Uninstall" "App info" + + "install shortcuts" "Allows an app to add shortcuts without user intervention." "read Home settings and shortcuts" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index f3f10cc82a..b4823f3a3e 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -47,6 +47,8 @@ "Remove" "Uninstall" "App info" + + "install shortcuts" "Allows an app to add shortcuts without user intervention." "read Home settings and shortcuts" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index f3f10cc82a..b4823f3a3e 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -47,6 +47,8 @@ "Remove" "Uninstall" "App info" + + "install shortcuts" "Allows an app to add shortcuts without user intervention." "read Home settings and shortcuts" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index f60e1862ae..af22e0be70 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -47,6 +47,8 @@ "Quitar" "Desinstalar" "Información de app" + + "instalar accesos directos" "Permite que una aplicación agregue accesos directos sin que el usuario intervenga." "leer configuración y accesos directos de la pantalla principal" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 3ab41caf69..cd80b378c5 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -47,6 +47,8 @@ "Quitar" "Desinstalar" "Datos de aplicación" + + "instalar accesos directos" "Permite que una aplicación añada accesos directos sin intervención del usuario." "leer información de accesos directos y de ajustes de la pantalla de inicio" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 9d9991ddce..d911ac4f01 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -47,6 +47,8 @@ "Eemalda" "Desinstalli" "Rakenduse teave" + + "installi otseteed" "Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta." "loe avaekraani seadeid ja otseteid" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index b6082fadab..68478dedf1 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -47,6 +47,7 @@ "Kendu" "Desinstalatu" "Aplikazioaren datuak" + "Instalatu" "Instalatu lasterbideak" "Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei." "Irakurri hasierako ezarpenak eta lasterbideak" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 8fe874b321..aaaf0d8019 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -47,6 +47,8 @@ "برداشتن" "حذف نصب" "اطلاعات برنامه" + + "نصب میان‌برها" "به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند." "خواندن تنظیمات و میان‌برهای صفحه اصلی" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 016dbbce37..7b09da0d3c 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -47,6 +47,8 @@ "Poista" "Poista asennus" "Sovelluksen tiedot" + + "asenna pikakuvakkeita" "Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa." "lue aloitusruudun asetuksia ja pikakuvakkeita" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index e57433d0c3..9768320176 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -47,6 +47,8 @@ "Supprimer" "Désinstaller" "Détails de l\'appli" + + "installer des raccourcis" "Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur." "lire les paramètres et les raccourcis de la page d\'accueil" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index dde439288c..fde0c60c86 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -47,6 +47,8 @@ "Supprimer" "Désinstaller" "Infos sur l\'appli" + + "installer des raccourcis" "Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur" "lire les paramètres et les raccourcis de l\'écran d\'accueil" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index d55514d1e9..bdc79f2845 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -47,6 +47,8 @@ "Eliminar" "Desinstalar" "Info. da aplicación" + + "instalar atallos" "Permite a unha aplicación engadir atallos sen intervención do usuario." "ler a configuración e os atallos da pantalla de inicio" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 7096a3ee0a..68a9e8626f 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -39,7 +39,7 @@ "ઍપ્લિકેશનો લોડ કરી રહ્યું છે…" "\"%1$s\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" "વધુ ઍપ્લિકેશનો શોધો" - "સૂચનાઓ" + "નોટિફિકેશનો" "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." "મનપસંદ ટ્રે પર વધુ જગ્યા નથી" "ઍપ્લિકેશનોની સૂચિ" @@ -47,6 +47,8 @@ "દૂર કરો" "અનઇન્સ્ટોલ કરો" "ઍપ્લિકેશન માહિતી" + + "શોર્ટકટ્સ ઇન્સ્ટોલ કરો" "એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે." "હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index f1ee0f2366..238740471b 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -28,31 +28,32 @@ "विजेट सुरक्षित मोड में अक्षम हैं" "शॉर्टकट उपलब्ध नहीं है" "होम स्क्रीन" - "कस्टम कार्रवाई" - "विजेट को चुनने के लिए स्‍पर्श करके रखें." - "कोई विजेट चुनने के लिए डबल टैप करके रखें या कस्‍टम कार्रवाइयां चुनें." + "कस्टम कार्रवाइयां" + "विजेट को चुनने के लिए दबाकर रखें" + "कोई विजेट चुनने के लिए दो बार छूएं और दबाये रखें या अपने मुताबिक कार्रवाइयां चुनें." "%1$d × %2$d" "%1$d चौड़ाई गुणा %2$d ऊंचाई" - "मैन्युअल रूप से जोड़ने के लिए स्पर्श करके रखें" + "खुद जोड़ने के लिए दबाकर रखें" "अपने आप जोड़ें" - "ऐप्लिकेशन खोजें" + "ऐप सर्च करें" "ऐप्लिकेशन लोड हो रहे हैं…" "\"%1$s\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला" - "अधिक ऐप्लिकेशन खोजें" - "नोटिफ़िकेशन" - "इस होम स्‍क्रीन पर स्थान शेष नहीं है." - "पसंदीदा ट्रे में और स्थान नहीं है" + "और ऐप सर्च करें" + "सूचनाएं" + "इस होम स्‍क्रीन पर जगह नहीं बची है" + "पसंदीदा ट्रे में और जगह नहीं है" "ऐप्लिकेशन सूची" - "होम" + "होम पेज" "निकालें" "अनइंस्टॉल करें" "ऐप की जानकारी" + "इंस्‍टॉल करें" "शॉर्टकट इंस्‍टॉल करें" - "ऐप्लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है." - "होम सेटिंग और शॉर्टकट पढ़ें" - "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट पढ़ने देती है." - "होम सेटिंग और शॉर्टकट लिखें" - "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट बदलने देती है." + "ऐप को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है." + "होम पेज की सेटिंग और शॉर्टकट पढ़ें" + "ऐप्लिकेशन को होम पेज में सेटिंग और शॉर्टकट पढ़ने देती है." + "होम पेज की सेटिंग और शॉर्टकट लिखें" + "ऐप्लिकेशन को होम पेज में सेटिंग और शॉर्टकट बदलने देती है." "%1$s को फ़ोन कॉल करने की अनुमति नहीं है" "विजेट लोड करने में समस्‍या" "सेटअप" @@ -61,7 +62,7 @@ "%1$s अक्षम है" "पेज %2$d में से %1$d" "होम स्क्रीन %2$d में से %1$d" - "नया होम स्‍क्रीन पृष्‍ठ" + "नया होम स्‍क्रीन पेज" "फ़ोल्डर खोला गया, %1$d गुणा %2$d" "फ़ोल्डर बंद करने के लिए टैप करें" "नाम बदलना सहेजने के लिए टैप करें" @@ -70,32 +71,32 @@ "फ़ोल्डर: %1$s" "शॉर्टकट" "वॉलपेपर" - "होम सेटिंग" + "होम पेज की सेटिंग" "आपके व्यवस्थापक द्वारा अक्षम" "खास जानकारी" "होमस्क्रीन घुमाने की अनुमति दें" "फ़ोन घुुमाए जाने पर" - "वर्तमान प्रदर्शन सेटिंग घुमाने की अनुमति नहीं देती" - "नोटिफ़िकेशन बिंदु" + "इस डिसप्ले सेटिंग में रोटेशन (स्क्रीन पर सामग्री को घुमाकर देखने) की सुविधा काम नहीं करती" + "सूचना बिंदु" "चालू" "बंद" - "नोटिफ़िकेशन एक्‍सेस ज़रूरी है" - "नोटिफ़िकेशन बिंदु दिखाने के लिए, %1$s के ऐप्लिकेशन नोटिफ़िकेशन चालू करें" + "सूचना के एक्सेस की ज़रूरत है" + "सूचना बिंदु दिखाने के लिए, %1$s के ऐप्लिकेशन सूचना चालू करें" "सेटिंग बदलें" - "होम स्क्रीन में आइकन जोड़ें" - "नए ऐप्लिकेशन के लिए" - "आइकन का आकार बदलें" + "होम स्क्रीन में आइकॉन जोड़ें" + "नए ऐप के लिए" + "आइकॉन का आकार बदलें" "सिस्टम डिफ़ॉल्ट का उपयोग करें" "वर्ग" "गोल कोनों वाला वर्ग" "मंडली" "आंसू की बूंद" - "आइकन के आकार में बदलाव लागू किए जा रहे हैं" + "आइकॉन के आकार में बदलाव किए जा रहे हैं" "अज्ञात" "निकालें" - "खोजें" + "सर्च करें" "यह ऐप्स इंस्टॉल नहीं है" - "इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." + "इस आइकॉन का ऐप इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप को खोज कर उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." "%1$s डाउनलोड हो रहा है, %2$s पूर्ण" "%1$s के इंस्टॉल होने की प्रतीक्षा की जा रही है" "%1$s विजेट" @@ -125,7 +126,7 @@ "विजेट का आकार बदलकर उसकी चौड़ाई %1$s और ऊंचाई %2$s कर दी गई" "शॉर्टकट" "%2$s के लिए %1$d शॉर्टकट" - "%3$s के लिए %1$d शॉर्टकट और %2$d नोटिफ़िकेशन हैं" + "%3$s के लिए %1$d शॉर्टकट और %2$d सूचनाएं हैं" "खारिज करें" - "नोटिफ़िकेशन को खारिज किया गया" + "सूचना को खारिज किया गया" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index aaae258b80..24f9997e75 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -47,6 +47,8 @@ "Ukloni" "Deinstaliraj" "Info o aplikaciji" + + "instaliranje prečaca" "Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika." "čitanje postavki početnog zaslona i prečaca" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 2da64b06bc..94907eb47b 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -47,6 +47,8 @@ "Törlés" "Eltávolítás" "Alkalmazásinformáció" + + "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." "Főoldal beállításainak és parancsikonjainak beolvasása" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 2b55e966a4..44b337d4f7 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -47,6 +47,8 @@ "Հեռացնել" "Հեռացնել" "Հավելվածի տվյալներ" + + "տեղադրել դյուրանցումներ" "Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:" "կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները" @@ -76,11 +78,11 @@ "Թույլ տալ հիմնական էկրանի պտտումը" "Հեռախոսը պտտելու դեպքում" "Ցուցադրման ընթացիկ կարգավորումներն արգելում են պտտումը" - "Ծանուցման կետեր" + "Ծանուցումների կետիկներ" "Միացված է" "Անջատված է" "Անհրաժեշտ է ծանուցման թույլտվություն" - "Ծանուցման կետերը ցուցադրելու համար միացրեք հավելվածի ծանուցումները %1$s-ի համար" + "Ծանուցումների կետիկները ցուցադրելու համար միացրեք ծանուցումները %1$s-ի համար" "Փոխել կարգավորումները" "Ավելացնել պատկերակը Հիմնական էկրանին" "Նոր հավելվածների համար" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 379a960d05..da9a683381 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -47,6 +47,8 @@ "Hapus" "Uninstal" "Info aplikasi" + + "memasang pintasan" "Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna." "membaca setelan dan pintasan layar Utama" @@ -82,7 +84,7 @@ "Perlu akses notifikasi" "Guna menampilkan Titik Notifikasi, aktifkan notifikasi aplikasi untuk %1$s" "Ubah setelan" - "Tambahkan ikon ke layar Utama" + "Tambahkan ikon ke Layar utama" "Untuk aplikasi baru" "Ubah bentuk ikon" "Gunakan default sistem" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index cf178a56ad..40c26d788b 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -47,6 +47,8 @@ "Fjarlægja" "Fjarlægja" "Forritsupplýsingar" + + "setja upp flýtileiðir" "Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda." "lesa stillingar og flýtileiðir heimaskjás" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index a52aee4295..c1d97d9dcc 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -47,6 +47,8 @@ "Rimuovi" "Disinstalla" "Informazioni app" + + "aggiunta di scorciatoie" "Consente a un\'app di aggiungere scorciatoie automaticamente." "lettura di impostazioni e scorciatoie in Home" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 650a90db81..d1595b9dcc 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -47,6 +47,8 @@ "הסר" "הסר התקנה" "פרטי אפליקציה" + + "התקן קיצורי דרך" "מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש." "קרא הגדרות וקיצורי דרך של דף הבית" @@ -93,7 +95,7 @@ "משנה את הצורה של הסמלים" "לא ידוע" "הסר" - "חפש" + "חיפוש" "אפליקציה זו אינה מותקנת" "האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית." "מוריד את %1$s, %2$s הושלמו" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 4071a6cc99..eb1b8aa052 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -47,6 +47,8 @@ "削除" "アンインストール" "アプリ情報" + + "ショートカットのインストール" "ユーザー操作なしでショートカットを追加することをアプリに許可します。" "ホームの設定とショートカットの読み取り" @@ -83,7 +85,7 @@ "通知ドットを表示するには、「%1$s」のアプリ通知を ON にしてください" "設定を変更" "ホーム画面にアイコンを追加" - "新しいアプリをダウンロードしたときに" + "新しいアプリをダウンロードしたとき" "アイコンの形の変更" "システムのデフォルトを使用" "スクエア" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 7e8b46ce88..297b9641bb 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -47,6 +47,8 @@ "ამოშლა" "დეინსტალაცია" "აპის შესახებ" + + "მალსახმობების დაყენება" "აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა." "მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 50ed9aa680..681816285f 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -47,6 +47,8 @@ "Жою" "Жою" "Қолданба ақпараты" + + "төте пернелерді орнату" "Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді." "Негізгі экрандағы параметрлер мен төте пернелерді оқу" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 7028f7e3ae..e72b2ec083 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -47,6 +47,8 @@ "យកចេញ" "លុបការដំឡើង" "ព័ត៌មាន​កម្មវិធី" + + "ដំឡើង​ផ្លូវកាត់" "អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។" "អាន​ការ​កំណត់​ និង​ផ្លូវកាត់​​អេក្រង់​ដើម" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 9a31649be3..df3b80a3db 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -45,8 +45,10 @@ "ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ" "ಮುಖಪುಟ" "ತೆಗೆದುಹಾಕಿ" - "ಅಸ್ಥಾಪಿಸು" + "ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್" "ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ" + + "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ" "ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ." "ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index e3511d4413..1630ddd054 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -47,6 +47,8 @@ "삭제" "제거" "앱 정보" + + "바로가기 설치" "앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다." "홈 설정 및 바로가기 읽기" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 85ba8be3c3..a0635d4d26 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -47,6 +47,8 @@ "Алып салуу" "Чыгарып салуу" "Колдонмо тууралуу" + + "тез чакырмаларды орнотуу" "Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет." "Үйдүн тууралоолорун жана тез чакырмаларын окуу" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 9a50c4b517..759c73200f 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -47,6 +47,8 @@ "ເອົາ​ອອກ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ຂໍ້ມູນແອັບ" + + "ຕິດຕັ້ງທາງລັດ" "ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້." "ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index d415e4df1b..ad1aa6d074 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -47,6 +47,8 @@ "Ištrinti" "Pašalinti" "Programos inform." + + "įdiegti sparčiuosius klavišus" "Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo." "skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 2d8bfd83a3..1b99b1e565 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -46,7 +46,9 @@ "Sākums" "Noņemt" "Atinstalēt" - "Lietotnes informācija" + "Par lietotni" + + "instalēt saīsnes" "Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam." "lasīt sākuma ekrāna iestatījumus un saīsnes" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 7d95a23a6c..a4b967bfb4 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -47,6 +47,8 @@ "Отстрани" "Деинсталирај" "Инф. за апликација" + + "инсталирај кратенки" "Овозможува апликацијата да додава кратенки без интервенција на корисникот." "чита поставки и кратенки на почетна страница" @@ -68,8 +70,8 @@ "Папката е затворена" "Папката е преименувана во %1$s" "Папка: %1$s" - "Додатоци" - "Позадини" + "Виџети" + "Тапети" "Поставки за Home" "Оневозможено од администраторот" "Краток преглед" @@ -82,7 +84,7 @@ "Потребен е пристап до известувањата" "За да се прикажуваат „Точки за известување“, вклучете ги известувањата за апликацијата %1$s" "Промени ги поставките" - "Додајте икона на почетниот екран" + "Додај икона на почетниот екран" "За нови апликации" "Променете ја формата на иконата" "Користи ја стандардната поставка на системот" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 0cc7aa023c..c18b119f17 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -47,6 +47,8 @@ "നീക്കംചെയ്യുക" "അൺഇൻസ്റ്റാളുചെയ്യുക" "ആപ്പ് വിവരം" + + "കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക" "ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." "ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക" diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 22479cc3e4..3fad545cf2 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -47,6 +47,8 @@ "Арилгах" "Устгах" "Апп-н мэдээлэл" + + "товчлол суулгах" "Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна" "Нүүрний тохиргоо болон товчлолыг унших" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 751ad227ab..fabe15db8a 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -22,7 +22,7 @@ "Launcher3" "कार्य" - "अॅप स्थापित केलेला नाही." + "अॅप इंस्टॉल केलेला नाही." "अॅप उपलब्ध नाही" "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" "विजेट सुरक्षित मोडमध्ये अक्षम झाले" @@ -45,18 +45,20 @@ "अॅप्स सूची" "होम" "काढा" - "विस्थापित करा" + "अनइंस्टॉल करा" "अॅप माहिती" + + "शॉर्टकट स्‍थापित करा" "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते." - "होम सेटिग्ज आणि शॉर्टकट वाचा" + "होम सेटिंग्ज आणि शॉर्टकट वाचा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते." - "होम स्क्रीन सेटिंग्ज आणि शॉर्टकट लिहा" + "होम सेटिंग्ज आणि शॉर्टकट लिहा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते." "%1$s ला फोन कॉल करण्याची अनुमती नाही" "विजेट लोड करण्यात समस्या" "सेटअप" - "हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही." + "हा सिस्टम अॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही." "अनामित फोल्डर" "%1$s अक्षम केला आहे" "%2$d पैकी %1$d पृष्ठ" @@ -75,7 +77,7 @@ "अवलोकन" "मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या" "फोन फिरविला जातो तेव्हा" - "वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही" + "वर्तमान डिस्प्ले सेटिंग रोटेशनला परवानगी देत नाही" "सूचना बिंदू" "चालू" "बंद" @@ -94,10 +96,10 @@ "अज्ञात" "काढा" "शोधा" - "हा अॅप स्थापित केलेला नाही" - "या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता." + "हा अॅप इंस्टॉल केलेला नाही" + "या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता." "%1$s डाउनलोड होत आहे , %2$s पूर्ण झाले" - "%1$s स्थापित करण्याची प्रतिक्षा करीत आहे" + "%1$s इंस्टॉल करण्याची प्रतिक्षा करीत आहे" "%1$s विजेट" "होम स्क्रीनवर जोडा" "आयटम येथे हलवा" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index f94b167a69..08743875e9 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -47,6 +47,8 @@ "Alih keluar" "Nyahpasang" "Maklumat apl" + + "pasang pintasan" "Membenarkan apl menambah pintasan tanpa campur tangan pengguna." "baca tetapan dan pintasan Laman Utama" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index 74574b9b23..0b2e0d5366 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -47,6 +47,7 @@ "ဖယ်ရှားမည်" "ဖယ်ထုတ်မည်" "အက်ပ်အချက်အလက်များ" + "ထည့်သွင်းရန်" "အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း" "အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း" "ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 65247c168d..3ad2cc88a1 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -47,6 +47,8 @@ "Fjern" "Avinstaller" "Info om appen" + + "installere snarveier" "Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren." "lese startsideinnstillinger og -snarveier" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 904dad45e2..776d6729c1 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -47,6 +47,8 @@ "हटाउनुहोस्" "विस्थापित गर्नुहोस्" "अनुप्रयोग जानकारी" + + "सर्टकट स्थापना गर्नेहोस्" "प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।" "गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 7e8def4ca8..16a79aef91 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -47,6 +47,7 @@ "Verwijderen" "Deïnstalleren" "App-info" + "Installeren" "Snelle links instellen" "Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker." "instellingen en snelkoppelingen op de homepage lezen" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index cf5351c7a7..b1d3efd1e4 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -27,10 +27,10 @@ "ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ" + "ਹੋਮ ਸਕ੍ਰੀਨ" "ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ" "ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT & ਹੋਲਡ ਕਰੋ।" - "ਡਬਲ-ਟੈਪ & ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।" + "ਡਬਲ ਟੈਪ ਕਰੋ & ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" "%1$d × %2$d" "%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ" "ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ" @@ -47,6 +47,8 @@ "ਹਟਾਓ" "ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ" "ਐਪ ਜਾਣਕਾਰੀ" + + "ਸ਼ਾਰਟਕੱਟ ਇੰਸਟੌਲ ਕਰੋ" "ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" "ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ" @@ -73,16 +75,16 @@ "ਹੋਮ ਸੈਟਿੰਗਾਂ" "ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ" "ਰੂਪ-ਰੇਖਾ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ" + "ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ" "ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ" "ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ" "ਸੂਚਨਾ ਬਿੰਦੂ" "ਚਾਲੂ" "ਬੰਦ" "ਸੂਚਨਾ ਪਹੁੰਚ ਲੋੜੀਂਦੀ ਹੈ" - "ਸੂਚਨਾ ਬਿੰਦੀਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" + "ਸੂਚਨਾ ਬਿੰਦੂਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" "ਸੈਟਿੰਗਾਂ ਬਦਲੋ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" "ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ" @@ -95,13 +97,13 @@ "ਹਟਾਓ" "ਖੋਜੋ" "ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।" - "ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਇੰਸਟੌਲ ਕਰ ਸਕਦੇ ਹੋ।" + "ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।" "%1$s ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, %2$s ਸੰਪੂਰਣ" "%1$s ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" "%1$s ਵਿਜੇਟ" "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ" "ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ" - "ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" + "ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" "ਅਈਟਮ ਹਟਾਈ ਗਈ" "ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ" "ਕਤਾਰ %1$s ਕਾਲਮ %2$s ਵਿੱਚ ਮੂਵ ਕਰੋ" @@ -113,7 +115,7 @@ "ਆਈਟਮ ਨੂੰ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" "ਇਸਦੇ ਨਾਲ ਫੋਲਡਰ ਬਣਾਓ: %1$s" "ਫੋਲਡਰ ਬਣਾਇਆ ਗਿਆ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਮੂਵ ਕਰੋ" "ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਮੂਵ ਕਰੋ" "ਸਕ੍ਰੀਨ ਨੂੰ ਮੂਵ ਕੀਤਾ ਗਿਆ" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index c07f7defb3..486a94e31c 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -47,6 +47,8 @@ "Usuń" "Odinstaluj" "O aplikacji" + + "instalowanie skrótów" "Pozwala aplikacji dodawać skróty bez interwencji użytkownika." "odczytywanie ustawień i skrótów na ekranie głównym" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 100f07b66b..1916b20147 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -47,6 +47,8 @@ "Remover" "Desinstalar" "Inf. da aplicação" + + "instalar atalhos" "Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador." "ler definições e atalhos do Ecrã Principal" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index d75159f94a..4a09cd52e7 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -47,6 +47,8 @@ "Remover" "Desinstalar" "Informações do app" + + "instalar atalhos" "Permite que um app adicione atalhos sem intervenção do usuário." "ler configurações e atalhos da tela inicial" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 512d96d6b8..43ce4c8f76 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -47,6 +47,8 @@ "Eliminați" "Dezinstalați" "Informații aplicație" + + "instalează comenzi rapide" "Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului." "citește setări și comenzi rapide pentru ecranul de pornire" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 923f3573b3..8bf71d631c 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -47,6 +47,8 @@ "Убрать" "Удалить" "О приложении" + + "Создание ярлыков" "Приложение сможет самостоятельно добавлять ярлыки." "Доступ к настройкам и ярлыкам главного экрана" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index f6c42b2d9a..fbdf5bfe1f 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -47,6 +47,8 @@ "ඉවත් කරන්න" "අස්ථාපනය කරන්න" "යෙදුම් තොරතුරු" + + "කෙටිමං ස්ථාපනය කරන්න" "පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි." "මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 4dbc407b9c..4268bad522 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -47,6 +47,8 @@ "Odstrániť" "Odinštalovať" "Info o aplikácii" + + "inštalovať odkazy" "Povoľuje aplikácii pridať odkazy bez zásahu používateľa." "čítanie nastavení a odkazov plochy" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 0b7d36ef8b..c7abd88638 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -47,6 +47,8 @@ "Odstrani" "Odstrani" "Podatki o aplikaciji" + + "namestitev bližnjic" "Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika." "branje nastavitev in bližnjic na začetnem zaslonu" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 3e6afee609..7b3a9095bf 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -47,6 +47,8 @@ "Hiqe" "Çinstalo" "Informacion mbi aplikacionin" + + "instalo shkurtore" "Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit." "lexo cilësimet dhe shkurtoret e ekranit bazë" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index f41e02d33f..76f28e03e0 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -47,6 +47,8 @@ "Уклони" "Деинсталирај" "Информ. о апликацији" + + "инсталирање пречица" "Дозвољава апликацији да додаје пречице без интервенције корисника." "читање подешавања и пречица на почетном екрану" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 6c598ff5dc..354d7099e9 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -47,6 +47,8 @@ "Ta bort" "Avinstallera" "Info om appen" + + "installera genvägar" "Tillåter att en app lägger till genvägar utan åtgärd från användaren." "läsa inställningar och genvägar för startsidan" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 3de28e011d..eb2845dab9 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -47,6 +47,8 @@ "Ondoa" "Ondoa" "Maelezo ya programu" + + "kuweka njia za mkato" "Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati." "soma mipangilio ya Mwanzo na njia za mkato" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 1d92581a05..c3401aa558 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -46,7 +46,9 @@ "முகப்பு" "அகற்று" "நிறுவல் நீக்கு" - "பயன்பாட்டுத் தகவல்" + "ஆப்ஸ் தகவல்" + + "குறுக்குவழிகளை நிறுவுதல்" "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது." "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்" @@ -77,7 +79,7 @@ "மொபைலைச் சுழற்றும் போது" "தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை" "அறிவிப்புப் புள்ளிகள்" - "இயக்கப்பட்டுள்ளது" + "ஆன்" "முடக்கப்பட்டுள்ளது" "அறிவிப்பிற்கான அணுகல் தேவை" "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 6c6669938b..79314e10f2 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -26,7 +26,7 @@ "యాప్ అందుబాటులో లేదు" "డౌన్‌లోడ్ చేసిన యాప్ సురక్షిత మోడ్‌లో నిలిపివేయబడింది" "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" - "సత్వరమార్గం అందుబాటులో లేదు" + "షార్ట్‌కట్ అందుబాటులో లేదు" "హోమ్ స్క్రీన్" "అనుకూల చర్యలు" "విడ్జెట్‌ను ఎంచుకోవడానికి తాకి & నొక్కి పెట్టండి." @@ -38,7 +38,7 @@ "అప్లికేషన్‌లను శోధించండి" "అప్లికేషన్‌లను లోడ్ చేస్తోంది…" "\"%1$s\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు" - "మరిన్ని అనువర్తనాల కోసం శోధించు" + "మరిన్ని యాప్‌ల కోసం వెతుకు" "నోటిఫికేషన్‌లు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." "ఇష్టమైనవి ట్రేలో ఖాళీ లేదు" @@ -46,7 +46,9 @@ "హోమ్" "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" - "అనువర్తన సమాచారం" + "యాప్ సమాచారం" + + "సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం" "వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం" @@ -83,7 +85,7 @@ "నోటిఫికేషన్ డాట్‌లను చూపించడానికి %1$sకు యాప్ నోటిఫికేషన్‌లను ఆన్ చేయండి" "సెట్టింగ్‌లను మార్చు" "హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు" - "కొత్త అనువర్తనాల కోసం" + "కొత్త యాప్‌ల కోసం" "చిహ్న ఆకారాన్ని మార్చు" "సిస్టమ్ డిఫాల్ట్‌ను ఉపయోగించండి" "చతురస్రం" @@ -93,7 +95,7 @@ "చిహ్న ఆకార మార్పులను వర్తింపజేస్తోంది" "తెలియదు" "తీసివేయి" - "శోధించు" + "వెతుకు" "ఈ యాప్ ఇన్‌స్టాల్ చేయబడలేదు" "ఈ చిహ్నం యొక్క యాప్ ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ యాప్ కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." "%1$s డౌన్‌లోడ్ అవుతోంది, %2$s పూర్తయింది" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index b370099889..10c0fa2d81 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -47,6 +47,8 @@ "นำออก" "ถอนการติดตั้ง" "ข้อมูลแอป" + + "ติดตั้งทางลัด" "อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ" "อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 54135c9d58..3a328aa603 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -47,6 +47,8 @@ "Alisin" "I-uninstall" "Impormasyon ng app" + + "i-install ang mga shortcut" "Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user." "basahin ang mga setting at shortcut ng Home" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index cb8b50af20..5c2d68157a 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -47,6 +47,8 @@ "Kaldır" "Yüklemeyi kaldır" "Uygulama bilgileri" + + "kısayolları yükle" "Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir." "Ana ekran ayarlarını ve kısayollarını oku" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index a9e010921f..8cb05ea8ee 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -47,6 +47,8 @@ "Видалити" "Видалити" "Про додаток" + + "створення ярликів" "Дозволяє програмі самостійно додавати ярлики." "читати налаштування та ярлики головного екрана" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index a704fab4ca..6011d51edf 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -47,6 +47,8 @@ "ہٹائیں" "اَن انسٹال کریں" "ایپ کی معلومات" + + "شارٹ کٹس انسٹال کریں" "کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔" "ہوم ترتیبات اور شارٹ کٹس کو پڑھیں" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 7e31889138..d289642195 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -47,6 +47,8 @@ "Olib tashlash" "O‘chirib tashlash" "Ilova haqida" + + "yorliqlar yaratish" "Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi." "Uy sozlamalari va yorliqlarini o‘qish" @@ -76,11 +78,11 @@ "Asosiy ekranni aylantirishga ruxsat berish" "Telefon burilganda" "Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi" - "Bildirishnoma nuqtalari" + "Bildirishnoma belgilari" "Yoniq" "O‘chiq" "Bildirishnomalarga ruxsat berilmagan" - "Bildirishnoma nuqtalarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" + "Bildirishnoma belgilarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" "Sozlamalarni o‘zgartirish" "Bosh ekranga ikonka qo‘shish" "Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 83e1ceabaa..b06646d253 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -47,6 +47,8 @@ "Xóa" "Gỡ cài đặt" "Thông tin ứng dụng" + + "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." "đọc cài đặt và lối tắt trên Màn hình chính" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index bef12fb61b..47e550668e 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -47,6 +47,8 @@ "移除" "卸载" "应用信息" + + "安装快捷方式" "允许应用自行添加快捷方式。" "读取主屏幕设置和快捷方式" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 7ac5553957..c5e09c5450 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -47,6 +47,8 @@ "移除" "解除安裝" "應用程式資料" + + "安裝捷徑" "允許應用程式無需使用者許可也可新增捷徑。" "讀取主畫面的設定和捷徑" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index e0c4c9973e..ae8df098b0 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -47,6 +47,8 @@ "移除" "解除安裝" "應用程式資訊" + + "安裝捷徑" "允許應用程式自動新增捷徑。" "讀取主螢幕的設定和捷徑" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index ef6fdeb10a..65e2561da2 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -47,6 +47,8 @@ "Susa" "Khipha" "Ulwazi lohlelo lokusebenza" + + "faka izinqamuleli" "Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi." "funda izilungiselelo zokuthi Ikhaya nezinqamuleli" From add3d8322dde223629f1653bed6098342a2d69f7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 8 Sep 2017 11:59:05 -0700 Subject: [PATCH 015/885] Removing some folder customization options The old folder preview and animation relied on creating bitmaps for transition. As we move to hardware bitmaps, creating custom bitmaps which rely on icon bitmaps would be costly (hardware bitmaps are immutable and cannot be drawn on a software canvas). Bug: 35428783 Change-Id: I39869ed44feb6a886985ad15775bc1ab55565727 --- res/values/config.xml | 5 - .../android/launcher3/config/BaseFlags.java | 2 - .../folder/ClippedFolderIconLayoutRule.java | 51 ++---- src/com/android/launcher3/folder/Folder.java | 152 ++---------------- .../folder/FolderAnimationManager.java | 23 +-- .../android/launcher3/folder/FolderIcon.java | 98 ++--------- .../folder/FolderIconPreviewVerifier.java | 9 +- .../launcher3/folder/PreviewImageView.java | 97 ----------- .../launcher3/folder/PreviewItemManager.java | 14 +- .../folder/StackFolderIconLayoutRule.java | 115 ------------- .../android/launcher3/model/LoaderTask.java | 5 +- 11 files changed, 64 insertions(+), 507 deletions(-) delete mode 100644 src/com/android/launcher3/folder/PreviewImageView.java delete mode 100644 src/com/android/launcher3/folder/StackFolderIconLayoutRule.java diff --git a/res/values/config.xml b/res/values/config.xml index 10b612ba2d..f8faf988ac 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -86,9 +86,7 @@ 500 - 120 200 - 60 30 @@ -128,9 +126,6 @@ - - - 150 80 diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index c88359b60f..5adeec1c6e 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -32,9 +32,7 @@ abstract class BaseFlags { // Custom flags go below this public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false; - public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false; public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false; - public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true; // When enabled allows to use any point on the fast scrollbar to start dragging. public static final boolean LAUNCHER3_DIRECT_SCROLL = true; // When enabled while all-apps open, the soft input will be set to adjust resize . diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java index f25345ee8d..5954efa95f 100644 --- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java +++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java @@ -1,9 +1,8 @@ package com.android.launcher3.folder; +public class ClippedFolderIconLayoutRule { -public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule { - - static final int MAX_NUM_ITEMS_IN_PREVIEW = 4; + public static final int MAX_NUM_ITEMS_IN_PREVIEW = 4; private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2; private static final float MIN_SCALE = 0.48f; @@ -11,8 +10,8 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule private static final float MAX_RADIUS_DILATION = 0.15f; private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f; - private static final int EXIT_INDEX = -2; - private static final int ENTER_INDEX = -3; + public static final int EXIT_INDEX = -2; + public static final int ENTER_INDEX = -3; private float[] mTmpPoint = new float[2]; @@ -22,7 +21,6 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule private boolean mIsRtl; private float mBaselineIconScale; - @Override public void init(int availableSpace, float intrinsicIconSize, boolean rtl) { mAvailableSpace = availableSpace; mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f; @@ -31,19 +29,18 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule mBaselineIconScale = availableSpace / (intrinsicIconSize * 1f); } - @Override public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, PreviewItemDrawingParams params) { - float totalScale = scaleForItem(index, curNumItems); + float totalScale = scaleForItem(curNumItems); float transX; float transY; float overlayAlpha = 0; - if (index == getExitIndex()) { + if (index == EXIT_INDEX) { // 0 1 * <-- Exit position (row 0, col 2) // 2 3 getGridPosition(0, 2, mTmpPoint); - } else if (index == getEnterIndex()) { + } else if (index == ENTER_INDEX) { // 0 1 // 2 3 * <-- Enter position (row 1, col 2) getGridPosition(1, 2, mTmpPoint); @@ -120,7 +117,7 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule MIN_NUM_ITEMS_IN_PREVIEW) / (MAX_NUM_ITEMS_IN_PREVIEW - MIN_NUM_ITEMS_IN_PREVIEW)); double theta = theta0 + index * (2 * Math.PI / curNumItems) * direction; - float halfIconSize = (mIconSize * scaleForItem(index, curNumItems)) / 2; + float halfIconSize = (mIconSize * scaleForItem(curNumItems)) / 2; // Map the location along the circle, and offset the coordinates to represent the center // of the icon, and to be based from the top / left of the preview area. The y component @@ -130,10 +127,9 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule } - @Override - public float scaleForItem(int index, int numItems) { + public float scaleForItem(int numItems) { // Scale is determined by the number of items in the preview. - float scale = 1f; + final float scale; if (numItems <= 2) { scale = MAX_SCALE; } else if (numItems == 3) { @@ -141,37 +137,10 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule } else { scale = MIN_SCALE; } - return scale * mBaselineIconScale; } - @Override public float getIconSize() { return mIconSize; } - - @Override - public int maxNumItems() { - return MAX_NUM_ITEMS_IN_PREVIEW; - } - - @Override - public boolean clipToBackground() { - return true; - } - - @Override - public boolean hasEnterExitIndices() { - return true; - } - - @Override - public int getExitIndex() { - return EXIT_INDEX; - } - - @Override - public int getEnterIndex() { - return ENTER_INDEX; - } } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 85792d4cc9..64a2dabab5 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -19,8 +19,6 @@ package com.android.launcher3.folder; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; @@ -37,7 +35,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AccelerateInterpolator; import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.widget.TextView; @@ -55,9 +52,7 @@ import com.android.launcher3.FolderInfo; import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.LogDecelerateInterpolator; import com.android.launcher3.OnAlarmListener; import com.android.launcher3.PagedView; import com.android.launcher3.R; @@ -66,8 +61,6 @@ import com.android.launcher3.UninstallDropTarget.DropTargetSource; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; -import com.android.launcher3.anim.AnimationLayerSet; -import com.android.launcher3.anim.CircleRevealOutlineProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; @@ -137,10 +130,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC private AnimatorSet mCurrentAnimator; - private final int mExpandDuration; - public final int mMaterialExpandDuration; - private final int mMaterialExpandStagger; - protected final Launcher mLauncher; protected DragController mDragController; public FolderInfo mInfo; @@ -201,9 +190,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC super(context, attrs); setAlwaysDrawnWithCacheEnabled(false); Resources res = getResources(); - mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration); - mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration); - mMaterialExpandStagger = res.getInteger(R.integer.config_materialFolderExpandStagger); if (sDefaultFolderName == null) { sDefaultFolderName = res.getString(R.string.folder_name); @@ -487,25 +473,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC ? R.layout.user_folder : R.layout.user_folder_icon_normalized, null); } - /** - * This method is intended to make the UserFolder to be visually identical in size and position - * to its associated FolderIcon. This allows for a seamless transition into the expanded state. - */ - private void positionAndSizeAsIcon() { - if (!(getParent() instanceof DragLayer)) return; - setScaleX(0.8f); - setScaleY(0.8f); - setAlpha(0f); - mState = STATE_SMALL; - } - - private void prepareReveal() { - setScaleX(1f); - setScaleY(1f); - setAlpha(1f); - mState = STATE_SMALL; - } - private void startAnimation(final AnimatorSet a) { if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) { mCurrentAnimator.cancel(); @@ -525,61 +492,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC a.start(); } - private AnimatorSet getOpeningAnimator() { - prepareReveal(); - mFolderIcon.growAndFadeOut(); - - AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); - - int width = getFolderWidth(); - int height = getFolderHeight(); - - float transX = - 0.075f * (width / 2 - getPivotX()); - float transY = - 0.075f * (height / 2 - getPivotY()); - setTranslationX(transX); - setTranslationY(transY); - PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0); - PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0); - - Animator drift = ObjectAnimator.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); - - Animator reveal = new CircleRevealOutlineProvider((int) getPivotX(), - (int) getPivotY(), 0, radius).createRevealAnimator(this); - reveal.setDuration(mMaterialExpandDuration); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - - mContent.setAlpha(0f); - Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f); - iconsAlpha.setDuration(mMaterialExpandDuration); - iconsAlpha.setStartDelay(mMaterialExpandStagger); - iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - - mFooter.setAlpha(0f); - Animator textAlpha = ObjectAnimator.ofFloat(mFooter, "alpha", 0f, 1f); - textAlpha.setDuration(mMaterialExpandDuration); - textAlpha.setStartDelay(mMaterialExpandStagger); - textAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - - anim.play(drift); - anim.play(iconsAlpha); - anim.play(textAlpha); - anim.play(reveal); - - AnimationLayerSet layerSet = new AnimationLayerSet(); - layerSet.addView(mContent); - layerSet.addView(mFooter); - anim.addListener(layerSet); - - return anim; - } - /** * Opens the user folder described by the specified tag. The opening of the folder * is animated relative to the specified View. If the View is null, no animation @@ -621,9 +533,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC final Runnable onCompleteRunnable; centerAboutIcon(); - AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION - ? new FolderAnimationManager(this, true /* isOpening */).getAnimator() - : getOpeningAnimator(); + AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator(); onCompleteRunnable = new Runnable() { @Override public void run() { @@ -633,12 +543,8 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) { - mFolderIcon.setBackgroundVisible(false); - mFolderIcon.drawLeaveBehindIfExists(); - } else { - mFolderIcon.setVisibility(INVISIBLE); - } + mFolderIcon.setBackgroundVisible(false); + mFolderIcon.drawLeaveBehindIfExists(); Utilities.sendCustomAccessibilityEvent( Folder.this, @@ -728,11 +634,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } if (mFolderIcon != null) { - if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) { - mFolderIcon.clearLeaveBehindIfExists(); - } else { - mFolderIcon.shrinkAndFadeIn(animate); - } + mFolderIcon.clearLeaveBehindIfExists(); } if (!(getParent() instanceof DragLayer)) return; @@ -749,21 +651,8 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } - private AnimatorSet getClosingAnimator() { - AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet(); - animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f)); - - AnimationLayerSet layerSet = new AnimationLayerSet(); - layerSet.addView(this); - animatorSet.addListener(layerSet); - animatorSet.setDuration(mExpandDuration); - return animatorSet; - } - private void animateClosed() { - AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION - ? new FolderAnimationManager(this, false /* isOpening */).getAnimator() - : getClosingAnimator(); + AnimatorSet a = new FolderAnimationManager(this, false /* isOpening */).getAnimator(); a.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -790,16 +679,12 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC clearFocus(); if (mFolderIcon != null) { mFolderIcon.setVisibility(View.VISIBLE); - if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) { - mFolderIcon.setBackgroundVisible(true); - mFolderIcon.mFolderName.setTextVisibility(true); - } + mFolderIcon.setBackgroundVisible(true); + mFolderIcon.mFolderName.setTextVisibility(true); if (wasAnimated) { - if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) { - mFolderIcon.mBackground.fadeInBackgroundShadow(); - mFolderIcon.mBackground.animateBackgroundStroke(); - mFolderIcon.onFolderClose(mContent.getCurrentPage()); - } + mFolderIcon.mBackground.fadeInBackgroundShadow(); + mFolderIcon.mBackground.animateBackgroundStroke(); + mFolderIcon.onFolderClose(mContent.getCurrentPage()); if (mFolderIcon.hasBadge()) { mFolderIcon.createBadgeScaleAnimator(0f, 1f).start(); } @@ -852,18 +737,14 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } - @Override - public void onDragOver(DragObject d) { - 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()); } - @Thunk void onDragOver(DragObject d, int reorderDelay) { + @Override + public void onDragOver(DragObject d) { if (mScrollPauseAlarm.alarmPending()) { return; } @@ -1095,10 +976,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } public boolean isDropEnabled() { - if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) { - return mState != STATE_ANIMATING; - } - return true; + return mState != STATE_ANIMATING; } public boolean isFull() { @@ -1113,7 +991,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC int width = getFolderWidth(); int height = getFolderHeight(); - float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect); + parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect); int centerX = sTempRect.centerX(); int centerY = sTempRect.centerY(); int centeredLeft = centerX - width / 2; @@ -1611,7 +1489,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC @Override public void onAlarm(Alarm alarm) { // Reorder immediately on page change. - onDragOver(mDragObject, 1); + onDragOver(mDragObject); } } diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 69705d5942..cdb0ce3670 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -22,6 +22,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.content.Context; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; @@ -44,6 +45,8 @@ import com.android.launcher3.util.Themes; import java.util.List; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; + /** * Manages the opening and closing animations for a {@link Folder}. * @@ -101,8 +104,9 @@ public class FolderAnimationManager { mIsOpening = isOpening; - mDuration = mFolder.mMaterialExpandDuration; - mDelay = mContext.getResources().getInteger(R.integer.config_folderDelay); + Resources res = mContent.getResources(); + mDuration = res.getInteger(R.integer.config_materialFolderExpandDuration); + mDelay = res.getInteger(R.integer.config_folderDelay); mFolderInterpolator = AnimationUtils.loadInterpolator(mContext, R.interpolator.folder_interpolator); @@ -118,7 +122,7 @@ public class FolderAnimationManager { */ public AnimatorSet getAnimator() { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams(); - FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule(); + ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); final List itemsInPreview = mFolderIcon.getPreviewItems(); // Match position of the FolderIcon @@ -129,7 +133,7 @@ public class FolderAnimationManager { float initialSize = (scaledRadius * 2) * scaleRelativeToDragLayer; // Match size/scale of icons in the preview - float previewScale = rule.scaleForItem(0, itemsInPreview.size()); + float previewScale = rule.scaleForItem(itemsInPreview.size()); float previewSize = rule.getIconSize() * previewScale; float initialScale = previewSize / itemsInPreview.get(0).getIconSize() * scaleRelativeToDragLayer; @@ -242,15 +246,14 @@ public class FolderAnimationManager { */ private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale, int previewItemOffsetX, int previewItemOffsetY) { - FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule(); + ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0; final List itemsInPreview = isOnFirstPage ? mFolderIcon.getPreviewItems() : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage()); final int numItemsInPreview = itemsInPreview.size(); final int numItemsInFirstPagePreview = isOnFirstPage - ? numItemsInPreview - : FolderIcon.NUM_ITEMS_IN_PREVIEW; + ? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW; TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator(); @@ -264,7 +267,7 @@ public class FolderAnimationManager { cwc.setupLp(btv); // Match scale of icons in the preview of the items on the first page. - float previewScale = rule.scaleForItem(i, numItemsInFirstPagePreview); + float previewScale = rule.scaleForItem(numItemsInFirstPagePreview); float previewSize = rule.getIconSize() * previewScale; float iconScale = previewSize / itemsInPreview.get(i).getIconSize(); @@ -299,7 +302,7 @@ public class FolderAnimationManager { scaleAnimator.setInterpolator(previewItemInterpolator); play(animatorSet, scaleAnimator); - if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) { + if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) { // These delays allows the preview items to move as part of the Folder's motion, // and its only necessary for large folders because of differing interpolators. int delay = mIsOpening ? mDelay : mDelay * 2; @@ -349,7 +352,7 @@ public class FolderAnimationManager { } private TimeInterpolator getPreviewItemInterpolator() { - if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) { + if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) { // With larger folders, we want the preview items to reach their final positions faster // (when opening) and later (when closing) so that they appear aligned with the rest of // the folder items when they are both visible. diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 8339bc5b8c..bb0a726268 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -17,7 +17,6 @@ package com.android.launcher3.folder; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; @@ -49,7 +48,6 @@ import com.android.launcher3.FolderInfo; import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherSettings; import com.android.launcher3.OnAlarmListener; import com.android.launcher3.R; @@ -60,7 +58,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.badge.FolderBadgeInfo; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.BaseItemDragListener; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; @@ -71,6 +68,7 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; import java.util.List; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION; /** @@ -82,10 +80,6 @@ public class FolderIcon extends FrameLayout implements FolderListener { private FolderInfo mInfo; @Thunk static boolean sStaticValuesDirty = true; - public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ? - StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW : - ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; - private CheckLongPressHelper mLongPressHelper; private StylusEventHelper mStylusEventHelper; @@ -103,7 +97,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private boolean mBackgroundIsVisible = true; FolderIconPreviewVerifier mPreviewVerifier; - PreviewLayoutRule mPreviewLayoutRule; + ClippedFolderIconLayoutRule mPreviewLayoutRule; private PreviewItemManager mPreviewItemManager; private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); @@ -146,9 +140,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void init() { mLongPressHelper = new CheckLongPressHelper(this); mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); - mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ? - new StackFolderIconLayoutRule() : - new ClippedFolderIconLayoutRule(); + mPreviewLayoutRule = new ClippedFolderIconLayoutRule(); mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); mPreviewItemManager = new PreviewItemManager(this); } @@ -314,8 +306,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } boolean itemAdded = false; - if (index >= mPreviewLayoutRule.maxNumItems() - && mPreviewLayoutRule.hasEnterExitIndices()) { + if (index >= MAX_NUM_ITEMS_IN_PREVIEW) { List oldPreviewItems = getPreviewItemsOnPage(0); addItem(item, false); List newPreviewItems = getPreviewItemsOnPage(0); @@ -347,7 +338,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { to.offset(center[0] - animateView.getMeasuredWidth() / 2, center[1] - animateView.getMeasuredHeight() / 2); - float finalAlpha = index < mPreviewLayoutRule.maxNumItems() ? 0.5f : 0f; + float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; float finalScale = scale * scaleRelativeToDragLayer; dragLayer.animateView(animateView, from, to, finalAlpha, @@ -391,7 +382,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { mBadgeInfo = badgeInfo; } - public PreviewLayoutRule getLayoutRule() { + public ClippedFolderIconLayoutRule getLayoutRule() { return mPreviewLayoutRule; } @@ -420,7 +411,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private float getLocalCenterForIndex(int index, int curNumItems, int[] center) { mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams( - Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams); + Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index), curNumItems, mTmpParams); mTmpParams.transX += mBackground.basePreviewOffsetX; mTmpParams.transY += mBackground.basePreviewOffsetY; @@ -474,19 +465,17 @@ public class FolderIcon extends FrameLayout implements FolderListener { Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); } else { saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); - if (mPreviewLayoutRule.clipToBackground()) { - canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT); - } + canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT); } mPreviewItemManager.draw(canvas); - if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) { + if (canvas.isHardwareAccelerated()) { mBackground.clipCanvasHardware(canvas); } canvas.restoreToCount(saveCount); - if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) { + if (!mBackground.drawingDelegated()) { mBackground.drawBackgroundStroke(canvas); } @@ -542,7 +531,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { itemsToDisplay.add(itemsOnPage.get(rank)); } - if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) { + if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) { break; } } @@ -631,30 +620,6 @@ public class FolderIcon extends FrameLayout implements FolderListener { mInfo.removeListener(mFolder); } - public void shrinkAndFadeIn(boolean animate) { - // We remove and re-draw the FolderIcon in-case it has changed - final PreviewImageView previewImage = PreviewImageView.get(getContext()); - previewImage.removeFromParent(); - copyToPreview(previewImage); - - clearLeaveBehindIfExists(); - - ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1); - oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); - oa.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Remove the ImageView copy of the FolderIcon and make the original visible. - previewImage.removeFromParent(); - setVisibility(View.VISIBLE); - } - }); - oa.start(); - if (!animate) { - oa.end(); - } - } - public void clearLeaveBehindIfExists() { ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { @@ -673,48 +638,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } } - public void growAndFadeOut() { - drawLeaveBehindIfExists(); - - // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original - PreviewImageView previewImage = PreviewImageView.get(getContext()); - copyToPreview(previewImage); - setVisibility(View.INVISIBLE); - - ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 0, 1.5f, 1.5f); - oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); - oa.start(); - } - - /** - * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView - * in the DragLayer in the exact absolute location of the original FolderIcon. - */ - private void copyToPreview(PreviewImageView previewImageView) { - previewImageView.copy(this); - if (mFolder != null) { - previewImageView.setPivotX(mFolder.getPivotXForIconAnimation()); - previewImageView.setPivotY(mFolder.getPivotYForIconAnimation()); - mFolder.bringToFront(); - } - } - public void onFolderClose(int currentPage) { mPreviewItemManager.onFolderClose(currentPage); } - - interface PreviewLayoutRule { - PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, - PreviewItemDrawingParams params); - void init(int availableSpace, float intrinsicIconSize, boolean rtl); - float scaleForItem(int index, int totalNumItems); - float getIconSize(); - int maxNumItems(); - boolean clipToBackground(); - - boolean hasEnterExitIndices(); - int getExitIndex(); - int getEnterIndex(); - - } } diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java index d054a5d424..5a27cd43e2 100644 --- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java +++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java @@ -18,7 +18,8 @@ package com.android.launcher3.folder; import com.android.launcher3.FolderInfo; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.config.FeatureFlags; + +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; /** * Verifies whether an item in a Folder is displayed in the FolderIcon preview. @@ -45,9 +46,7 @@ public class FolderIconPreviewVerifier { mMaxGridCountY, mMaxItemsPerPage, mGridSize); mGridCountX = mGridSize[0]; - mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION - && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON - && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW; + mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW; } /** @@ -70,6 +69,6 @@ public class FolderIconPreviewVerifier { int row = rank / mGridCountX; return col < 2 && row < 2; } - return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW; + return rank < MAX_NUM_ITEMS_IN_PREVIEW; } } diff --git a/src/com/android/launcher3/folder/PreviewImageView.java b/src/com/android/launcher3/folder/PreviewImageView.java deleted file mode 100644 index 65d9db1180..0000000000 --- a/src/com/android/launcher3/folder/PreviewImageView.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2016 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.folder; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.view.View; -import android.widget.ImageView; - -import com.android.launcher3.Launcher; -import com.android.launcher3.R; -import com.android.launcher3.dragndrop.DragLayer; - -/** - * A temporary view which displays the a bitmap (used for folder icon animation) - */ -public class PreviewImageView extends ImageView { - - private final Rect mTempRect = new Rect(); - private final DragLayer mParent; - - private Bitmap mBitmap; - private Canvas mCanvas; - - public PreviewImageView(DragLayer parent) { - super(parent.getContext()); - mParent = parent; - } - - public void copy(View view) { - final int width = view.getMeasuredWidth(); - final int height = view.getMeasuredHeight(); - - if (mBitmap == null || mBitmap.getWidth() != width || mBitmap.getHeight() != height) { - mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mCanvas = new Canvas(mBitmap); - } - - DragLayer.LayoutParams lp; - if (getLayoutParams() instanceof DragLayer.LayoutParams) { - lp = (DragLayer.LayoutParams) getLayoutParams(); - } else { - lp = new DragLayer.LayoutParams(width, height); - } - - // The layout from which the folder is being opened may be scaled, adjust the starting - // view size by this scale factor. - float scale = mParent.getDescendantRectRelativeToSelf(view, mTempRect); - lp.customPosition = true; - lp.x = mTempRect.left; - lp.y = mTempRect.top; - lp.width = (int) (scale * width); - lp.height = (int) (scale * height); - - mCanvas.drawColor(0, PorterDuff.Mode.CLEAR); - view.draw(mCanvas); - setImageBitmap(mBitmap); - - // Just in case this image view is still in the drag layer from a previous animation, - // we remove it and re-add it. - removeFromParent(); - mParent.addView(this, lp); - } - - public void removeFromParent() { - if (mParent.indexOfChild(this) != -1) { - mParent.removeView(this); - } - } - - public static PreviewImageView get(Context context) { - DragLayer dragLayer = Launcher.getLauncher(context).getDragLayer(); - PreviewImageView view = (PreviewImageView) dragLayer.getTag(R.id.preview_image_id); - if (view == null) { - view = new PreviewImageView(dragLayer); - dragLayer.setTag(R.id.preview_image_id, view); - } - return view; - } -} diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 2d979a6619..5d400100fd 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -29,11 +29,13 @@ import android.widget.TextView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import java.util.ArrayList; import java.util.List; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION; /** @@ -202,7 +204,7 @@ public class PreviewItemManager { params.add(new PreviewItemDrawingParams(0, 0, 0, 0)); } - int numItemsInFirstPagePreview = page == 0 ? items.size() : FolderIcon.NUM_ITEMS_IN_PREVIEW; + int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_NUM_ITEMS_IN_PREVIEW; for (int i = 0; i < params.size(); i++) { PreviewItemDrawingParams p = params.get(i); p.drawable = items.get(i).getCompoundDrawables()[1]; @@ -213,7 +215,7 @@ public class PreviewItemManager { p.drawable.setCallback(mIcon); } - if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) { + if (!animate) { computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p); if (mReferenceDrawable == null) { mReferenceDrawable = p.drawable; @@ -308,7 +310,7 @@ public class PreviewItemManager { int prevIndex = newParams.indexOf(moveIn.get(i)); PreviewItemDrawingParams p = params.get(prevIndex); computePreviewItemDrawingParams(prevIndex, numItems, p); - updateTransitionParam(p, moveIn.get(i), mIcon.mPreviewLayoutRule.getEnterIndex(), + updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i))); } @@ -328,7 +330,7 @@ public class PreviewItemManager { BubbleTextView item = moveOut.get(i); int oldIndex = oldParams.indexOf(item); PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null); - updateTransitionParam(p, item, oldIndex, mIcon.mPreviewLayoutRule.getExitIndex()); + updateTransitionParam(p, item, oldIndex, EXIT_INDEX); params.add(0, p); // We want these items first so that they are on drawn last. } @@ -349,7 +351,7 @@ public class PreviewItemManager { } FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, - FolderIcon.NUM_ITEMS_IN_PREVIEW, newIndex, FolderIcon.NUM_ITEMS_IN_PREVIEW, + MAX_NUM_ITEMS_IN_PREVIEW, newIndex, MAX_NUM_ITEMS_IN_PREVIEW, DROP_IN_ANIMATION_DURATION, null); if (p.anim != null && !p.anim.hasEqualFinalState(anim)) { p.anim.cancel(); diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java deleted file mode 100644 index 7d10556d01..0000000000 --- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java +++ /dev/null @@ -1,115 +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.folder; - -public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule { - - static final int MAX_NUM_ITEMS_IN_PREVIEW = 3; - - // The degree to which the item in the back of the stack is scaled [0...1] - // (0 means it's not scaled at all, 1 means it's scaled to nothing) - private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; - - // The amount of vertical spread between items in the stack [0...1] - private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f; - - private float mBaselineIconScale; - private int mBaselineIconSize; - private int mAvailableSpaceInPreview; - private float mMaxPerspectiveShift; - - @Override - public void init(int availableSpace, float intrinsicIconSize, boolean rtl) { - mAvailableSpaceInPreview = availableSpace; - - // cos(45) = 0.707 + ~= 0.1) = 0.8f - int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f)); - - int unscaledHeight = (int) (intrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); - - mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight); - - mBaselineIconSize = (int) (intrinsicIconSize * mBaselineIconScale); - mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR; - } - - @Override - public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, - PreviewItemDrawingParams params) { - float scale = scaleForItem(index, curNumItems); - - index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1; - float r = (index * 1.0f) / (MAX_NUM_ITEMS_IN_PREVIEW - 1); - - float offset = (1 - r) * mMaxPerspectiveShift; - float scaledSize = scale * mBaselineIconSize; - float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize; - - // We want to imagine our coordinates from the bottom left, growing up and to the - // right. This is natural for the x-axis, but for the y-axis, we have to invert things. - float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection); - float transX = (mAvailableSpaceInPreview - scaledSize) / 2; - float totalScale = mBaselineIconScale * scale; - final float overlayAlpha = (80 * (1 - r)) / 255f; - - if (params == null) { - params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); - } else { - params.update(transX, transY, totalScale); - params.overlayAlpha = overlayAlpha; - } - return params; - } - - @Override - public int maxNumItems() { - return MAX_NUM_ITEMS_IN_PREVIEW; - } - - @Override - public float getIconSize() { - return mBaselineIconSize; - } - - @Override - public float scaleForItem(int index, int numItems) { - // Scale is determined by the position of the icon in the preview. - index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1; - float r = (index * 1.0f) / (MAX_NUM_ITEMS_IN_PREVIEW - 1); - return (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); - } - - @Override - public boolean clipToBackground() { - return false; - } - - @Override - public boolean hasEnterExitIndices() { - return false; - } - - @Override - public int getExitIndex() { - throw new RuntimeException("hasEnterExitIndices not supported"); - } - - @Override - public int getEnterIndex() { - throw new RuntimeException("hasEnterExitIndices not supported"); - } -} diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 4756edcc03..e1b208a059 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -53,7 +53,6 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; -import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.logging.FileLog; @@ -76,6 +75,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CancellationException; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; + /** * Runnable for the thread that loads the contents of the launcher: * - workspace icons @@ -734,7 +735,7 @@ public class LoaderTask implements Runnable { numItemsInPreview++; } - if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) { + if (numItemsInPreview >= MAX_NUM_ITEMS_IN_PREVIEW) { break; } } From 7ce471bdf6d77c9d5778810d9ea033313c579067 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 2 Aug 2017 03:37:39 -0700 Subject: [PATCH 016/885] Removing support for CustomContent screen The supported way to customize Launcher and add a "-1 page" is by using LauncherOverlays. Custom content screen is no longer supported. Change-Id: I3f8a2734c287d1a69ae0c038ec1de1d45fa1b464 --- src/com/android/launcher3/CellLayout.java | 51 +-- src/com/android/launcher3/FocusHelper.java | 14 - src/com/android/launcher3/Launcher.java | 150 +------ .../android/launcher3/LauncherCallbacks.java | 7 - .../launcher3/PinchToOverviewListener.java | 2 +- .../launcher3/ShortcutAndWidgetContainer.java | 33 +- src/com/android/launcher3/Workspace.java | 371 ++---------------- .../WorkspaceStateTransitionAnimation.java | 5 +- .../LauncherAccessibilityDelegate.java | 2 +- .../OverviewScreenAccessibilityDelegate.java | 2 +- .../launcher3/testing/LauncherExtension.java | 53 --- .../util/WallpaperOffsetInterpolator.java | 16 +- 12 files changed, 65 insertions(+), 641 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 3ebccda9e7..f835748088 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -89,8 +89,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private int mCountY; private boolean mDropPending = false; - private boolean mIsDragTarget = true; - private boolean mJailContent = true; // 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. @@ -404,14 +402,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - void disableDragTarget() { - mIsDragTarget = false; - } - - public boolean isDragTarget() { - return mIsDragTarget; - } - void setIsDragOverlapping(boolean isDragOverlapping) { if (mIsDragOverlapping != isDragOverlapping) { mIsDragOverlapping = isDragOverlapping; @@ -421,26 +411,22 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - public void disableJailContent() { - mJailContent = false; - } - @Override protected void dispatchSaveInstanceState(SparseArray container) { - if (mJailContent) { - ParcelableSparseArray jail = getJailedArray(container); - super.dispatchSaveInstanceState(jail); - container.put(R.id.cell_layout_jail_id, jail); - } else { - super.dispatchSaveInstanceState(container); - } + ParcelableSparseArray jail = getJailedArray(container); + super.dispatchSaveInstanceState(jail); + container.put(R.id.cell_layout_jail_id, jail); } @Override protected void dispatchRestoreInstanceState(SparseArray container) { - super.dispatchRestoreInstanceState(mJailContent ? getJailedArray(container) : container); + super.dispatchRestoreInstanceState(getJailedArray(container)); } + /** + * Wrap the SparseArray in another Parcelable so that the item ids do not conflict with our + * our internal resource ids + */ private ParcelableSparseArray getJailedArray(SparseArray container) { final Parcelable parcelable = container.get(R.id.cell_layout_jail_id); return parcelable instanceof ParcelableSparseArray ? @@ -453,10 +439,6 @@ 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 @@ -838,16 +820,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { - boolean isFullscreen = mShortcutsAndWidgets.getChildCount() > 0 && - ((LayoutParams) mShortcutsAndWidgets.getChildAt(0).getLayoutParams()).isFullscreen; int left = getPaddingLeft(); - if (!isFullscreen) { - left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f); - } + left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f); int right = r - l - getPaddingRight(); - if (!isFullscreen) { - right -= (int) Math.ceil(getUnusedHorizontalSpace() / 2f); - } + right -= (int) Math.ceil(getUnusedHorizontalSpace() / 2f); int top = getPaddingTop(); int bottom = b - t - getPaddingBottom(); @@ -888,7 +864,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { @Override protected boolean verifyDrawable(Drawable who) { - return super.verifyDrawable(who) || (mIsDragTarget && who == mBackground); + return super.verifyDrawable(who) || (who == mBackground); } public void setShortcutAndWidgetAlpha(float alpha) { @@ -2653,11 +2629,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { */ public boolean isLockedToGrid = true; - /** - * Indicates that this item should use the full extents of its parent. - */ - public boolean isFullscreen = false; - /** * Indicates whether this item can be reordered. Always true except in the case of the * the AllApps button and QSB place holder. diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index fe7acda17b..1f18ea1ac8 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -288,25 +288,11 @@ public class FocusHelper { case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN: // Go to the previous page but keep the focus on the same hotseat icon. workspace.snapToPage(pageIndex - 1); - // If the page we are going to is fullscreen, have it take the focus from hotseat. - CellLayout prevPage = (CellLayout) workspace.getPageAt(pageIndex - 1); - boolean isPrevPageFullscreen = ((CellLayout.LayoutParams) prevPage - .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen; - if (isPrevPageFullscreen) { - workspace.getPageAt(pageIndex - 1).requestFocus(); - } break; case FocusLogic.NEXT_PAGE_LEFT_COLUMN: case FocusLogic.NEXT_PAGE_RIGHT_COLUMN: // Go to the next page but keep the focus on the same hotseat icon. workspace.snapToPage(pageIndex + 1); - // If the page we are going to is fullscreen, have it take the focus from hotseat. - CellLayout nextPage = (CellLayout) workspace.getPageAt(pageIndex + 1); - boolean isNextPageFullscreen = ((CellLayout.LayoutParams) nextPage - .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen; - if (isNextPageFullscreen) { - workspace.getPageAt(pageIndex + 1).requestFocus(); - } break; } if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 60d2e81f3f..c22a04aa94 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -291,8 +291,6 @@ public class Launcher extends BaseActivity // it from the context. private SharedPreferences mSharedPrefs; - private boolean mMoveToDefaultScreenFromNewIntent; - // 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. @@ -420,8 +418,6 @@ public class Launcher extends BaseActivity ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE)) .addAccessibilityStateChangeListener(this); - lockAllApps(); - restoreState(savedInstanceState); if (LauncherAppState.PROFILE_STARTUP) { @@ -508,10 +504,6 @@ public class Launcher extends BaseActivity mExtractedColors.notifyChange(); } - public ExtractedColors getExtractedColors() { - return mExtractedColors; - } - @Override public void onAppWidgetHostReset() { if (mAppWidgetHost != null) { @@ -564,44 +556,6 @@ public class Launcher extends BaseActivity } } - /** To be overridden by subclasses to hint to Launcher that we have custom content */ - protected boolean hasCustomContentToLeft() { - if (mLauncherCallbacks != null) { - return mLauncherCallbacks.hasCustomContentToLeft(); - } - return false; - } - - /** - * To be overridden by subclasses to populate the custom content container and call - * {@link #addToCustomContentPage}. This will only be invoked if - * {@link #hasCustomContentToLeft()} is {@code true}. - */ - protected void populateCustomContentContainer() { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.populateCustomContentContainer(); - } - } - - /** - * Invoked by subclasses to signal a change to the {@link #addToCustomContentPage} value to - * ensure the custom content page is added or removed if necessary. - */ - protected void invalidateHasCustomContentToLeft() { - if (mWorkspace == null || mWorkspace.getScreenOrder().isEmpty()) { - // Not bound yet, wait for bindScreens to be called. - return; - } - - if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) { - // Create the custom content page and call the subclass to populate it. - mWorkspace.createCustomContentContainer(); - populateCustomContentContainer(); - } else if (mWorkspace.hasCustomContent() && !hasCustomContentToLeft()) { - mWorkspace.removeCustomContentPage(); - } - } - 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(). @@ -801,7 +755,7 @@ public class Launcher extends BaseActivity } } - /** @Override for MNC */ + @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { PendingRequestArgs pendingArgs = mPendingRequestArgs; @@ -1017,21 +971,6 @@ public class Launcher extends BaseActivity Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime)); } - // We want to suppress callbacks about CustomContent being shown if we have just received - // onNewIntent while the user was present within launcher. In that case, we post a call - // to move the user to the main screen (which will occur after onResume). We don't want to - // have onHide (from onPause), then onShow, then onHide again, which we get if we don't - // suppress here. - if (mWorkspace.getCustomContentCallbacks() != null - && !mMoveToDefaultScreenFromNewIntent) { - // If we are resuming and the custom content is the current page, we call onShow(). - // It is also possible that onShow will instead be called slightly after first layout - // if PagedView#setRestorePage was set to the custom content page in onCreate(). - if (mWorkspace.isOnOrMovingToCustomContent()) { - mWorkspace.getCustomContentCallbacks().onShow(true); - } - } - mMoveToDefaultScreenFromNewIntent = false; updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); mWorkspace.onResume(); @@ -1061,32 +1000,11 @@ public class Launcher extends BaseActivity mDragController.cancelDrag(); mDragController.resetLastGestureUpTime(); - // We call onHide() aggressively. The custom content callbacks should be able to - // debounce excess onHide calls. - if (mWorkspace.getCustomContentCallbacks() != null) { - mWorkspace.getCustomContentCallbacks().onHide(); - } - if (mLauncherCallbacks != null) { mLauncherCallbacks.onPause(); } } - public interface CustomContentCallbacks { - // Custom content is completely shown. {@code fromResume} indicates whether this was caused - // by a onResume or by scrolling otherwise. - void onShow(boolean fromResume); - - // Custom content is completely hidden - void onHide(); - - // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing). - void onScrollProgressChanged(float progress); - - // Indicates whether the user is allowed to scroll away from the custom content. - boolean isScrollingAllowed(); - } - public interface LauncherOverlay { /** @@ -1136,16 +1054,6 @@ public class Launcher extends BaseActivity } } - public void addToCustomContentPage(View customContent, - CustomContentCallbacks callbacks, String description) { - mWorkspace.addToCustomContentPage(customContent, callbacks, description); - } - - // The custom content needs to offset its content to account for the QSB - public int getTopOffsetForCustomContent() { - return mWorkspace.getPaddingTop(); - } - @Override public Object onRetainNonConfigurationInstance() { // Flag the loader to stop early before switching @@ -1205,7 +1113,7 @@ public class Launcher extends BaseActivity public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU) { // Ignore the menu key if we are currently dragging or are on the custom content screen - if (!isOnCustomContent() && !mDragController.isDragging()) { + if (!mDragController.isDragging()) { // Close any open floating view AbstractFloatingView.closeAllOpenViews(this); @@ -1765,14 +1673,11 @@ public class Launcher extends BaseActivity if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive() && callbackAllowsMoveToDefaultScreen) { - // We use this flag to suppress noisy callbacks above custom content state - // from onResume. - mMoveToDefaultScreenFromNewIntent = true; mWorkspace.post(new Runnable() { @Override public void run() { if (mWorkspace != null) { - mWorkspace.moveToDefaultScreen(true); + mWorkspace.moveToDefaultScreen(); } } }); @@ -1795,8 +1700,7 @@ public class Launcher extends BaseActivity @Override protected void onSaveInstanceState(Bundle outState) { if (mWorkspace.getChildCount() > 0) { - outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, - mWorkspace.getCurrentPageOffsetFromCustomContent()); + outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); } super.onSaveInstanceState(outState); @@ -1952,10 +1856,6 @@ public class Launcher extends BaseActivity } } - public boolean isOnCustomContent() { - return mWorkspace.isOnOrMovingToCustomContent(); - } - @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); @@ -2028,12 +1928,6 @@ public class Launcher extends BaseActivity } } - protected void moveToCustomContentScreen(boolean animate) { - // Close any folders that may be open. - AbstractFloatingView.closeAllOpenViews(this, animate); - mWorkspace.moveToCustomContentScreen(animate); - } - public void addPendingItem(PendingAddItemInfo info, long container, long screenId, int[] cell, int spanX, int spanY) { info.container = container; @@ -2554,14 +2448,6 @@ public class Launcher extends BaseActivity mDragLayer.onAccessibilityStateChanged(enabled); } - public void onDragStarted() { - if (isOnCustomContent()) { - // Custom content screen doesn't participate in drag and drop. If on custom - // content screen, move to default. - moveWorkspaceToDefaultScreen(); - } - } - /** * Called when the user stops interacting with the launcher. * This implies that the user is now on the homescreen and is not doing housekeeping. @@ -3072,14 +2958,6 @@ public class Launcher extends BaseActivity } } - void lockAllApps() { - // TODO - } - - void unlockAllApps() { - // TODO - } - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); @@ -3219,13 +3097,6 @@ public class Launcher extends BaseActivity } bindAddScreens(orderedScreenIds); - // Create the custom content page (this call updates mDefaultScreen which calls - // setCurrentPage() so ensure that all pages are added before calling this). - if (hasCustomContentToLeft()) { - mWorkspace.createCustomContentContainer(); - populateCustomContentContainer(); - } - // After we have added all the screens, if the wallpaper was locked to the default state, // then notify to indicate that it can be released and a proper wallpaper offset can be // computed before the next layout @@ -3650,13 +3521,6 @@ public class Launcher extends BaseActivity return mDeviceProfile.isVerticalBarLayout(); } - public int getSearchBarHeight() { - if (mLauncherCallbacks != null) { - return mLauncherCallbacks.getSearchBarHeight(); - } - return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL; - } - /** * Add the icons for all apps. * @@ -3910,10 +3774,6 @@ public class Launcher extends BaseActivity return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false); } - protected void moveWorkspaceToDefaultScreen() { - mWorkspace.moveToDefaultScreen(false); - } - /** * $ adb shell dumpsys activity com.android.launcher3.Launcher [--all] */ @@ -3923,7 +3783,7 @@ public class Launcher extends BaseActivity if (args.length > 0 && TextUtils.equals(args[0], "--all")) { writer.println(prefix + "Workspace Items"); - for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) { + for (int i = 0; i < mWorkspace.getPageCount(); i++) { writer.println(prefix + " Homescreen " + i); ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets(); diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 66da046ef8..d1e2b621a3 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -81,10 +81,6 @@ public interface LauncherCallbacks { */ boolean startSearch( String initialQuery, boolean selectInitialQuery, Bundle appSearchData); - boolean hasCustomContentToLeft(); - void populateCustomContentContainer(); - View getQsbBar(); - Bundle getAdditionalSearchWidgetOptions(); /* * Extensions points for adding / replacing some other aspects of the Launcher experience. @@ -92,7 +88,4 @@ public interface LauncherCallbacks { boolean shouldMoveToDefaultScreenOnHomeIntent(); boolean hasSettings(); List> getPredictedApps(); - int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1; - /** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */ - int getSearchBarHeight(); } diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index 42515d1fcf..2a5899c31e 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -80,7 +80,7 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG @Override public boolean onScaleBegin(ScaleGestureDetector detector) { - if (mLauncher.mState != Launcher.State.WORKSPACE || mLauncher.isOnCustomContent()) { + if (mLauncher.mState != Launcher.State.WORKSPACE) { // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. return false; } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index a7e68ff148..841c0cd57a 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -108,28 +108,21 @@ public class ShortcutAndWidgetContainer extends ViewGroup { public void measureChild(View child) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - if (!lp.isFullscreen) { - final DeviceProfile profile = mLauncher.getDeviceProfile(); + final DeviceProfile profile = mLauncher.getDeviceProfile(); - if (child instanceof LauncherAppWidgetHostView) { - lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, - profile.appWidgetScale.x, profile.appWidgetScale.y); - // Widgets have their own padding - } else { - lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX); - // Center the icon/folder - int cHeight = getCellContentHeight(); - int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f)); - int cellPaddingX = mContainerType == CellLayout.WORKSPACE - ? profile.workspaceCellPaddingXPx - : (int) (profile.edgeMarginPx / 2f); - child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0); - } + if (child instanceof LauncherAppWidgetHostView) { + lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, + profile.appWidgetScale.x, profile.appWidgetScale.y); + // Widgets have their own padding } else { - lp.x = 0; - lp.y = 0; - lp.width = getMeasuredWidth(); - lp.height = getMeasuredHeight(); + lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX); + // Center the icon/folder + int cHeight = getCellContentHeight(); + int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f)); + int cellPaddingX = mContainerType == CellLayout.WORKSPACE + ? profile.workspaceCellPaddingXPx + : (int) (profile.edgeMarginPx / 2f); + child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0); } int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index bdeed4c972..d7f709932e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -53,7 +53,6 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.Toast; -import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.UninstallDropTarget.DropTargetSource; @@ -118,6 +117,8 @@ public class Workspace extends PagedView private static final int ADJACENT_SCREEN_DROP_DURATION = 300; + private static final int DEFAULT_PAGE = 0; + private static final boolean MAP_NO_RECURSE = false; private static final boolean MAP_RECURSE = true; @@ -126,12 +127,6 @@ public class Workspace extends PagedView // The is the first screen. It is always present, even if its empty. public static final long FIRST_SCREEN_ID = 0; - private final static long CUSTOM_CONTENT_SCREEN_ID = -301; - - private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; - private long mTouchDownTime = -1; - private long mCustomContentShowTime = -1; - private LayoutTransition mLayoutTransition; @Thunk final WallpaperManager mWallpaperManager; @@ -155,11 +150,6 @@ public class Workspace extends PagedView private int mDragOverX = -1; private int mDragOverY = -1; - CustomContentCallbacks mCustomContentCallbacks; - boolean mCustomContentShowing; - private float mLastCustomContentScrollProgress = -1f; - private String mCustomContentDescription = ""; - /** * The CellLayout that is currently being dragged over */ @@ -250,7 +240,6 @@ public class Workspace extends PagedView private boolean mUnlockWallpaperFromDefaultPageOnLayout; @Thunk Runnable mDelayedResizeRunnable; - private Runnable mDelayedSnapToPageRunnable; // Variables relating to the creation of user folders by hovering shortcuts over shortcuts private static final int FOLDER_CREATION_TIMEOUT = 0; @@ -357,14 +346,6 @@ public class Workspace extends PagedView @Override public void setInsets(Rect insets) { mInsets.set(insets); - - CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID); - if (customScreen != null) { - View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0); - if (customContent instanceof Insettable) { - ((Insettable) customContent).setInsets(mInsets); - } - } } /** @@ -378,8 +359,8 @@ public class Workspace extends PagedView float shrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor; int[] size = new int[2]; if (getChildCount() > 0) { - // Use the first non-custom page to estimate the child position - CellLayout cl = (CellLayout) getChildAt(numCustomPages()); + // Use the first page to estimate the child position + CellLayout cl = (CellLayout) getChildAt(0); boolean isWidget = itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; Rect r = estimateItemPosition(cl, 0, 0, itemInfo.spanX, itemInfo.spanY); @@ -432,7 +413,6 @@ public class Workspace extends PagedView } updateChildrenLayersEnabled(false); - mLauncher.onDragStarted(); mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging @@ -502,7 +482,7 @@ public class Workspace extends PagedView * Initializes various states for this workspace. */ protected void initWorkspace() { - mCurrentPage = getDefaultPage(); + mCurrentPage = DEFAULT_PAGE; DeviceProfile grid = mLauncher.getDeviceProfile(); setWillNotDraw(false); setClipChildren(false); @@ -523,10 +503,6 @@ public class Workspace extends PagedView mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate()); } - private int getDefaultPage() { - return numCustomPages(); - } - private void setupLayoutTransition() { // We want to show layout transitions when pages are deleted, to close the gap. mLayoutTransition = new LayoutTransition(); @@ -587,16 +563,9 @@ public class Workspace extends PagedView public void removeAllWorkspaceScreens() { // Disable all layout transitions before removing all pages to ensure that we don't get the - // transition animations competing with us changing the scroll when we add pages or the - // custom content screen + // transition animations competing with us changing the scroll when we add pages disableLayoutTransitions(); - // Since we increment the current page when we call addCustomContentPage via bindScreens - // (and other places), we need to adjust the current page back when we clear the pages - if (hasCustomContent()) { - removeCustomContentPage(); - } - // Recycle the QSB widget View qsb = findViewById(R.id.search_container_workspace); if (qsb != null) { @@ -657,78 +626,6 @@ public class Workspace extends PagedView return newScreen; } - public void createCustomContentContainer() { - CellLayout customScreen = (CellLayout) - LayoutInflater.from(getContext()).inflate(R.layout.workspace_screen, this, false); - customScreen.disableDragTarget(); - customScreen.disableJailContent(); - - mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen); - mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID); - - // We want no padding on the custom content - customScreen.setPadding(0, 0, 0, 0); - - addFullScreenPage(customScreen); - - // Update the custom content hint - setCurrentPage(getCurrentPage() + 1); - } - - public void removeCustomContentPage() { - CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID); - if (customScreen == null) { - throw new RuntimeException("Expected custom content screen to exist"); - } - - mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID); - mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID); - removeView(customScreen); - - if (mCustomContentCallbacks != null) { - mCustomContentCallbacks.onScrollProgressChanged(0); - mCustomContentCallbacks.onHide(); - } - - mCustomContentCallbacks = null; - - // Update the custom content hint - setCurrentPage(getCurrentPage() - 1); - } - - public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks, - String description) { - if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) { - throw new RuntimeException("Expected custom content screen to exist"); - } - - // Add the custom content to the full screen custom page - CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID); - int spanX = customScreen.getCountX(); - int spanY = customScreen.getCountY(); - CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY); - lp.canReorder = false; - lp.isFullscreen = true; - if (customContent instanceof Insettable) { - ((Insettable)customContent).setInsets(mInsets); - } - - // Verify that the child is removed from any existing parent. - if (customContent.getParent() instanceof ViewGroup) { - ViewGroup parent = (ViewGroup) customContent.getParent(); - parent.removeView(customContent); - } - customScreen.removeAllViews(); - customContent.setFocusable(true); - customContent.setOnKeyListener(new FullscreenKeyEventListener()); - customContent.setOnFocusChangeListener(mLauncher.mFocusHandler - .getHideIndicatorOnFocusListener()); - customScreen.addViewToCellLayout(customContent, 0, 0, lp, true); - mCustomContentDescription = description; - - mCustomContentCallbacks = callbacks; - } - public void addExtraEmptyScreenOnDrag() { boolean lastChildOnScreen = false; boolean childOnFinalScreen = false; @@ -772,7 +669,6 @@ public class Workspace extends PagedView if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return; long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1); - if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return; CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId); // If the final screen is empty, convert it to the extra empty screen @@ -781,7 +677,7 @@ public class Workspace extends PagedView mWorkspaceScreens.remove(finalScreenId); mScreenOrder.remove(finalScreenId); - // if this is the last non-custom content screen, convert it to the empty screen + // if this is the last screen, convert it to the empty screen mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen); mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); @@ -877,9 +773,7 @@ public class Workspace extends PagedView } public boolean hasExtraEmptyScreen() { - int nScreens = getChildCount(); - nScreens = nScreens - numCustomPages(); - return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1; + return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1; } public long commitExtraEmptyScreen() { @@ -960,7 +854,7 @@ public class Workspace extends PagedView // We enforce at least one page to add new items to. In the case that we remove the last // such screen, we convert the last screen to the empty screen - int minScreens = 1 + numCustomPages(); + int minScreens = 1; int pageShift = 0; for (Long id: removeScreens) { @@ -979,7 +873,7 @@ public class Workspace extends PagedView removeView(cl); } else { - // if this is the last non-custom content screen, convert it to the empty screen + // if this is the last screen, convert it to the empty screen mRemoveEmptyScreenRunnable = null; mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl); mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); @@ -1147,7 +1041,6 @@ public class Workspace extends PagedView case MotionEvent.ACTION_DOWN: mXDown = ev.getX(); mYDown = ev.getY(); - mTouchDownTime = System.currentTimeMillis(); break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: @@ -1161,17 +1054,6 @@ public class Workspace extends PagedView return super.onInterceptTouchEvent(ev); } - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - // Ignore pointer scroll events if the custom content doesn't allow scrolling. - if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID) - && (mCustomContentCallbacks != null) - && !mCustomContentCallbacks.isScrollingAllowed()) { - return false; - } - return super.onGenericMotionEvent(event); - } - protected void reinflateWidgetsIfNecessary() { final int clCount = getChildCount(); for (int i = 0; i < clCount; i++) { @@ -1213,24 +1095,6 @@ public class Workspace extends PagedView cancelCurrentPageLongPress(); } - boolean passRightSwipesToCustomContent = - (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY; - - boolean swipeInIgnoreDirection = mIsRtl ? deltaX < 0 : deltaX > 0; - boolean onCustomContentScreen = - getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID; - if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) { - // Pass swipes to the right to the custom content page. - return; - } - - if (onCustomContentScreen && (mCustomContentCallbacks != null) - && !mCustomContentCallbacks.isScrollingAllowed()) { - // Don't allow workspace scrolling if the current custom content screen doesn't allow - // scrolling. - return; - } - if (theta > MAX_SWIPE_ANGLE) { // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace return; @@ -1271,10 +1135,6 @@ public class Workspace extends PagedView mDelayedResizeRunnable = null; } - if (mDelayedSnapToPageRunnable != null) { - mDelayedSnapToPageRunnable.run(); - mDelayedSnapToPageRunnable = null; - } if (mStripScreensOnPageStopMoving) { stripEmptyScreens(); mStripScreensOnPageStopMoving = false; @@ -1336,7 +1196,6 @@ public class Workspace extends PagedView } updatePageAlphaValues(); - updateStateForCustomContent(); enableHwLayersOnVisiblePages(); } @@ -1348,9 +1207,6 @@ public class Workspace extends PagedView @Override protected void overScroll(float amount) { - boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || mIsRtl)) || - (amount >= 0 && (!hasCustomContent() || !mIsRtl)); - boolean shouldScrollOverlay = mLauncherOverlay != null && ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl)); @@ -1365,7 +1221,7 @@ public class Workspace extends PagedView mLastOverlayScroll = Math.abs(amount / getViewportWidth()); mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl); - } else if (shouldOverScroll) { + } else { dampedOverScroll(amount); } @@ -1518,22 +1374,6 @@ public class Workspace extends PagedView mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE, swipeDirection, ContainerType.WORKSPACE, prevPage); } - if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) { - mCustomContentShowing = true; - if (mCustomContentCallbacks != null) { - mCustomContentCallbacks.onShow(false); - mCustomContentShowTime = System.currentTimeMillis(); - } - } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) { - mCustomContentShowing = false; - if (mCustomContentCallbacks != null) { - mCustomContentCallbacks.onHide(); - } - } - } - - protected CustomContentCallbacks getCustomContentCallbacks() { - return mCustomContentCallbacks; } protected void setWallpaperDimension() { @@ -1560,26 +1400,6 @@ public class Workspace extends PagedView } } - protected void snapToPage(int whichPage, Runnable r) { - snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r); - } - - protected void snapToPage(int whichPage, int duration, Runnable r) { - if (mDelayedSnapToPageRunnable != null) { - mDelayedSnapToPageRunnable.run(); - } - mDelayedSnapToPageRunnable = r; - snapToPage(whichPage, duration); - } - - public void snapToScreenId(long screenId) { - snapToScreenId(screenId, null); - } - - protected void snapToScreenId(long screenId, Runnable r) { - snapToPage(getPageIndexForScreenId(screenId), r); - } - @Override public void computeScroll() { super.computeScroll(); @@ -1614,7 +1434,7 @@ public class Workspace extends PagedView private void updatePageAlphaValues() { if (!workspaceInModalState() && !mIsSwitchingState) { int screenCenter = getScrollX() + getViewportWidth() / 2; - for (int i = numCustomPages(); i < getChildCount(); i++) { + for (int i = 0; i < getChildCount(); i++) { CellLayout child = (CellLayout) getChildAt(i); if (child != null) { float scrollProgress = getScrollProgress(screenCenter, child, i); @@ -1632,66 +1452,6 @@ public class Workspace extends PagedView } } - public boolean hasCustomContent() { - return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID); - } - - public int numCustomPages() { - return hasCustomContent() ? 1 : 0; - } - - public boolean isOnOrMovingToCustomContent() { - return hasCustomContent() && getNextPage() == 0; - } - - private void updateStateForCustomContent() { - float translationX = 0; - float progress = 0; - if (hasCustomContent()) { - int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID); - - int scrollDelta = getScrollX() - getScrollForPage(index) - - getLayoutTransitionOffsetForPage(index); - float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index); - translationX = scrollRange - scrollDelta; - progress = (scrollRange - scrollDelta) / scrollRange; - - if (mIsRtl) { - translationX = Math.min(0, translationX); - } else { - translationX = Math.max(0, translationX); - } - progress = Math.max(0, progress); - } - - if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return; - - CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID); - if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) { - cc.setVisibility(VISIBLE); - } - - mLastCustomContentScrollProgress = progress; - - // 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 == 1 ? 0 : progress * 0.8f); - } - - if (mLauncher.getHotseat() != null) { - mLauncher.getHotseat().setTranslationX(translationX); - } - - if (mPageIndicator != null) { - mPageIndicator.setTranslationX(translationX); - } - - if (mCustomContentCallbacks != null) { - mCustomContentCallbacks.onScrollProgressChanged(progress); - } - } - protected void onAttachedToWindow() { super.onAttachedToWindow(); IBinder windowToken = getWindowToken(); @@ -1772,7 +1532,7 @@ public class Workspace extends PagedView int leftScreen = -1; int rightScreen = -1; - for (int i = numCustomPages(); i < screenCount; i++) { + for (int i = 0; i < screenCount; i++) { final View child = getPageAt(i); float left = child.getLeft() + child.getTranslationX() - getScrollX(); @@ -1785,8 +1545,7 @@ public class Workspace extends PagedView } if (mForceDrawAdjacentPages) { // In overview mode, make sure that the two side pages are visible. - leftScreen = Utilities.boundToRange(getCurrentPage() - 1, - numCustomPages(), rightScreen); + leftScreen = Utilities.boundToRange(getCurrentPage() - 1, 0, rightScreen); rightScreen = Utilities.boundToRange(getCurrentPage() + 1, leftScreen, getPageCount() - 1); } @@ -1800,7 +1559,7 @@ public class Workspace extends PagedView } } - for (int i = numCustomPages(); i < screenCount; i++) { + for (int i = 0; i < screenCount; i++) { final CellLayout layout = (CellLayout) getPageAt(i); // enable layers between left and right screen inclusive. boolean enableLayer = leftScreen <= i && i <= rightScreen; @@ -1845,19 +1604,6 @@ public class Workspace extends PagedView dragLayer.clearResizeFrame(); } - @Override - protected void getFreeScrollPageRange(int[] range) { - getOverviewModePages(range); - } - - private void getOverviewModePages(int[] range) { - int start = numCustomPages(); - int end = getChildCount() - 1; - - range[0] = Math.max(0, Math.min(start, getChildCount() - 1)); - range[1] = Math.max(0, end); - } - public void onStartReordering() { super.onStartReordering(); // Reordering handles its own animations, disable the automatic ones. @@ -1990,7 +1736,7 @@ public class Workspace extends PagedView // TODO: Update the accessibility flags appropriately when dragging. if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) { int total = getPageCount(); - for (int i = numCustomPages(); i < total; i++) { + for (int i = 0; i < total; i++) { updateAccessibilityFlags((CellLayout) getPageAt(i), i); } setImportantForAccessibility((mState == State.NORMAL || mState == State.OVERVIEW) @@ -2034,44 +1780,15 @@ public class Workspace extends PagedView invalidate(); // This will call dispatchDraw(), which calls getVisiblePages(). updateChildrenLayersEnabled(false); - hideCustomContentIfNecessary(); } public void onEndStateTransition() { mIsSwitchingState = false; updateChildrenLayersEnabled(false); - showCustomContentIfNecessary(); mForceDrawAdjacentPages = false; mTransitionProgress = 1; } - void updateCustomContentVisibility() { - int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE; - setCustomContentVisibility(visibility); - } - - void setCustomContentVisibility(int visibility) { - if (hasCustomContent()) { - mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility); - } - } - - void showCustomContentIfNecessary() { - boolean show = mState == Workspace.State.NORMAL; - if (show && hasCustomContent()) { - mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE); - } - } - - void hideCustomContentIfNecessary() { - boolean hide = mState != Workspace.State.NORMAL; - if (hide && hasCustomContent()) { - disableLayoutTransitions(); - mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE); - enableLayoutTransitions(); - } - } - public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) { View child = cellInfo.cell; @@ -2810,17 +2527,6 @@ public class Workspace extends PagedView xy[1] = mTempXY[1]; } - /* - * - * Convert the 2D coordinate xy from this CellLayout's coordinate space to - * the parent View's coordinate space. The argument xy is modified with the return result. - * - */ - void mapPointFromChildToSelf(View v, float[] xy) { - xy[0] += v.getLeft(); - xy[1] += v.getTop(); - } - private boolean isDragWidget(DragObject d) { return (d.dragInfo instanceof LauncherAppWidgetInfo || d.dragInfo instanceof PendingAddWidgetInfo); @@ -2949,7 +2655,7 @@ public class Workspace extends PagedView } // Always pick the current page. - if (layout == null && nextPage >= numCustomPages() && nextPage < getPageCount()) { + if (layout == null && nextPage >= 0 && nextPage < getPageCount()) { layout = (CellLayout) getChildAt(nextPage); } if (layout != mDragTargetLayout) { @@ -2964,7 +2670,7 @@ public class Workspace extends PagedView * Returns the child CellLayout if the point is inside the page coordinates, null otherwise. */ private CellLayout verifyInsidePage(int pageNo, float[] touchXy) { - if (pageNo >= numCustomPages() && pageNo < getPageCount()) { + if (pageNo >= 0 && pageNo < getPageCount()) { CellLayout cl = (CellLayout) getChildAt(pageNo); mapPointFromSelfToChild(cl, touchXy); if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && @@ -3142,7 +2848,7 @@ public class Workspace extends PagedView if (!mLauncher.isHotseatLayout(cellLayout) && screenId != getScreenIdForPageIndex(mCurrentPage) && mState != State.SPRING_LOADED) { - snapToScreenId(screenId, null); + snapToPage(getPageIndexForScreenId(screenId)); } if (info instanceof PendingAddItemInfo) { @@ -3434,10 +3140,6 @@ public class Workspace extends PagedView return mDragInfo; } - public int getCurrentPageOffsetFromCustomContent() { - return getNextPage() - numCustomPages(); - } - /** * Calculate the nearest cell where the given object would be dropped. * @@ -3964,14 +3666,10 @@ public class Workspace extends PagedView } } - void moveToDefaultScreen(boolean animate) { - int page = getDefaultPage(); + void moveToDefaultScreen() { + int page = DEFAULT_PAGE; if (!workspaceInModalState() && getNextPage() != page) { - if (animate) { - snapToPage(page); - } else { - setCurrentPage(page); - } + snapToPage(page); } View child = getChildAt(page); if (child != null) { @@ -3979,22 +3677,6 @@ public class Workspace extends PagedView } } - void moveToCustomContentScreen(boolean animate) { - if (hasCustomContent()) { - int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID); - if (animate) { - snapToPage(ccIndex); - } else { - setCurrentPage(ccIndex); - } - View child = getChildAt(ccIndex); - if (child != null) { - child.requestFocus(); - } - } - exitWidgetResizeMode(); - } - @Override protected String getPageIndicatorDescription() { return getResources().getString(R.string.all_apps_button_label); @@ -4002,16 +3684,12 @@ public class Workspace extends PagedView @Override protected String getCurrentPageDescription() { - if (hasCustomContent() && getNextPage() == 0) { - return mCustomContentDescription; - } int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; return getPageDescription(page); } private String getPageDescription(int page) { - int delta = numCustomPages(); - int nScreens = getChildCount() - delta; + int nScreens = getChildCount(); int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID); if (extraScreenId >= 0 && nScreens > 1) { if (page == extraScreenId) { @@ -4023,8 +3701,7 @@ public class Workspace extends PagedView // When the workspace is not loaded, we do not know how many screen will be bound. return getContext().getString(R.string.all_apps_home_button_label); } - return getContext().getString(R.string.workspace_scroll_format, - page + 1 - delta, nScreens); + return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens); } @Override diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index a105a73034..e84d3b4e6f 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -274,7 +274,6 @@ public class WorkspaceStateTransitionAnimation { 1.0f : 0f; float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded || states.stateIsNormalHidden) ? 1f : 0f; - float finalQsbAlpha = (states.stateIsNormal || states.stateIsNormalHidden) ? 1f : 0f; float finalWorkspaceTranslationY = 0; if (states.stateIsOverview || states.stateIsOverviewHidden) { @@ -284,7 +283,6 @@ public class WorkspaceStateTransitionAnimation { } final int childCount = mWorkspace.getChildCount(); - final int customPageCount = mWorkspace.numCustomPages(); mNewScale = 1.0f; @@ -313,7 +311,7 @@ public class WorkspaceStateTransitionAnimation { } else if(states.stateIsNormalHidden) { finalAlpha = (i == mWorkspace.getNextPage()) ? 1 : 0; } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) { - finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f; + finalAlpha = (i == toPage) ? 1f : 0f; } else { finalAlpha = 1f; } @@ -426,7 +424,6 @@ public class WorkspaceStateTransitionAnimation { mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded); mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end(); - mWorkspace.updateCustomContentVisibility(); mWorkspace.setScaleX(mNewScale); mWorkspace.setScaleY(mNewScale); mWorkspace.setTranslationY(finalWorkspaceTranslationY); diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index a0ad07aeb1..c695758b2f 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -411,7 +411,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex); boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); - screenIndex = workspace.hasCustomContent() ? 1 : 0; + screenIndex = 0; while (!found && screenIndex < workspaceScreens.size()) { screenId = workspaceScreens.get(screenIndex); layout = (CellLayout) workspace.getPageAt(screenIndex); diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java index edb0b168d0..f9eb2eda62 100644 --- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java @@ -88,7 +88,7 @@ public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate { info.addAction(mActions.get(MOVE_FORWARD)); } - int startIndex = mWorkspace.numCustomPages() + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0); + int startIndex = FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0; if (index > startIndex) { info.addAction(mActions.get(MOVE_BACKWARD)); } diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index 5cb8c4c7d5..355963bfeb 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -1,11 +1,8 @@ package com.android.launcher3.testing; import android.content.Intent; -import android.graphics.Color; import android.os.Bundle; import android.view.Menu; -import android.view.View; -import android.widget.FrameLayout; import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; @@ -142,51 +139,6 @@ public class LauncherExtension extends Launcher { return false; } - CustomContentCallbacks mCustomContentCallbacks = new CustomContentCallbacks() { - - // Custom content is completely shown. {@code fromResume} indicates whether this was caused - // by a onResume or by scrolling otherwise. - public void onShow(boolean fromResume) { - } - - // Custom content is completely hidden - public void onHide() { - } - - // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing). - public void onScrollProgressChanged(float progress) { - - } - - // Indicates whether the user is allowed to scroll away from the custom content. - public boolean isScrollingAllowed() { - return true; - } - - }; - - @Override - public boolean hasCustomContentToLeft() { - return true; - } - - @Override - public void populateCustomContentContainer() { - FrameLayout customContent = new FrameLayout(LauncherExtension.this); - customContent.setBackgroundColor(Color.GRAY); - addToCustomContentPage(customContent, mCustomContentCallbacks, ""); - } - - @Override - public View getQsbBar() { - return null; - } - - @Override - public Bundle getAdditionalSearchWidgetOptions() { - return new Bundle(); - } - @Override public boolean shouldMoveToDefaultScreenOnHomeIntent() { return true; @@ -203,11 +155,6 @@ public class LauncherExtension extends Launcher { return new ArrayList<>(); } - @Override - public int getSearchBarHeight() { - return SEARCH_BAR_HEIGHT_NORMAL; - } - @Override public void onAttachedToWindow() { } diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java index 392697458b..f99efcec94 100644 --- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java +++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java @@ -108,7 +108,7 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback public float wallpaperOffsetForScroll(int scroll) { // To match the default wallpaper behavior in the system, we default to either the left // or right edge on initialization - int numScrollingPages = getNumScreensExcludingEmptyAndCustom(); + int numScrollingPages = getNumScreensExcludingEmpty(); if (mLockedToDefaultPage || numScrollingPages <= 1) { return mIsRtl ? 1f : 0f; } @@ -125,10 +125,10 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback int leftPageIndex; int rightPageIndex; if (mIsRtl) { - rightPageIndex = mWorkspace.numCustomPages(); + rightPageIndex = 0; leftPageIndex = rightPageIndex + numScrollingPages - 1; } else { - leftPageIndex = mWorkspace.numCustomPages(); + leftPageIndex = 0; rightPageIndex = leftPageIndex + numScrollingPages - 1; } @@ -163,7 +163,7 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback } private int numEmptyScreensToIgnore() { - int numScrollingPages = mWorkspace.getChildCount() - mWorkspace.numCustomPages(); + int numScrollingPages = mWorkspace.getChildCount(); if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) { return 1; } else { @@ -171,8 +171,8 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback } } - private int getNumScreensExcludingEmptyAndCustom() { - return mWorkspace.getChildCount() - numEmptyScreensToIgnore() - mWorkspace.numCustomPages(); + private int getNumScreensExcludingEmpty() { + return mWorkspace.getChildCount() - numEmptyScreensToIgnore(); } public void syncWithScroll() { @@ -207,13 +207,13 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback public void setFinalX(float x) { scheduleUpdate(); mFinalOffset = Math.max(0f, Math.min(x, 1f)); - if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) { + if (getNumScreensExcludingEmpty() != mNumScreens) { if (mNumScreens > 0 && Float.compare(mCurrentOffset, mFinalOffset) != 0) { // Don't animate if we're going from 0 screens, or if the final offset is the same // as the current offset animateToFinal(); } - mNumScreens = getNumScreensExcludingEmptyAndCustom(); + mNumScreens = getNumScreensExcludingEmpty(); } } From 6d3586c761baafc28aeff755c16c2f5968229f22 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Fri, 8 Sep 2017 12:39:10 -0700 Subject: [PATCH 017/885] Polish animation when icon returns to Folder. ie. The animation when the user drags an icon from a Folder to a full workspace/hotseat. Bug: 31443188 Change-Id: If51b23cd8fc822543ac3f550f9fd2e48dd58e0e9 --- src/com/android/launcher3/Workspace.java | 2 +- src/com/android/launcher3/folder/Folder.java | 2 +- .../android/launcher3/folder/FolderIcon.java | 42 ++++++++++++------- .../launcher3/folder/PreviewItemManager.java | 24 ++++++----- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d7f709932e..59437f6082 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2092,7 +2092,7 @@ public class Workspace extends PagedView if (dropOverView instanceof FolderIcon) { FolderIcon fi = (FolderIcon) dropOverView; if (fi.acceptDrop(d.dragInfo)) { - fi.onDrop(d); + fi.onDrop(d, false /* itemReturnedOnFailedDrop */); // if the drag started here, we need to remove it from the workspace if (!external) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 64a2dabab5..b7f8f3e2e2 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -888,7 +888,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mItemsInvalidated = true; try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mFolderIcon.onDrop(d); + mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */); } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index bb0a726268..399888899a 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -100,6 +100,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { ClippedFolderIconLayoutRule mPreviewLayoutRule; private PreviewItemManager mPreviewItemManager; private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); + private List mCurrentPreviewItems = new ArrayList<>(); boolean mAnimating = false; private Rect mTempBounds = new Rect(); @@ -198,7 +199,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void setFolder(Folder folder) { mFolder = folder; mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv); - mPreviewItemManager.updateItemDrawingParams(false); + updatePreviewItems(false); } private boolean willAcceptItem(ItemInfo item) { @@ -262,7 +263,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { .start(); // This will animate the dragView (srcView) into the new folder - onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable); + onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, + false /* itemReturnedOnFailedDrop */); } public void performDestroyAnimation(Runnable onCompleteRunnable) { @@ -277,7 +279,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { } private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect, - float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) { + float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable, + boolean itemReturnedOnFailedDrop) { item.cellX = -1; item.cellY = -1; @@ -305,21 +308,25 @@ public class FolderIcon extends FrameLayout implements FolderListener { workspace.resetTransitionTransform((CellLayout) getParent().getParent()); } + int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1); boolean itemAdded = false; - if (index >= MAX_NUM_ITEMS_IN_PREVIEW) { - List oldPreviewItems = getPreviewItemsOnPage(0); + if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) { + List oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); addItem(item, false); - List newPreviewItems = getPreviewItemsOnPage(0); + mCurrentPreviewItems.clear(); + mCurrentPreviewItems.addAll(getPreviewItems()); - if (!oldPreviewItems.containsAll(newPreviewItems)) { - for (int i = 0; i < newPreviewItems.size(); ++i) { - if (newPreviewItems.get(i).getTag().equals(item)) { + if (!oldPreviewItems.equals(mCurrentPreviewItems)) { + for (int i = 0; i < mCurrentPreviewItems.size(); ++i) { + if (mCurrentPreviewItems.get(i).getTag().equals(item)) { // If the item dropped is going to be in the preview, we update the // index here to reflect its position in the preview. index = i; } } - mPreviewItemManager.onDrop(oldPreviewItems, newPreviewItems, item); + + mPreviewItemManager.hidePreviewItem(index, true); + mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item); itemAdded = true; } else { removeItem(item, false); @@ -331,7 +338,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } int[] center = new int[2]; - float scale = getLocalCenterForIndex(index, index + 1, center); + float scale = getLocalCenterForIndex(index, numItemsInPreview, center); center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); @@ -362,7 +369,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } } - public void onDrop(DragObject d) { + public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) { ShortcutInfo item; if (d.dragInfo instanceof AppInfo) { // Came from all apps -- make a copy @@ -374,7 +381,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { item = (ShortcutInfo) d.dragInfo; } mFolder.notifyDrop(); - onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable); + onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, + itemReturnedOnFailedDrop); } public void setBadgeInfo(FolderBadgeInfo badgeInfo) { @@ -545,11 +553,17 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Override public void onItemsChanged(boolean animate) { - mPreviewItemManager.updateItemDrawingParams(animate); + updatePreviewItems(animate); invalidate(); requestLayout(); } + private void updatePreviewItems(boolean animate) { + mPreviewItemManager.updatePreviewItems(animate); + mCurrentPreviewItems.clear(); + mCurrentPreviewItems.addAll(getPreviewItems()); + } + @Override public void prepareAutoUpdate() { } diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 5d400100fd..06d3eb166b 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -110,7 +110,7 @@ public class PreviewItemManager { mIcon.mPreviewLayoutRule.init(mIcon.mBackground.previewSize, mIntrinsicIconSize, Utilities.isRtl(mIcon.getResources())); - updateItemDrawingParams(false); + updatePreviewItems(false); } } @@ -185,6 +185,11 @@ public class PreviewItemManager { } public void hidePreviewItem(int index, boolean hidden) { + // If there are more params than visible in the preview, they are used for enter/exit + // animation purposes and they were added to the front of the list. + // To index the params properly, we need to skip these params. + index = index + Math.max(mFirstPageParams.size() - MAX_NUM_ITEMS_IN_PREVIEW, 0); + PreviewItemDrawingParams params = index < mFirstPageParams.size() ? mFirstPageParams.get(index) : null; if (params != null) { @@ -266,7 +271,7 @@ public class PreviewItemManager { } } - void updateItemDrawingParams(boolean animate) { + void updatePreviewItems(boolean animate) { buildParamsForPage(0, mFirstPageParams, animate); } @@ -310,8 +315,8 @@ public class PreviewItemManager { int prevIndex = newParams.indexOf(moveIn.get(i)); PreviewItemDrawingParams p = params.get(prevIndex); computePreviewItemDrawingParams(prevIndex, numItems, p); - updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, - newParams.indexOf(moveIn.get(i))); + updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i)), + numItems); } // Items that are moving into new positions within the preview. @@ -319,7 +324,7 @@ public class PreviewItemManager { int oldIndex = oldParams.indexOf(newParams.get(newIndex)); if (oldIndex >= 0 && newIndex != oldIndex) { PreviewItemDrawingParams p = params.get(newIndex); - updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex); + updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex, numItems); } } @@ -330,7 +335,7 @@ public class PreviewItemManager { BubbleTextView item = moveOut.get(i); int oldIndex = oldParams.indexOf(item); PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null); - updateTransitionParam(p, item, oldIndex, EXIT_INDEX); + updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems); params.add(0, p); // We want these items first so that they are on drawn last. } @@ -342,7 +347,7 @@ public class PreviewItemManager { } private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv, - int prevIndex, int newIndex) { + int prevIndex, int newIndex, int numItems) { p.drawable = btv.getCompoundDrawables()[1]; if (!mIcon.mFolder.isOpen()) { // Set the callback to FolderIcon as it is responsible to drawing the icon. The @@ -350,9 +355,8 @@ public class PreviewItemManager { p.drawable.setCallback(mIcon); } - FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, - MAX_NUM_ITEMS_IN_PREVIEW, newIndex, MAX_NUM_ITEMS_IN_PREVIEW, - DROP_IN_ANIMATION_DURATION, null); + FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, numItems, + newIndex, numItems, DROP_IN_ANIMATION_DURATION, null); if (p.anim != null && !p.anim.hasEqualFinalState(anim)) { p.anim.cancel(); } From be213def874f580a58b2ee4a94ba503a6d7e3f15 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 13 Sep 2017 11:49:14 -0700 Subject: [PATCH 018/885] Import translations. DO NOT MERGE Change-Id: Id11401a9e60c09548748d7a60c602d33ae4a9329 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-af/strings.xml | 3 +-- res/values-am/strings.xml | 3 +-- res/values-ar/strings.xml | 3 +-- res/values-b+sr+Latn/strings.xml | 3 +-- res/values-be/strings.xml | 3 +-- res/values-bg/strings.xml | 3 +-- res/values-bn/strings.xml | 3 +-- res/values-bs/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-rAU/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/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/strings.xml | 3 +-- res/values-gu/strings.xml | 3 +-- res/values-hr/strings.xml | 3 +-- res/values-hu/strings.xml | 3 +-- res/values-hy/strings.xml | 3 +-- res/values-in/strings.xml | 3 +-- res/values-is/strings.xml | 3 +-- res/values-it/strings.xml | 3 +-- res/values-iw/strings.xml | 3 +-- res/values-ja/strings.xml | 3 +-- res/values-ka/strings.xml | 3 +-- res/values-kk/strings.xml | 3 +-- res/values-km/strings.xml | 3 +-- res/values-kn/strings.xml | 3 +-- res/values-ko/strings.xml | 3 +-- res/values-ky/strings.xml | 3 +-- res/values-lo/strings.xml | 3 +-- res/values-lt/strings.xml | 3 +-- res/values-lv/strings.xml | 3 +-- res/values-mk/strings.xml | 3 +-- res/values-ml/strings.xml | 3 +-- res/values-mn/strings.xml | 3 +-- res/values-mr/strings.xml | 3 +-- res/values-ms/strings.xml | 3 +-- res/values-nb/strings.xml | 3 +-- res/values-ne/strings.xml | 3 +-- res/values-pa/strings.xml | 29 ++++++++++++++--------------- 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/strings.xml | 3 +-- res/values-sk/strings.xml | 3 +-- res/values-sl/strings.xml | 3 +-- res/values-sq/strings.xml | 3 +-- res/values-sr/strings.xml | 3 +-- res/values-sv/strings.xml | 3 +-- res/values-sw/strings.xml | 3 +-- res/values-ta/strings.xml | 3 +-- res/values-te/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/strings.xml | 3 +-- res/values-uz/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 +-- 75 files changed, 88 insertions(+), 163 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index a79c2d4b57..5a776a6486 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -47,8 +47,7 @@ "Verwyder" "Deïnstalleer" "Programinligting" - - + "Installeer" "installeer kortpaaie" "Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging." "lees Tuis-instellings en -kortpaaie" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index c7d2958d0c..5225dbab57 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -47,8 +47,7 @@ "አስወግድ" "አራግፍ" "የመተግበሪያ መረጃ" - - + "ጫን" "አቋራጮችን ይጭናል" "መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።" "የመነሻ ቅንብሮች እና አቋራጮችን ያነባል" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index a15bb76736..5f2d58d125 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -47,8 +47,7 @@ "إزالة" "إلغاء التثبيت" "معلومات عن التطبيق" - - + "تثبيت" "تثبيت اختصارات" "للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم." "قراءة إعدادات واختصارات الشاشة الرئيسية" diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index b583865e77..4fa86d294a 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -47,8 +47,7 @@ "Ukloni" "Deinstaliraj" "Inform. o aplikaciji" - - + "Instaliraj" "instaliranje prečica" "Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika." "čitanje podešavanja i prečica na početnom ekranu" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index eae036e55f..1d7797d98d 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -47,8 +47,7 @@ "Выдаліць" "Выдаліць" "Звесткі пра праграмы" - - + "Усталяваць" "усталёўваць ярлыкі" "Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка." "счытваць налады і ярлыкі на Галоўнай старонцы" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 8ef3df8e6a..b429492d0b 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -47,8 +47,7 @@ "Премахване" "Деинсталиране" "Данни за прилож." - - + "Инсталиране" "инсталиране на преки пътища" "Разрешава на приложението да добавя преки пътища без намеса на потребителя." "четене на настройките и преките пътища в Начало" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index b6c4226059..897a8db0e3 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -47,8 +47,7 @@ "সরান" "আনইনস্টল করুন" "অ্যাপের তথ্য" - - + "ইনস্টল করুন" "শর্টকাটগুলি ইনস্টল করে" "একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷" "হোম সেটিংস এবং শর্টকাটগুলি পড়ে" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 773fe4af70..b062b88a50 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -47,8 +47,7 @@ "Ukloni" "Deinstaliraj" "Informacije o aplikaciji" - - + "Instaliraj" "instaliraj prečice" "Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika." "čitaj postavke na početnom ekranu i prečice" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index acaf997250..008531a97f 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -47,8 +47,7 @@ "Suprimeix" "Desinstal·la" "Dades de l\'aplicació" - - + "Instal·la" "instal·la dreceres" "Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari." "llegeix la configuració i les dreceres de la pantalla d\'inici" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index db7f7bbeed..7cd71c2777 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -47,8 +47,7 @@ "Odstranit" "Odinstalovat" "O aplikaci" - - + "Nainstalovat" "instalace zástupce" "Umožňuje aplikaci přidat zástupce bez zásahu uživatele." "čtení nastavení a odkazů plochy" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 8e5448696c..9a91bc49b4 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -47,8 +47,7 @@ "Fjern" "Afinstaller" "Appinfo" - - + "Installer" "installere genveje" "Tillader, at en app tilføjer genveje uden brugerens indgriben." "læs indstillinger og genveje for startskærmen" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index b32cdf4574..6bc08755fe 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -47,8 +47,7 @@ "Entfernen" "Deinstallieren" "App-Details" - - + "Installieren" "Verknüpfungen installieren" "Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers" "Einstellungen und Verknüpfungen auf dem Startbildschirm lesen" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 421907ffc1..cdb255d5d2 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -47,8 +47,7 @@ "Κατάργηση" "Απεγκατάσταση" "Πληροφορίες εφαρμογής" - - + "Εγκατάσταση" "εγκατάσταση συντομεύσεων" "Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη." "ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index b4823f3a3e..afd6757964 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -47,8 +47,7 @@ "Remove" "Uninstall" "App info" - - + "Install" "install shortcuts" "Allows an app to add shortcuts without user intervention." "read Home settings and shortcuts" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index b4823f3a3e..afd6757964 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -47,8 +47,7 @@ "Remove" "Uninstall" "App info" - - + "Install" "install shortcuts" "Allows an app to add shortcuts without user intervention." "read Home settings and shortcuts" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index b4823f3a3e..afd6757964 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -47,8 +47,7 @@ "Remove" "Uninstall" "App info" - - + "Install" "install shortcuts" "Allows an app to add shortcuts without user intervention." "read Home settings and shortcuts" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index af22e0be70..05f1e067f0 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -47,8 +47,7 @@ "Quitar" "Desinstalar" "Información de app" - - + "Instalar" "instalar accesos directos" "Permite que una aplicación agregue accesos directos sin que el usuario intervenga." "leer configuración y accesos directos de la pantalla principal" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index cd80b378c5..272bb61076 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -47,8 +47,7 @@ "Quitar" "Desinstalar" "Datos de aplicación" - - + "Instalar" "instalar accesos directos" "Permite que una aplicación añada accesos directos sin intervención del usuario." "leer información de accesos directos y de ajustes de la pantalla de inicio" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index d911ac4f01..ccc85a3e55 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -47,8 +47,7 @@ "Eemalda" "Desinstalli" "Rakenduse teave" - - + "Installimine" "installi otseteed" "Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta." "loe avaekraani seadeid ja otseteid" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index aaaf0d8019..8051b85394 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -47,8 +47,7 @@ "برداشتن" "حذف نصب" "اطلاعات برنامه" - - + "نصب" "نصب میان‌برها" "به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند." "خواندن تنظیمات و میان‌برهای صفحه اصلی" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 7b09da0d3c..5e05bbe17e 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -47,8 +47,7 @@ "Poista" "Poista asennus" "Sovelluksen tiedot" - - + "Asenna" "asenna pikakuvakkeita" "Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa." "lue aloitusruudun asetuksia ja pikakuvakkeita" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 9768320176..290fe0b51b 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -47,8 +47,7 @@ "Supprimer" "Désinstaller" "Détails de l\'appli" - - + "Installer" "installer des raccourcis" "Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur." "lire les paramètres et les raccourcis de la page d\'accueil" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index fde0c60c86..bc98cd25f6 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -47,8 +47,7 @@ "Supprimer" "Désinstaller" "Infos sur l\'appli" - - + "Installer" "installer des raccourcis" "Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur" "lire les paramètres et les raccourcis de l\'écran d\'accueil" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index bdc79f2845..54f96049cc 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -47,8 +47,7 @@ "Eliminar" "Desinstalar" "Info. da aplicación" - - + "Instalar" "instalar atallos" "Permite a unha aplicación engadir atallos sen intervención do usuario." "ler a configuración e os atallos da pantalla de inicio" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 68a9e8626f..973478146c 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -47,8 +47,7 @@ "દૂર કરો" "અનઇન્સ્ટોલ કરો" "ઍપ્લિકેશન માહિતી" - - + "ઇન્સ્ટૉલ કરો" "શોર્ટકટ્સ ઇન્સ્ટોલ કરો" "એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે." "હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 24f9997e75..f5330157c9 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -47,8 +47,7 @@ "Ukloni" "Deinstaliraj" "Info o aplikaciji" - - + "Instaliraj" "instaliranje prečaca" "Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika." "čitanje postavki početnog zaslona i prečaca" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 94907eb47b..e38da35dad 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -47,8 +47,7 @@ "Törlés" "Eltávolítás" "Alkalmazásinformáció" - - + "Telepítés" "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." "Főoldal beállításainak és parancsikonjainak beolvasása" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 44b337d4f7..4901dc5c20 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -47,8 +47,7 @@ "Հեռացնել" "Հեռացնել" "Հավելվածի տվյալներ" - - + "Տեղադրել" "տեղադրել դյուրանցումներ" "Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:" "կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index da9a683381..5112223640 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -47,8 +47,7 @@ "Hapus" "Uninstal" "Info aplikasi" - - + "Instal" "memasang pintasan" "Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna." "membaca setelan dan pintasan layar Utama" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 40c26d788b..e1b04e283e 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -47,8 +47,7 @@ "Fjarlægja" "Fjarlægja" "Forritsupplýsingar" - - + "Setja upp" "setja upp flýtileiðir" "Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda." "lesa stillingar og flýtileiðir heimaskjás" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index c1d97d9dcc..a22a4ce9a0 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -47,8 +47,7 @@ "Rimuovi" "Disinstalla" "Informazioni app" - - + "Installa" "aggiunta di scorciatoie" "Consente a un\'app di aggiungere scorciatoie automaticamente." "lettura di impostazioni e scorciatoie in Home" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index d1595b9dcc..d7905b4db5 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -47,8 +47,7 @@ "הסר" "הסר התקנה" "פרטי אפליקציה" - - + "התקנה" "התקן קיצורי דרך" "מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש." "קרא הגדרות וקיצורי דרך של דף הבית" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index eb1b8aa052..4b6c3ef43d 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -47,8 +47,7 @@ "削除" "アンインストール" "アプリ情報" - - + "インストール" "ショートカットのインストール" "ユーザー操作なしでショートカットを追加することをアプリに許可します。" "ホームの設定とショートカットの読み取り" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 297b9641bb..ec60583309 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -47,8 +47,7 @@ "ამოშლა" "დეინსტალაცია" "აპის შესახებ" - - + "ინსტალაცია" "მალსახმობების დაყენება" "აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა." "მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 681816285f..115b752933 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -47,8 +47,7 @@ "Жою" "Жою" "Қолданба ақпараты" - - + "Орнату" "төте пернелерді орнату" "Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді." "Негізгі экрандағы параметрлер мен төте пернелерді оқу" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index e72b2ec083..9040811544 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -47,8 +47,7 @@ "យកចេញ" "លុបការដំឡើង" "ព័ត៌មាន​កម្មវិធី" - - + "ដំឡើង" "ដំឡើង​ផ្លូវកាត់" "អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។" "អាន​ការ​កំណត់​ និង​ផ្លូវកាត់​​អេក្រង់​ដើម" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index df3b80a3db..4156a21e14 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -47,8 +47,7 @@ "ತೆಗೆದುಹಾಕಿ" "ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್" "ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ" - - + "ಸ್ಥಾಪಿಸಿ" "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ" "ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ." "ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 1630ddd054..d9d8da68f2 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -47,8 +47,7 @@ "삭제" "제거" "앱 정보" - - + "설치" "바로가기 설치" "앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다." "홈 설정 및 바로가기 읽기" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index a0635d4d26..0808214bc9 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -47,8 +47,7 @@ "Алып салуу" "Чыгарып салуу" "Колдонмо тууралуу" - - + "Орнотуу" "тез чакырмаларды орнотуу" "Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет." "Үйдүн тууралоолорун жана тез чакырмаларын окуу" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 759c73200f..c1a1e71583 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -47,8 +47,7 @@ "ເອົາ​ອອກ" "ຖອນ​ການ​ຕິດ​ຕັ້ງ" "ຂໍ້ມູນແອັບ" - - + "ຕິດຕັ້ງ" "ຕິດຕັ້ງທາງລັດ" "ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້." "ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index ad1aa6d074..ca45583db7 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -47,8 +47,7 @@ "Ištrinti" "Pašalinti" "Programos inform." - - + "Įdiegti" "įdiegti sparčiuosius klavišus" "Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo." "skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 1b99b1e565..7a4903ab68 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -47,8 +47,7 @@ "Noņemt" "Atinstalēt" "Par lietotni" - - + "Instalēt" "instalēt saīsnes" "Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam." "lasīt sākuma ekrāna iestatījumus un saīsnes" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index a4b967bfb4..0aaed6231b 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -47,8 +47,7 @@ "Отстрани" "Деинсталирај" "Инф. за апликација" - - + "Инсталирај" "инсталирај кратенки" "Овозможува апликацијата да додава кратенки без интервенција на корисникот." "чита поставки и кратенки на почетна страница" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index c18b119f17..299fc4592d 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -47,8 +47,7 @@ "നീക്കംചെയ്യുക" "അൺഇൻസ്റ്റാളുചെയ്യുക" "ആപ്പ് വിവരം" - - + "ഇൻസ്‌റ്റാൾ ചെയ്യുക" "കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക" "ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." "ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക" diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 3fad545cf2..8921c77935 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -47,8 +47,7 @@ "Арилгах" "Устгах" "Апп-н мэдээлэл" - - + "Суулгах" "товчлол суулгах" "Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна" "Нүүрний тохиргоо болон товчлолыг унших" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index fabe15db8a..ca9a4028d0 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -47,8 +47,7 @@ "काढा" "अनइंस्टॉल करा" "अॅप माहिती" - - + "इंस्टॉल करा" "शॉर्टकट स्‍थापित करा" "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते." "होम सेटिंग्ज आणि शॉर्टकट वाचा" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 08743875e9..6aeac8b28e 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -47,8 +47,7 @@ "Alih keluar" "Nyahpasang" "Maklumat apl" - - + "Pasang" "pasang pintasan" "Membenarkan apl menambah pintasan tanpa campur tangan pengguna." "baca tetapan dan pintasan Laman Utama" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 3ad2cc88a1..0a6276b9ca 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -47,8 +47,7 @@ "Fjern" "Avinstaller" "Info om appen" - - + "Installer" "installere snarveier" "Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren." "lese startsideinnstillinger og -snarveier" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 776d6729c1..41e43a7501 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -47,8 +47,7 @@ "हटाउनुहोस्" "विस्थापित गर्नुहोस्" "अनुप्रयोग जानकारी" - - + "स्थापना गर्नुहोस्" "सर्टकट स्थापना गर्नेहोस्" "प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।" "गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index b1d3efd1e4..525f2f629d 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -28,9 +28,9 @@ "ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਹੋਮ ਸਕ੍ਰੀਨ" - "ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ" + "ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ" "ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT & ਹੋਲਡ ਕਰੋ।" - "ਡਬਲ ਟੈਪ ਕਰੋ & ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" + "ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤਣ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" "%1$d × %2$d" "%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ" "ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ" @@ -45,17 +45,16 @@ "ਐਪ ਸੂਚੀ" "ਹੋਮ" "ਹਟਾਓ" - "ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ" + "ਅਣਸਥਾਪਤ ਕਰੋ" "ਐਪ ਜਾਣਕਾਰੀ" - - - "ਸ਼ਾਰਟਕੱਟ ਇੰਸਟੌਲ ਕਰੋ" - "ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" + "ਸਥਾਪਤ ਕਰੋ" + "ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ" + "ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" "ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ" "ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" "ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਲਿਖੋ" "ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" - "%1$s ਨੂੰ ਫੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ" + "%1$s ਨੂੰ ਫ਼ੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ" "ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ" "ਸਥਾਪਤ ਕਰੋ" "ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।" @@ -86,22 +85,22 @@ "ਸੈਟਿੰਗਾਂ ਬਦਲੋ" "ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" - "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" + "ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" "ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ" "ਵਰਗ" "ਵਰਗਾਕਾਰ-ਚੱਕਰ" "ਚੱਕਰ" "ਹੰਝੂ ਦੀ ਬੂੰਦ" - "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + "ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" "ਅਗਿਆਤ" "ਹਟਾਓ" "ਖੋਜੋ" "ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।" - "ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।" + "ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।" "%1$s ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, %2$s ਸੰਪੂਰਣ" - "%1$s ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" + "%1$s ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" "%1$s ਵਿਜੇਟ" - "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" "ਅਈਟਮ ਹਟਾਈ ਗਈ" @@ -110,8 +109,8 @@ "ਸਥਿਤੀ %1$s ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਮਨਪਸੰਦ ਸਥਿਤੀ %1$s ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਆਈਟਮ ਮੂਵ ਕੀਤੀ ਗਈ" - "ਇਸ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜੋ: %1$s" - "%1$s ਦੇ ਨਾਲ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜੋ" + "ਇਸ ਫੋਲਡਰ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ: %1$s" + "%1$s ਦੇ ਨਾਲ ਫੋਲਡਰ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" "ਇਸਦੇ ਨਾਲ ਫੋਲਡਰ ਬਣਾਓ: %1$s" "ਫੋਲਡਰ ਬਣਾਇਆ ਗਿਆ" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 486a94e31c..2a01d04e0a 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -47,8 +47,7 @@ "Usuń" "Odinstaluj" "O aplikacji" - - + "Zainstaluj" "instalowanie skrótów" "Pozwala aplikacji dodawać skróty bez interwencji użytkownika." "odczytywanie ustawień i skrótów na ekranie głównym" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 1916b20147..ee4bf081d0 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -47,8 +47,7 @@ "Remover" "Desinstalar" "Inf. da aplicação" - - + "Instalar" "instalar atalhos" "Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador." "ler definições e atalhos do Ecrã Principal" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 4a09cd52e7..b550f6fc2f 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -47,8 +47,7 @@ "Remover" "Desinstalar" "Informações do app" - - + "Instalar" "instalar atalhos" "Permite que um app adicione atalhos sem intervenção do usuário." "ler configurações e atalhos da tela inicial" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 43ce4c8f76..9cf1cc682c 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -47,8 +47,7 @@ "Eliminați" "Dezinstalați" "Informații aplicație" - - + "Instalați" "instalează comenzi rapide" "Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului." "citește setări și comenzi rapide pentru ecranul de pornire" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 8bf71d631c..bfd014e050 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -47,8 +47,7 @@ "Убрать" "Удалить" "О приложении" - - + "Установить" "Создание ярлыков" "Приложение сможет самостоятельно добавлять ярлыки." "Доступ к настройкам и ярлыкам главного экрана" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index fbdf5bfe1f..349b7ca21d 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -47,8 +47,7 @@ "ඉවත් කරන්න" "අස්ථාපනය කරන්න" "යෙදුම් තොරතුරු" - - + "ස්ථාපනය කරන්න" "කෙටිමං ස්ථාපනය කරන්න" "පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි." "මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 4268bad522..ba84811c83 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -47,8 +47,7 @@ "Odstrániť" "Odinštalovať" "Info o aplikácii" - - + "Inštalovať" "inštalovať odkazy" "Povoľuje aplikácii pridať odkazy bez zásahu používateľa." "čítanie nastavení a odkazov plochy" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index c7abd88638..c317c0ea1c 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -47,8 +47,7 @@ "Odstrani" "Odstrani" "Podatki o aplikaciji" - - + "Namesti" "namestitev bližnjic" "Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika." "branje nastavitev in bližnjic na začetnem zaslonu" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 7b3a9095bf..ed07912bf1 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -47,8 +47,7 @@ "Hiqe" "Çinstalo" "Informacion mbi aplikacionin" - - + "Instalo" "instalo shkurtore" "Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit." "lexo cilësimet dhe shkurtoret e ekranit bazë" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 76f28e03e0..f40366f486 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -47,8 +47,7 @@ "Уклони" "Деинсталирај" "Информ. о апликацији" - - + "Инсталирај" "инсталирање пречица" "Дозвољава апликацији да додаје пречице без интервенције корисника." "читање подешавања и пречица на почетном екрану" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 354d7099e9..ff6b4e80f7 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -47,8 +47,7 @@ "Ta bort" "Avinstallera" "Info om appen" - - + "Installera" "installera genvägar" "Tillåter att en app lägger till genvägar utan åtgärd från användaren." "läsa inställningar och genvägar för startsidan" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index eb2845dab9..2f0bbf755b 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -47,8 +47,7 @@ "Ondoa" "Ondoa" "Maelezo ya programu" - - + "Sakinisha" "kuweka njia za mkato" "Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati." "soma mipangilio ya Mwanzo na njia za mkato" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index c3401aa558..5d72030ca7 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -47,8 +47,7 @@ "அகற்று" "நிறுவல் நீக்கு" "ஆப்ஸ் தகவல்" - - + "நிறுவு" "குறுக்குவழிகளை நிறுவுதல்" "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது." "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 79314e10f2..4db5352e51 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -47,8 +47,7 @@ "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" "యాప్ సమాచారం" - - + "ఇన్‌స్టాల్ చేయండి" "సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం" "వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 10c0fa2d81..ae5aeb5160 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -47,8 +47,7 @@ "นำออก" "ถอนการติดตั้ง" "ข้อมูลแอป" - - + "ติดตั้ง" "ติดตั้งทางลัด" "อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ" "อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 3a328aa603..11f13b097e 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -47,8 +47,7 @@ "Alisin" "I-uninstall" "Impormasyon ng app" - - + "I-install" "i-install ang mga shortcut" "Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user." "basahin ang mga setting at shortcut ng Home" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 5c2d68157a..f19cd78476 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -47,8 +47,7 @@ "Kaldır" "Yüklemeyi kaldır" "Uygulama bilgileri" - - + "Yükle" "kısayolları yükle" "Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir." "Ana ekran ayarlarını ve kısayollarını oku" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 8cb05ea8ee..08f15756bf 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -47,8 +47,7 @@ "Видалити" "Видалити" "Про додаток" - - + "Установити" "створення ярликів" "Дозволяє програмі самостійно додавати ярлики." "читати налаштування та ярлики головного екрана" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 6011d51edf..99b8c806f4 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -47,8 +47,7 @@ "ہٹائیں" "اَن انسٹال کریں" "ایپ کی معلومات" - - + "انسٹال کریں" "شارٹ کٹس انسٹال کریں" "کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔" "ہوم ترتیبات اور شارٹ کٹس کو پڑھیں" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index d289642195..5167670dfd 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -47,8 +47,7 @@ "Olib tashlash" "O‘chirib tashlash" "Ilova haqida" - - + "O‘rnatish" "yorliqlar yaratish" "Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi." "Uy sozlamalari va yorliqlarini o‘qish" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index b06646d253..c0e1454527 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -47,8 +47,7 @@ "Xóa" "Gỡ cài đặt" "Thông tin ứng dụng" - - + "Cài đặ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." "đọc cài đặt và lối tắt trên Màn hình chính" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 47e550668e..2342133472 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -47,8 +47,7 @@ "移除" "卸载" "应用信息" - - + "安装" "安装快捷方式" "允许应用自行添加快捷方式。" "读取主屏幕设置和快捷方式" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index c5e09c5450..f87ff7fe69 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -47,8 +47,7 @@ "移除" "解除安裝" "應用程式資料" - - + "安裝" "安裝捷徑" "允許應用程式無需使用者許可也可新增捷徑。" "讀取主畫面的設定和捷徑" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index ae8df098b0..814a6e48f2 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -47,8 +47,7 @@ "移除" "解除安裝" "應用程式資訊" - - + "安裝" "安裝捷徑" "允許應用程式自動新增捷徑。" "讀取主螢幕的設定和捷徑" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 65e2561da2..35fa1c7861 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -47,8 +47,7 @@ "Susa" "Khipha" "Ulwazi lohlelo lokusebenza" - - + "Faka" "faka izinqamuleli" "Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi." "funda izilungiselelo zokuthi Ikhaya nezinqamuleli" From ad7ff440092e5bf3a013299755ef0137edd407ae Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 Sep 2017 12:42:26 -0700 Subject: [PATCH 019/885] Unifying various icon loading methods Change-Id: I5a8969b6aad6513d769b5bb38bf95a701fe346d8 --- .../android/launcher3/AutoInstallsLayout.java | 8 +- .../launcher3/graphics/LauncherIcons.java | 89 ++++++------------- 2 files changed, 34 insertions(+), 63 deletions(-) diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index d82579bf77..162aa0889c 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -28,7 +28,9 @@ import android.content.res.XmlResourceParser; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.Process; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -433,8 +435,10 @@ public class AutoInstallsLayout { return -1; } - mValues.put(LauncherSettings.Favorites.ICON, - Utilities.flattenBitmap(LauncherIcons.createIconBitmap(icon, mContext))); + // Auto installs should always support the current platform version. + mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap( + LauncherIcons.createBadgedIconBitmap( + icon, Process.myUserHandle(), mContext, Build.VERSION.SDK_INT))); mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId)); mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId)); diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java index d55baf0f8c..d471af6e74 100644 --- a/src/com/android/launcher3/graphics/LauncherIcons.java +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -16,10 +16,12 @@ package com.android.launcher3.graphics; +import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; @@ -70,11 +72,15 @@ public class LauncherIcons { PackageManager packageManager = context.getPackageManager(); // the resource try { - Resources resources = packageManager.getResourcesForApplication(iconRes.packageName); + Resources resources = packageManager.getResourcesForApplication(iconRes.resourceName); if (resources != null) { final int id = resources.getIdentifier(iconRes.resourceName, null, null); - return createIconBitmap(resources.getDrawableForDensity( - id, LauncherAppState.getIDP(context).fillResIconDpi), context); + // do not stamp old legacy shortcuts as the app may have already forgotten about it + return createBadgedIconBitmap(resources.getDrawableForDensity( + id, LauncherAppState.getIDP(context).fillResIconDpi), + Process.myUserHandle() /* only available on primary user */, + context, + 0 /* do not apply legacy treatment */); } } catch (Exception e) { // Icon not found. @@ -90,11 +96,12 @@ public class LauncherIcons { if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { return icon; } - return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context); + return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f); } /** - * Returns a bitmap suitable for the all apps view. The icon is badged for {@param user}. + * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps + * view or workspace. The icon is badged for {@param user}. * The bitmap is also visually normalized with other icons. */ public static Bitmap createBadgedIconBitmap( @@ -127,24 +134,18 @@ public class LauncherIcons { icon instanceof AdaptiveIconDrawable) { bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap); } - return badgeIconForUser(bitmap, user, context); - } - /** - * Badges the provided icon with the user badge if required. - */ - public static Bitmap badgeIconForUser(Bitmap icon, UserHandle user, Context context) { if (user != null && !Process.myUserHandle().equals(user)) { - BitmapDrawable drawable = new FixedSizeBitmapDrawable(icon); + BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap); Drawable badged = context.getPackageManager().getUserBadgedIcon( drawable, user); if (badged instanceof BitmapDrawable) { return ((BitmapDrawable) badged).getBitmap(); } else { - return createIconBitmap(badged, context); + return createIconBitmap(badged, context, 1f); } } else { - return icon; + return bitmap; } } @@ -152,7 +153,8 @@ public class LauncherIcons { * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually * normalized with other icons and has enough spacing to add shadow. */ - public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context, int iconAppTargetSdk) { + public static Bitmap createScaledBitmapWithoutShadow( + Drawable icon, Context context, int iconAppTargetSdk) { RectF iconBounds = new RectF(); IconNormalizer normalizer; float scale = 1f; @@ -181,21 +183,6 @@ public class LauncherIcons { return createIconBitmap(icon, context, scale); } - /** - * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using - * {@link #createScaledBitmapWithoutShadow(Drawable, Context, int)} - */ - public static Bitmap addShadowToIcon(Bitmap icon, Context context) { - return ShadowGenerator.getInstance(context).recreateIcon(icon); - } - - /** - * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions. - */ - public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) { - return badgeWithDrawable(srcTgt, new FastBitmapDrawable(badge), context); - } - public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) { int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size); synchronized (sCanvas) { @@ -208,27 +195,10 @@ public class LauncherIcons { return srcTgt; } - /** - * Returns a bitmap suitable for the all apps view. - */ - public static Bitmap createIconBitmap(Drawable icon, Context context) { - float scale = 1f; - if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO && - icon instanceof AdaptiveIconDrawable) { - scale = ShadowGenerator.getScaleForBounds(new RectF(0, 0, 0, 0)); - } - Bitmap bitmap = createIconBitmap(icon, context, scale); - if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO && - icon instanceof AdaptiveIconDrawable) { - bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap); - } - return bitmap; - } - /** * @param scale the scale to apply before drawing {@param icon} on the canvas */ - public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) { + private static Bitmap createIconBitmap(Drawable icon, Context context, float scale) { synchronized (sCanvas) { final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize; int width = iconBitmapSize; @@ -295,7 +265,9 @@ public class LauncherIcons { * shrink the legacy icon and set it as foreground. Use color drawable as background to * create AdaptiveIconDrawable. */ - static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable, float scale) { + @TargetApi(Build.VERSION_CODES.O) + private static Drawable wrapToAdaptiveIconDrawable( + Context context, Drawable drawable, float scale) { if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) { return drawable; } @@ -307,7 +279,7 @@ public class LauncherIcons { FixedScaleDrawable fsd = ((FixedScaleDrawable) iconWrapper.getForeground()); fsd.setDrawable(drawable); fsd.setScale(scale); - return (Drawable) iconWrapper; + return iconWrapper; } } catch (Exception e) { return drawable; @@ -326,15 +298,9 @@ public class LauncherIcons { public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context, final Bitmap fallbackIcon) { - Provider fallbackIconProvider = new Provider() { - @Override - public Bitmap get() { - // If the shortcut is pinned but no longer has an icon in the system, - // keep the current icon instead of reverting to the default icon. - return fallbackIcon; - } - }; - return createShortcutIcon(shortcutInfo, context, true, fallbackIconProvider); + // If the shortcut is pinned but no longer has an icon in the system, + // keep the current icon instead of reverting to the default icon. + return createShortcutIcon(shortcutInfo, context, true, Provider.of(fallbackIcon)); } public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context, @@ -360,8 +326,9 @@ public class LauncherIcons { if (!badged) { return unbadgedBitmap; } - unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap, context); - return badgeWithBitmap(unbadgedBitmap, getShortcutInfoBadge(shortcutInfo, cache), context); + unbadgedBitmap = ShadowGenerator.getInstance(context).recreateIcon(unbadgedBitmap); + return badgeWithDrawable(unbadgedBitmap, + new FastBitmapDrawable(getShortcutInfoBadge(shortcutInfo, cache)), context); } public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) { From 952e63d0065d79d2bd9557ed7f28e96adb39ca9a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 16 Aug 2017 04:59:08 -0700 Subject: [PATCH 020/885] Fixing custom widgets support: > Moving the definitions to xml so that it is easier to override in derivative projects > Fixing verious bind and save logic for custom widgets > Adding feature flag to easily disable custom widgets Change-Id: I0e278bc7dd415713029364060ef10842da990be9 --- res/values/attrs.xml | 15 ++ res/values/dimens.xml | 1 - .../custom_widgets.xml} | 36 ++--- .../launcher3/AppWidgetResizeFrame.java | 11 +- .../android/launcher3/CustomAppWidget.java | 14 -- src/com/android/launcher3/Launcher.java | 37 ++--- .../launcher3/LauncherAppWidgetHost.java | 2 +- .../launcher3/LauncherAppWidgetInfo.java | 12 +- .../LauncherAppWidgetProviderInfo.java | 51 ++----- .../launcher3/WidgetPreviewLoader.java | 4 +- src/com/android/launcher3/Workspace.java | 2 +- .../compat/AppWidgetManagerCompat.java | 14 +- .../compat/AppWidgetManagerCompatVL.java | 33 ++++ .../android/launcher3/config/BaseFlags.java | 3 + .../model/GridSizeMigrationTask.java | 2 +- .../launcher3/testing/DummyWidget.java | 53 ------- .../android/launcher3/util/TestingUtils.java | 19 --- .../widget/PendingAddWidgetInfo.java | 4 +- .../widget/WidgetHostViewLoader.java | 2 +- .../custom/CustomAppWidgetProviderInfo.java | 103 +++++++++++++ .../widget/custom/CustomWidgetParser.java | 142 ++++++++++++++++++ .../launcher3/ui/widget/BindWidgetTest.java | 8 +- 22 files changed, 363 insertions(+), 205 deletions(-) rename res/{layout/zzz_dummy_widget.xml => xml/custom_widgets.xml} (50%) delete mode 100644 src/com/android/launcher3/CustomAppWidget.java delete mode 100644 src/com/android/launcher3/testing/DummyWidget.java create mode 100644 src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java create mode 100644 src/com/android/launcher3/widget/custom/CustomWidgetParser.java diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 68b628fb79..34385b053f 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -143,4 +143,19 @@ + + + + + + + + + + + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b1f9d63797..b403fbdc2d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -54,7 +54,6 @@ 14dp - 8dp 13dp 24dp diff --git a/res/layout/zzz_dummy_widget.xml b/res/xml/custom_widgets.xml similarity index 50% rename from res/layout/zzz_dummy_widget.xml rename to res/xml/custom_widgets.xml index a0fa8fc3e4..4b54386501 100644 --- a/res/layout/zzz_dummy_widget.xml +++ b/res/xml/custom_widgets.xml @@ -1,5 +1,6 @@ - - - - - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index a486a3aa34..d0b1c3082c 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -8,7 +8,6 @@ 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.Point; import android.graphics.Rect; import android.util.AttributeSet; @@ -126,14 +125,8 @@ public class AppWidgetResizeFrame extends FrameLayout mMinHSpan = info.minSpanX; mMinVSpan = info.minSpanY; - if (!info.isCustomWidget) { - mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), - widgetView.getAppWidgetInfo().provider, null); - } else { - Resources r = getContext().getResources(); - int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding); - mWidgetPadding = new Rect(padding, padding, padding, padding); - } + mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), + widgetView.getAppWidgetInfo().provider, null); if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) { mDragHandles[INDEX_TOP].setVisibility(GONE); diff --git a/src/com/android/launcher3/CustomAppWidget.java b/src/com/android/launcher3/CustomAppWidget.java deleted file mode 100644 index 1b4ed79c0b..0000000000 --- a/src/com/android/launcher3/CustomAppWidget.java +++ /dev/null @@ -1,14 +0,0 @@ -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/Launcher.java b/src/com/android/launcher3/Launcher.java index 8d9c29f981..76f5cda357 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -136,6 +136,7 @@ import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; +import com.android.launcher3.widget.custom.CustomWidgetParser; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -296,15 +297,6 @@ public class Launcher extends BaseActivity // the press state and keep this reference to reset the press state when we return to launcher. private BubbleTextView mWaitingForResume; - protected static final HashMap sCustomAppWidgets = - new HashMap<>(); - - static { - if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) { - TestingUtils.addDummyWidget(sCustomAppWidgets); - } - } - // Exiting spring loaded mode happens with a delay. This runnable object triggers the // state transition. If another state transition happened during this delay, // simply unregister this runnable. @@ -1408,17 +1400,13 @@ public class Launcher extends BaseActivity appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId); } - if (appWidgetInfo.isCustomWidget) { - appWidgetId = LauncherAppWidgetInfo.CUSTOM_WIDGET_ID; - } - LauncherAppWidgetInfo launcherInfo; launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider); launcherInfo.spanX = itemInfo.spanX; launcherInfo.spanY = itemInfo.spanY; launcherInfo.minSpanX = itemInfo.minSpanX; launcherInfo.minSpanY = itemInfo.minSpanY; - launcherInfo.user = appWidgetInfo.getUser(); + launcherInfo.user = appWidgetInfo.getProfile(); getModelWriter().addItemToDatabase(launcherInfo, itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY); @@ -1968,7 +1956,7 @@ public class Launcher extends BaseActivity */ private void addAppWidgetFromDrop(PendingAddWidgetInfo info) { AppWidgetHostView hostView = info.boundWidget; - int appWidgetId; + final int appWidgetId; WidgetAddFlowHandler addFlowHandler = info.getHandler(); if (hostView != null) { // In the case where we've prebound the widget, we remove it from the DragLayer @@ -1985,7 +1973,13 @@ public class Launcher extends BaseActivity } 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. - appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && + info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { + appWidgetId = CustomWidgetParser.getWidgetIdForCustomProvider( + this, info.componentName); + } else { + appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + } Bundle options = info.bindOptions; boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( @@ -3192,7 +3186,8 @@ public class Launcher extends BaseActivity (FolderInfo) item); break; } - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: { + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: { view = inflateAppWidget((LauncherAppWidgetInfo) item); if (view == null) { continue; @@ -3882,14 +3877,6 @@ public class Launcher extends BaseActivity return super.onKeyShortcut(keyCode, event); } - public static CustomAppWidget getCustomAppWidget(String name) { - return sCustomAppWidgets.get(name); - } - - public static HashMap getCustomAppWidgets() { - return sCustomAppWidgets; - } - public static Launcher getLauncher(Context context) { if (context instanceof Launcher) { return (Launcher) context; diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 5573c5c151..819f23faeb 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -116,7 +116,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { public AppWidgetHostView createView(Context context, int appWidgetId, LauncherAppWidgetProviderInfo appWidget) { - if (appWidget.isCustomWidget) { + if (appWidget.isCustomWidget()) { LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index 6f23e56b34..051846c871 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -71,7 +71,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { /** * Indicates that this is a locally defined widget and hence has no system allocated id. */ - static final int CUSTOM_WIDGET_ID = -100; + public static final int CUSTOM_WIDGET_ID = -100; /** * Identifier for this widget when talking with @@ -104,15 +104,15 @@ public class LauncherAppWidgetInfo extends ItemInfo { private boolean mHasNotifiedInitialWidgetSizeChanged; public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) { - if (appWidgetId == CUSTOM_WIDGET_ID) { + this.appWidgetId = appWidgetId; + this.providerName = providerName; + + if (isCustomWidget()) { itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } else { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; } - this.appWidgetId = appWidgetId; - this.providerName = providerName; - // Since the widget isn't instantiated yet, we don't know these values. Set them to -1 // to indicate that they should be calculated based on the layout and minWidth/minHeight spanX = -1; @@ -128,7 +128,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { } public boolean isCustomWidget() { - return appWidgetId == CUSTOM_WIDGET_ID; + return appWidgetId <= CUSTOM_WIDGET_ID; } @Override diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index 6cb703b43a..c7139925c8 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -2,15 +2,11 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.Parcel; -import android.os.Process; -import android.os.UserHandle; /** * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords @@ -20,7 +16,7 @@ import android.os.UserHandle; */ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { - public boolean isCustomWidget = false; + public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-"; public int spanX; public int spanY; @@ -48,22 +44,12 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { return launcherInfo; } - private LauncherAppWidgetProviderInfo(Parcel in) { + protected LauncherAppWidgetProviderInfo() {} + + protected 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(); - initSpans(context); - } - public void initSpans(Context context) { InvariantDeviceProfile idp = LauncherAppState.getIDP(context); @@ -97,34 +83,15 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { } public String getLabel(PackageManager packageManager) { - if (isCustomWidget) { - return Utilities.trim(label); - } return super.loadLabel(packageManager); } - public Drawable getIcon(Context context, IconCache cache) { - if (isCustomWidget) { - return cache.getFullResIcon(provider.getPackageName(), icon); - } - return super.loadIcon(context, LauncherAppState.getIDP(context).fillResIconDpi); + public Point getMinSpans() { + return new Point((resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1, + (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1); } - public String toString(PackageManager pm) { - if (isCustomWidget) { - return "WidgetProviderInfo(" + provider + ")"; - } - return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s", - provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm)); - } - - public Point getMinSpans(InvariantDeviceProfile idp, Context context) { - return new Point( - (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1, - (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1); - } - - public UserHandle getUser() { - return isCustomWidget ? Process.myUserHandle() : getProfile(); + public boolean isCustomWidget() { + return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX); } } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index a65ea9b10b..f150c89c1c 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -22,7 +22,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.os.Build; import android.os.CancellationSignal; import android.os.Handler; import android.os.UserHandle; @@ -412,7 +411,8 @@ public class WidgetPreviewLoader { // Draw icon in the center. try { - Drawable icon = info.getIcon(launcher, mIconCache); + Drawable icon = + mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon); if (icon != null) { int appIconSize = launcher.getDeviceProfile().iconSizePx; int iconSize = (int) Math.min(appIconSize * scale, diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d7f709932e..c432a53727 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3641,7 +3641,7 @@ public class Workspace extends PagedView .getInstance(mLauncher).findProvider(item.providerName, item.user); } else { widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher) - .getAppWidgetInfo(item.appWidgetId); + .getLauncherAppWidgetInfo(item.appWidgetId); } if (widgetInfo != null) { diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index a77a87f2c4..fd1f0cca2e 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -24,10 +24,13 @@ import android.os.Bundle; import android.os.UserHandle; import android.support.annotation.Nullable; +import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.custom.CustomWidgetParser; import java.util.HashMap; import java.util.List; @@ -58,12 +61,13 @@ public abstract class AppWidgetManagerCompat { mAppWidgetManager = AppWidgetManager.getInstance(context); } - public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { - return mAppWidgetManager.getAppWidgetInfo(appWidgetId); - } - public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) { - AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId); + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS + && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { + return CustomWidgetParser.getWidgetProvider(mContext, appWidgetId); + } + + AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId); return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index cb3bd6c7d6..843028503e 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -20,14 +20,17 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.Nullable; +import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.custom.CustomWidgetParser; import java.util.ArrayList; import java.util.Collections; @@ -54,6 +57,10 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { for (UserHandle user : mUserManager.getUserProfiles()) { providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user)); } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) { + providers.addAll(CustomWidgetParser.getCustomWidgets(mContext)); + } return providers; } // Only get providers for the given package/user. @@ -65,6 +72,11 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { iterator.remove(); } } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(packageUser.mUser) + && mContext.getPackageName().equals(packageUser.mPackageName)) { + providers.addAll(CustomWidgetParser.getCustomWidgets(mContext)); + } return providers; } @@ -74,6 +86,11 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { if (FeatureFlags.GO_DISABLE_WIDGETS) { return false; } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS + && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { + return true; + } return mAppWidgetManager.bindAppWidgetIdIfAllowed( appWidgetId, info.getProfile(), info.provider, options); } @@ -89,6 +106,15 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); } } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(user)) { + for (LauncherAppWidgetProviderInfo info : + CustomWidgetParser.getCustomWidgets(mContext)) { + if (info.provider.equals(provider)) { + return info; + } + } + } return null; } @@ -104,6 +130,13 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { result.put(new ComponentKey(info.provider, user), info); } } + + if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) { + for (LauncherAppWidgetProviderInfo info : + CustomWidgetParser.getCustomWidgets(mContext)) { + result.put(new ComponentKey(info.provider, info.getProfile()), info); + } + } return result; } } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 5adeec1c6e..5f6909c705 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -61,6 +61,9 @@ abstract class BaseFlags { // When enabled, the qsb will be moved to the hotseat. public static final boolean QSB_IN_HOTSEAT = true; + // When true, custom widgets are loaded using CustomWidgetParser. + public static final boolean ENABLE_CUSTOM_WIDGETS = false; + // Features to control Launcher3Go behavior public static final boolean GO_DISABLE_WIDGETS = false; } diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index 8de0de053f..d9b1a3f9e7 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -726,7 +726,7 @@ public class GridSizeMigrationTask { mContext).getLauncherAppWidgetInfo(widgetId); Point spans = null; if (pInfo != null) { - spans = pInfo.getMinSpans(mIdp, mContext); + spans = pInfo.getMinSpans(); } if (spans != null) { entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX; diff --git a/src/com/android/launcher3/testing/DummyWidget.java b/src/com/android/launcher3/testing/DummyWidget.java deleted file mode 100644 index df887ac1fc..0000000000 --- a/src/com/android/launcher3/testing/DummyWidget.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.android.launcher3.testing; - -import android.appwidget.AppWidgetProviderInfo; - -import com.android.launcher3.CustomAppWidget; -import com.android.launcher3.R; - -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.zzz_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/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java index a7cc42b5fa..d927dc3351 100644 --- a/src/com/android/launcher3/util/TestingUtils.java +++ b/src/com/android/launcher3/util/TestingUtils.java @@ -3,18 +3,14 @@ package com.android.launcher3.util; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; -import com.android.launcher3.CustomAppWidget; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import java.util.HashMap; - public class TestingUtils { public static final String MEMORY_TRACKER = "com.android.launcher3.testing.MemoryTracker"; @@ -23,9 +19,6 @@ public class TestingUtils { public static final boolean MEMORY_DUMP_ENABLED = false; public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem"; - public static final boolean ENABLE_CUSTOM_WIDGET_TEST = false; - public static final String DUMMY_WIDGET = "com.android.launcher3.testing.DummyWidget"; - public static void startTrackingMemory(Context context) { if (MEMORY_DUMP_ENABLED) { context.startService(new Intent() @@ -55,16 +48,4 @@ public class TestingUtils { launcher.mWeightWatcher = watcher; } } - - public static void addDummyWidget(HashMap set) { - if (ENABLE_CUSTOM_WIDGET_TEST) { - try { - Class clazz = Class.forName(DUMMY_WIDGET); - CustomAppWidget widget = (CustomAppWidget) clazz.newInstance(); - set.put(widget.getClass().getName(), widget); - } catch (Exception e) { - Log.e("TestingUtils", "Error adding dummy widget", e); - } - } - } } diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index ad05ce9171..bc404842eb 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -35,13 +35,13 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { public Bundle bindOptions = null; public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i) { - if (i.isCustomWidget) { + if (i.isCustomWidget()) { itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; } else { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; } this.info = i; - user = i.getUser(); + user = i.getProfile(); componentName = i.provider; previewImage = i.previewImage; icon = i.icon; diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 5eeea44b89..e6f8bb68f5 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -85,7 +85,7 @@ public class WidgetHostViewLoader implements DragController.DragListener { private boolean preloadWidget() { final LauncherAppWidgetProviderInfo pInfo = mInfo.info; - if (pInfo.isCustomWidget) { + if (pInfo.isCustomWidget()) { return false; } final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo); diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java new file mode 100644 index 0000000000..1086987f83 --- /dev/null +++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.widget.custom; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.Utilities; + +/** + * Custom app widget provider info that can be used as a widget, but provide extra functionality + * by allowing custom code and views. + */ +public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo + implements Parcelable { + + public final int providerId; + + protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, int providerId) { + super(parcel); + if (readSelf) { + this.providerId = parcel.readInt(); + + provider = new ComponentName(parcel.readString(), CLS_CUSTOM_WIDGET_PREFIX + providerId); + + label = parcel.readString(); + initialLayout = parcel.readInt(); + icon = parcel.readInt(); + previewImage = parcel.readInt(); + + resizeMode = parcel.readInt(); + spanX = parcel.readInt(); + spanY = parcel.readInt(); + minSpanX = parcel.readInt(); + minSpanY = parcel.readInt(); + } else { + this.providerId = providerId; + } + } + + @Override + public void initSpans(Context context) { } + + @Override + public String getLabel(PackageManager packageManager) { + return Utilities.trim(label); + } + + @Override + public String toString() { + return "WidgetProviderInfo(" + provider + ")"; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(providerId); + out.writeString(provider.getPackageName()); + + out.writeString(label); + out.writeInt(initialLayout); + out.writeInt(icon); + out.writeInt(previewImage); + + out.writeInt(resizeMode); + out.writeInt(spanX); + out.writeInt(spanY); + out.writeInt(minSpanX); + out.writeInt(minSpanY); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + + @Override + public CustomAppWidgetProviderInfo createFromParcel(Parcel parcel) { + return new CustomAppWidgetProviderInfo(parcel, true, 0); + } + + @Override + public CustomAppWidgetProviderInfo[] newArray(int size) { + return new CustomAppWidgetProviderInfo[size]; + } + }; +} diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java new file mode 100644 index 0000000000..00720c4496 --- /dev/null +++ b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget.custom; + +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.Parcel; +import android.os.Process; +import android.util.SparseArray; +import android.util.Xml; + +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX; + +/** + * Utility class to parse {@ink CustomAppWidgetProviderInfo} definitions from xml + */ +public class CustomWidgetParser { + + private static List sCustomWidgets; + private static SparseArray sWidgetsIdMap; + + public static List getCustomWidgets(Context context) { + if (sCustomWidgets == null) { + // Synchronization not needed as it it safe to load multiple times + parseCustomWidgets(context); + } + + return sCustomWidgets; + } + + public static int getWidgetIdForCustomProvider(Context context, ComponentName provider) { + if (sWidgetsIdMap == null) { + parseCustomWidgets(context); + } + int index = sWidgetsIdMap.indexOfValue(provider); + if (index >= 0) { + return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - sWidgetsIdMap.keyAt(index); + } else { + return AppWidgetManager.INVALID_APPWIDGET_ID; + } + } + + public static LauncherAppWidgetProviderInfo getWidgetProvider(Context context, int widgetId) { + if (sWidgetsIdMap == null || sCustomWidgets == null) { + parseCustomWidgets(context); + } + ComponentName cn = sWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId); + for (LauncherAppWidgetProviderInfo info : sCustomWidgets) { + if (info.provider.equals(cn)) { + return info; + } + } + return null; + } + + private static void parseCustomWidgets(Context context) { + ArrayList widgets = new ArrayList<>(); + SparseArray idMap = new SparseArray<>(); + + List providers = AppWidgetManager.getInstance(context) + .getInstalledProvidersForProfile(Process.myUserHandle()); + if (providers.isEmpty()) { + sCustomWidgets = widgets; + sWidgetsIdMap = idMap; + return; + } + + Parcel parcel = Parcel.obtain(); + providers.get(0).writeToParcel(parcel, 0); + + try (XmlResourceParser parser = context.getResources().getXml(R.xml.custom_widgets)) { + 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) && "widget".equals(parser.getName())) { + TypedArray a = context.obtainStyledAttributes( + Xml.asAttributeSet(parser), R.styleable.CustomAppWidgetProviderInfo); + + parcel.setDataPosition(0); + CustomAppWidgetProviderInfo info = newInfo(a, parcel, context); + widgets.add(info); + a.recycle(); + + idMap.put(info.providerId, info.provider); + } + } + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + parcel.recycle(); + sCustomWidgets = widgets; + sWidgetsIdMap = idMap; + } + + private static CustomAppWidgetProviderInfo newInfo(TypedArray a, Parcel parcel, Context context) { + int providerId = a.getInt(R.styleable.CustomAppWidgetProviderInfo_providerId, 0); + CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false, providerId); + info.provider = new ComponentName(context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId); + + info.label = a.getString(R.styleable.CustomAppWidgetProviderInfo_android_label); + info.initialLayout = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_initialLayout, 0); + info.icon = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_icon, 0); + info.previewImage = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_previewImage, 0); + info.resizeMode = a.getInt(R.styleable.CustomAppWidgetProviderInfo_android_resizeMode, 0); + + info.spanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numColumns, 1); + info.spanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numRows, 1); + info.minSpanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinColumns, 1); + info.minSpanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinRows, 1); + return info; + } +} diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index d4d517a22c..87103d7133 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -16,6 +16,7 @@ package com.android.launcher3.ui.widget; import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; @@ -171,8 +172,9 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // Widget has a valid Id now. assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); - assertNotNull(mWidgetManager.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( - LauncherSettings.Favorites.APPWIDGET_ID)))); + assertNotNull(AppWidgetManager.getInstance(mTargetContext) + .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( + LauncherSettings.Favorites.APPWIDGET_ID)))); } @Test @@ -297,7 +299,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { item.spanY = info.minSpanY; item.minSpanX = info.minSpanX; item.minSpanY = info.minSpanY; - item.user = info.getUser(); + item.user = info.getProfile(); item.cellX = 0; item.cellY = 1; item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; From 318a8b626fff66708cee5ef6e3b4cc99eeb410eb Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 13 Sep 2017 17:15:59 -0700 Subject: [PATCH 021/885] Use tools:node="replace" instead of tools:merge="override" tools:merge="override" is no longer supported in Manifest Merger 2. Bug: 36005379 Test: m -j Launcher3Go Change-Id: I289f74f35876cdf274433ec1a579875217d9b9a8 --- go/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml index ed8e0ad3bc..fbaf981d33 100644 --- a/go/AndroidManifest.xml +++ b/go/AndroidManifest.xml @@ -42,7 +42,7 @@ android:excludeFromRecents="true" android:autoRemoveFromRecents="true" android:label="@string/action_add_to_workspace" - tools:merge="override" > + tools:node="replace" > From 693db5d70b29814d2795039bcecb5fbff0b731cb Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 13 Sep 2017 17:15:59 -0700 Subject: [PATCH 022/885] Use tools:node="replace" instead of tools:merge="override" tools:merge="override" is no longer supported in Manifest Merger 2. Bug: 36005379 Test: m -j Launcher3Go Change-Id: I289f74f35876cdf274433ec1a579875217d9b9a8 (cherry picked from commit 318a8b626fff66708cee5ef6e3b4cc99eeb410eb) --- go/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml index ed8e0ad3bc..fbaf981d33 100644 --- a/go/AndroidManifest.xml +++ b/go/AndroidManifest.xml @@ -42,7 +42,7 @@ android:excludeFromRecents="true" android:autoRemoveFromRecents="true" android:label="@string/action_add_to_workspace" - tools:merge="override" > + tools:node="replace" > From baaee5fd4e6ef03d0673b9b5ce5b36116d03a4a7 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 18 Sep 2017 14:44:18 -0700 Subject: [PATCH 023/885] Import translations. DO NOT MERGE Change-Id: I8b2409eb2a055992bd47f2c4f1959919333a9417 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- res/values-es/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 272bb61076..e7e1cf818c 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -84,7 +84,7 @@ "Para mostrar burbujas de notificación, activa las notificaciones de %1$s" "Cambiar ajustes" "Añadir icono a la pantalla de inicio" - "Para nuevas aplicaciones" + "Para aplicaciones nuevas" "Cambiar forma de los iconos" "Usar opción predeterminada del sistema" "Cuadrado" From 0cd04571d26f133900e7f42428d180371f23edce Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 19 Sep 2017 11:31:42 -0700 Subject: [PATCH 024/885] Change rule so that multi-window labels are shown if there is space. ie. Taimen has enough room to show the labels in mw mode. Bug: 65778773 Change-Id: If585f3f975b44d17ea6e07f23c50758998bbb564 --- src/com/android/launcher3/DeviceProfile.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index dec0a92a9e..824a554e22 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -277,8 +277,12 @@ public class DeviceProfile { DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y, isLandscape); - // Hide labels on the workspace. - profile.adjustToHideWorkspaceLabels(); + // If there isn't enough vertical cell padding with the labels displayed, hide the labels. + float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx + - iconDrawablePaddingPx - profile.iconTextSizePx; + if (workspaceCellPaddingY < profile.iconDrawablePaddingPx * 2) { + profile.adjustToHideWorkspaceLabels(); + } // We use these scales to measure and layout the widgets using their full invariant profile // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans. From dee55f92ad3c74797a0e5689d526b1258a3d90b9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 19 Sep 2017 12:05:54 -0700 Subject: [PATCH 025/885] Disabling state saving for search textbox as search results are not preserved across activity recreate Bug: 65661416 Change-Id: I0bd6414cea8d25b341374cd40ecaa270d5f19c18 --- res/layout/search_container_all_apps.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml index c4305214d5..fc07002cb5 100644 --- a/res/layout/search_container_all_apps.xml +++ b/res/layout/search_container_all_apps.xml @@ -19,11 +19,11 @@ android:layout_width="match_parent" android:layout_height="@dimen/all_apps_search_bar_height" android:layout_gravity="center|top" + android:layout_marginBottom="-8dp" android:gravity="center|bottom" - android:saveEnabled="false" android:paddingLeft="@dimen/dynamic_grid_edge_margin" android:paddingRight="@dimen/dynamic_grid_edge_margin" - android:layout_marginBottom="-8dp" > + android:saveEnabled="false" > - - 24dp diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index a6d80e336e..09f9e827cd 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -16,16 +16,9 @@ package com.android.launcher3; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.Color; import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -35,11 +28,9 @@ import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dynamicui.ExtractedColors; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.util.Themes; public class Hotseat extends FrameLayout implements UserEventDispatcher.LogContainerProvider { @@ -51,12 +42,6 @@ public class Hotseat extends FrameLayout @ViewDebug.ExportedProperty(category = "launcher") private final boolean mHasVerticalHotseat; - @ViewDebug.ExportedProperty(category = "launcher") - private int mBackgroundColor; - @ViewDebug.ExportedProperty(category = "launcher") - private ColorDrawable mBackground; - private ValueAnimator mBackgroundColorAnimator; - public Hotseat(Context context) { this(context, null); } @@ -69,12 +54,6 @@ public class Hotseat extends FrameLayout super(context, attrs, defStyle); mLauncher = Launcher.getLauncher(context); mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout(); - mBackgroundColor = ColorUtils.setAlphaComponent( - Themes.getAttrColor(context, android.R.attr.colorPrimary), 0); - mBackground = new ColorDrawable(mBackgroundColor); - if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - setBackground(mBackground); - } } public CellLayout getLayout() { @@ -177,49 +156,4 @@ public class Hotseat extends FrameLayout target.gridY = info.cellY; targetParent.containerType = ContainerType.HOTSEAT; } - - public void updateColor(ExtractedColors extractedColors, boolean animate) { - if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - // not hotseat visible - return; - } - if (!mHasVerticalHotseat) { - int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX); - if (mBackgroundColorAnimator != null) { - mBackgroundColorAnimator.cancel(); - } - if (!animate) { - setBackgroundColor(color); - } else { - mBackgroundColorAnimator = ValueAnimator.ofInt(mBackgroundColor, color); - mBackgroundColorAnimator.setEvaluator(new ArgbEvaluator()); - mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mBackground.setColor((Integer) animation.getAnimatedValue()); - } - }); - mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mBackgroundColorAnimator = null; - } - }); - mBackgroundColorAnimator.start(); - } - mBackgroundColor = color; - } - } - - public void setBackgroundTransparent(boolean enable) { - if (enable) { - mBackground.setAlpha(0); - } else { - mBackground.setAlpha(255); - } - } - - public int getBackgroundDrawableColor() { - return mBackgroundColor; - } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 99f3803b1c..b1b8566a2b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; +import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; + import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -97,7 +100,6 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.PinItemDragListener; -import com.android.launcher3.dynamicui.ExtractedColors; import com.android.launcher3.dynamicui.WallpaperColorInfo; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; @@ -118,7 +120,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.ActivityResultInfo; -import com.android.launcher3.util.RunnableWithId; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ComponentKeyMapper; import com.android.launcher3.util.ItemInfoMatcher; @@ -126,6 +127,7 @@ import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; +import com.android.launcher3.util.RunnableWithId; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Themes; @@ -142,15 +144,11 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; -import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; -import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; - /** * Default launcher application. */ @@ -223,8 +221,6 @@ public class Launcher extends BaseActivity private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500; - private final ExtractedColors mExtractedColors = new ExtractedColors(); - @Thunk Workspace mWorkspace; private View mLauncherView; @Thunk DragLayer mDragLayer; @@ -406,7 +402,6 @@ public class Launcher extends BaseActivity setupViews(); mDeviceProfile.layout(this, false /* notifyListeners */); - loadExtractedColorsAndColorItems(); mPopupDataProvider = new PopupDataProvider(this); @@ -493,12 +488,6 @@ public class Launcher extends BaseActivity return mLauncherView.findViewById(id); } - @Override - public void onExtractedColorsChanged() { - loadExtractedColorsAndColorItems(); - mExtractedColors.notifyChange(); - } - @Override public void onAppWidgetHostReset() { if (mAppWidgetHost != null) { @@ -506,15 +495,6 @@ public class Launcher extends BaseActivity } } - private void loadExtractedColorsAndColorItems() { - // TODO: do this in pre-N as well, once the extraction part is complete. - if (Utilities.ATLEAST_NOUGAT) { - mExtractedColors.load(this); - mHotseat.updateColor(mExtractedColors, !mPaused); - mWorkspace.getPageIndicator().updateColor(mExtractedColors); - } - } - private LauncherCallbacks mLauncherCallbacks; public void onPostCreate(Bundle savedInstanceState) { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 1ffe41bc6c..5df2dadec1 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -28,7 +28,6 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.ConfigMonitor; import com.android.launcher3.util.Preconditions; @@ -111,18 +110,11 @@ public class LauncherAppState { filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED); - // For extracting colors from the wallpaper - if (Utilities.ATLEAST_NOUGAT) { - // TODO: add a broadcast entry to the manifest for pre-N. - filter.addAction(Intent.ACTION_WALLPAPER_CHANGED); - } mContext.registerReceiver(mModel, filter); UserManagerCompat.getInstance(mContext).enableAndResetCache(); new ConfigMonitor(mContext).register(); - ExtractionUtils.startColorExtractionServiceIfNecessary(mContext); - if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) { mNotificationBadgingObserver = null; } else { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index a906b00f1f..74a5bac995 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -37,7 +37,6 @@ import android.util.Pair; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.model.AddWorkspaceItemsTask; import com.android.launcher3.model.BgDataModel; @@ -406,8 +405,6 @@ public class LauncherModel extends BroadcastReceiver enqueueModelUpdateTask(new UserLockStateChangedTask(user)); } } - } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) { - ExtractionUtils.startColorExtractionServiceIfNecessary(context); } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index dc83f36adf..6d158f71fb 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -55,7 +55,6 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.WorkspaceScreens; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.graphics.IconShapeOverride; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.DbDowngradeHelper; @@ -372,19 +371,6 @@ public class LauncherProvider extends ContentProvider { createDbIfNotExists(); switch (method) { - case LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID: { - String extractedColors = extras.getString( - LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS); - int wallpaperId = extras.getInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID); - Utilities.getPrefs(getContext()).edit() - .putString(ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, extractedColors) - .putInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, wallpaperId) - .apply(); - mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_EXTRACTED_COLORS_CHANGED); - Bundle result = new Bundle(); - result.putString(LauncherSettings.Settings.EXTRA_VALUE, extractedColors); - return result; - } case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: { clearFlagEmptyDbCreated(); return null; @@ -1153,8 +1139,7 @@ public class LauncherProvider extends ContentProvider { private static class ChangeListenerWrapper implements Handler.Callback { private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1; - private static final int MSG_EXTRACTED_COLORS_CHANGED = 2; - private static final int MSG_APP_WIDGET_HOST_RESET = 3; + private static final int MSG_APP_WIDGET_HOST_RESET = 2; private LauncherProviderChangeListener mListener; @@ -1165,9 +1150,6 @@ public class LauncherProvider extends ContentProvider { case MSG_LAUNCHER_PROVIDER_CHANGED: mListener.onLauncherProviderChanged(); break; - case MSG_EXTRACTED_COLORS_CHANGED: - mListener.onExtractedColorsChanged(); - break; case MSG_APP_WIDGET_HOST_RESET: mListener.onAppWidgetHostReset(); break; diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java index 704481232d..024308863a 100644 --- a/src/com/android/launcher3/LauncherProviderChangeListener.java +++ b/src/com/android/launcher3/LauncherProviderChangeListener.java @@ -9,7 +9,5 @@ public interface LauncherProviderChangeListener { void onLauncherProviderChanged(); - void onExtractedColorsChanged(); - void onAppWidgetHostReset(); } diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 87f62eb01b..3b337ef18e 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -304,11 +304,6 @@ public class LauncherSettings { public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites"; - public static final String METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID = - "set_extracted_colors_and_wallpaper_id_setting"; - public static final String EXTRA_EXTRACTED_COLORS = "extra_extractedColors"; - public static final String EXTRA_WALLPAPER_ID = "extra_wallpaperId"; - public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets"; public static final String EXTRA_VALUE = "value"; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 3364c61eea..f249c90c9b 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -6,14 +6,11 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; -import android.graphics.Color; import android.support.animation.SpringAnimation; -import android.support.v4.graphics.ColorUtils; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; @@ -46,12 +43,8 @@ import com.android.launcher3.util.TouchController; public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener, SearchUiManager.OnScrollRangeChangeListener { - private static final String TAG = "AllAppsTrans"; - private static final boolean DBG = false; - private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f); private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f); - private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f); private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator(); private final SwipeDetector.ScrollInterpolator mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); @@ -60,10 +53,8 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private static final int SINGLE_FRAME_MS = 16; private AllAppsContainerView mAppsView; - private int mAllAppsBackgroundColor; private Workspace mWorkspace; private Hotseat mHotseat; - private int mHotseatBackgroundColor; private AllAppsCaretController mCaretController; @@ -112,7 +103,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mProgress = 1f; mEvaluator = new ArgbEvaluator(); - mAllAppsBackgroundColor = Themes.getAttrColor(l, android.R.attr.colorPrimary); mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); } @@ -266,29 +256,16 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect // Initialize values that should not change until #onDragEnd mStatusBarHeight = mLauncher.getDragLayer().getInsets().top; mHotseat.setVisibility(View.VISIBLE); - mHotseatBackgroundColor = mHotseat.getBackgroundDrawableColor(); - mHotseat.setBackgroundTransparent(true /* transparent */); if (!mLauncher.isAllAppsVisible()) { mLauncher.tryAndUpdatePredictedApps(); mAppsView.setVisibility(View.VISIBLE); - if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - mAppsView.setRevealDrawableColor(mHotseatBackgroundColor); - } } } } private void updateLightStatusBar(float shift) { - // Do not modify status bar on landscape as all apps is not full bleed. - if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS - && mLauncher.getDeviceProfile().isVerticalBarLayout()) { - return; - } - // Use a light system UI (dark icons) if all apps is behind at least half of the status bar. - boolean forceChange = FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS ? - shift <= mShiftRange / 4 : - shift <= mStatusBarHeight / 2; + boolean forceChange = shift <= mShiftRange / 4; if (forceChange) { mLauncher.getSystemUiController().updateUiState( SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme); @@ -320,17 +297,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect float workspaceAlpha = mWorkspaceAccelnterpolator.getInterpolation(workspaceHotseatAlpha); float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha); - int color = (Integer) mEvaluator.evaluate(mDecelInterpolator.getInterpolation(alpha), - mHotseatBackgroundColor, mAllAppsBackgroundColor); - int bgAlpha = Color.alpha((int) mEvaluator.evaluate(alpha, - mHotseatBackgroundColor, mAllAppsBackgroundColor)); - - if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - updateAllAppsBg(alpha); - } else { - mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha)); - } - + updateAllAppsBg(alpha); mAppsView.getContentView().setAlpha(alpha); mAppsView.setTranslationY(shiftCurrent); @@ -530,7 +497,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect public void finishPullDown() { mAppsView.setVisibility(View.INVISIBLE); - mHotseat.setBackgroundTransparent(false /* transparent */); mHotseat.setVisibility(View.VISIBLE); mAppsView.reset(); if (hasSpringAnimationHandler()) { diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java index 8e572ee1aa..4cc70d3677 100644 --- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java +++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java @@ -15,6 +15,10 @@ */ package com.android.launcher3.compat; +import static android.app.WallpaperManager.FLAG_SYSTEM; + +import static com.android.launcher3.Utilities.getDevicePrefs; + import android.app.WallpaperInfo; import android.app.WallpaperManager; import android.app.job.JobInfo; @@ -38,7 +42,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.support.annotation.Nullable; -import android.support.v7.graphics.Palette; import android.util.Log; import android.util.Pair; @@ -46,12 +49,6 @@ import com.android.launcher3.Utilities; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import static android.app.WallpaperManager.FLAG_SYSTEM; -import static com.android.launcher3.Utilities.getDevicePrefs; public class WallpaperManagerCompatVL extends WallpaperManagerCompat { @@ -260,27 +257,8 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat { String value = VERSION_PREFIX + wallpaperId; if (bitmap != null) { - Palette palette = Palette.from(bitmap).generate(); - bitmap.recycle(); - - StringBuilder builder = new StringBuilder(value); - List> colorsToOccurrences = new ArrayList<>(); - for (Palette.Swatch swatch : palette.getSwatches()) { - colorsToOccurrences.add(new Pair(swatch.getRgb(), swatch.getPopulation())); - } - - Collections.sort(colorsToOccurrences, new Comparator>() { - @Override - public int compare(Pair a, Pair b) { - return b.second - a.second; - } - }); - - for (int i=0; i < Math.min(3, colorsToOccurrences.size()); i++) { - builder.append(',').append(colorsToOccurrences.get(i).first); - } - - value = builder.toString(); + int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA); + value += "," + color; } // Send the result diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 5f6909c705..8a1bc6388e 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -39,8 +39,6 @@ abstract class BaseFlags { public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false; // When enabled the promise icon is visible in all apps while installation an app. public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false; - // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps - public static final boolean LAUNCHER3_GRADIENT_ALL_APPS = true; // When enabled allows use of physics based motions in the Launcher. public static final boolean LAUNCHER3_PHYSICS = true; // When enabled allows use of spring motions on the icons. @@ -50,16 +48,12 @@ abstract class BaseFlags { public static final boolean QSB_ON_FIRST_SCREEN = true; // When enabled the all-apps icon is not added to the hotseat. public static final boolean NO_ALL_APPS_ICON = true; - // When enabled the status bar may show dark icons based on the top of the wallpaper. - public static final boolean LIGHT_STATUS_BAR = false; // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}. public static final boolean LEGACY_ICON_TREATMENT = true; // When enabled, adaptive icons would have shadows baked when being stored to icon cache. public static final boolean ADAPTIVE_ICON_SHADOW = true; // When enabled, app discovery will be enabled if service is implemented public static final boolean DISCOVERY_ENABLED = false; - // When enabled, the qsb will be moved to the hotseat. - public static final boolean QSB_IN_HOTSEAT = true; // When true, custom widgets are loaded using CustomWidgetParser. public static final boolean ENABLE_CUSTOM_WIDGETS = false; diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java deleted file mode 100644 index b9dd3b588f..0000000000 --- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2016 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.dynamicui; - -import android.annotation.TargetApi; -import android.app.WallpaperManager; -import android.app.job.JobParameters; -import android.app.job.JobService; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; -import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.ParcelFileDescriptor; -import android.support.v7.graphics.Palette; -import android.util.Log; - -import com.android.launcher3.LauncherProvider; -import com.android.launcher3.LauncherSettings; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; - -import java.io.IOException; - -/** - * Extracts colors from the wallpaper, and saves results to {@link LauncherProvider}. - */ -public class ColorExtractionService extends JobService { - - private static final String TAG = "ColorExtractionService"; - private static final boolean DEBUG = false; - - /** The fraction of the wallpaper to extract colors for use on the hotseat. */ - private static final float HOTSEAT_FRACTION = 1f / 4; - - private HandlerThread mWorkerThread; - private Handler mWorkerHandler; - - @Override - public void onCreate() { - super.onCreate(); - mWorkerThread = new HandlerThread("ColorExtractionService"); - mWorkerThread.start(); - mWorkerHandler = new Handler(mWorkerThread.getLooper()); - } - - @Override - public void onDestroy() { - super.onDestroy(); - mWorkerThread.quit(); - } - - @Override - public boolean onStartJob(final JobParameters jobParameters) { - if (DEBUG) Log.d(TAG, "onStartJob"); - mWorkerHandler.post(new Runnable() { - @Override - public void run() { - WallpaperManager wallpaperManager = WallpaperManager.getInstance( - ColorExtractionService.this); - int wallpaperId = ExtractionUtils.getWallpaperId(wallpaperManager); - - ExtractedColors extractedColors = new ExtractedColors(); - if (wallpaperManager.getWallpaperInfo() != null) { - // We can't extract colors from live wallpapers; always use the default color. - extractedColors.updateHotseatPalette(null); - - if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - extractedColors.updateWallpaperThemePalette(null); - } - } else { - // We extract colors for the hotseat and status bar separately, - // since they only consider part of the wallpaper. - extractedColors.updateHotseatPalette(getHotseatPalette()); - - if (FeatureFlags.LIGHT_STATUS_BAR) { - extractedColors.updateStatusBarPalette(getStatusBarPalette()); - } - - if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - extractedColors.updateWallpaperThemePalette(getWallpaperPalette()); - } - } - - // Save the extracted colors and wallpaper id to LauncherProvider. - String colorsString = extractedColors.encodeAsString(); - Bundle extras = new Bundle(); - extras.putInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID, wallpaperId); - extras.putString(LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS, colorsString); - getContentResolver().call( - LauncherSettings.Settings.CONTENT_URI, - LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID, - null, extras); - jobFinished(jobParameters, false /* needsReschedule */); - if (DEBUG) Log.d(TAG, "job finished!"); - } - }); - return true; - } - - @Override - public boolean onStopJob(JobParameters jobParameters) { - if (DEBUG) Log.d(TAG, "onStopJob"); - mWorkerHandler.removeCallbacksAndMessages(null); - return true; - } - - @TargetApi(Build.VERSION_CODES.N) - private Palette getHotseatPalette() { - WallpaperManager wallpaperManager = WallpaperManager.getInstance(this); - if (Utilities.ATLEAST_NOUGAT) { - try (ParcelFileDescriptor fd = wallpaperManager - .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) { - BitmapRegionDecoder decoder = BitmapRegionDecoder - .newInstance(fd.getFileDescriptor(), false); - int height = decoder.getHeight(); - Rect decodeRegion = new Rect(0, (int) (height * (1f - HOTSEAT_FRACTION)), - decoder.getWidth(), height); - Bitmap bitmap = decoder.decodeRegion(decodeRegion, null); - decoder.recycle(); - if (bitmap != null) { - return Palette.from(bitmap).clearFilters().generate(); - } - } catch (IOException | NullPointerException e) { - Log.e(TAG, "Fetching partial bitmap failed, trying old method", e); - } - } - - Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap(); - return Palette.from(wallpaper) - .setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)), - wallpaper.getWidth(), wallpaper.getHeight()) - .clearFilters() - .generate(); - } - - @TargetApi(Build.VERSION_CODES.N) - private Palette getStatusBarPalette() { - WallpaperManager wallpaperManager = WallpaperManager.getInstance(this); - int statusBarHeight = getResources() - .getDimensionPixelSize(R.dimen.status_bar_height); - - if (Utilities.ATLEAST_NOUGAT) { - try (ParcelFileDescriptor fd = wallpaperManager - .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) { - BitmapRegionDecoder decoder = BitmapRegionDecoder - .newInstance(fd.getFileDescriptor(), false); - Rect decodeRegion = new Rect(0, 0, - decoder.getWidth(), statusBarHeight); - Bitmap bitmap = decoder.decodeRegion(decodeRegion, null); - decoder.recycle(); - if (bitmap != null) { - return Palette.from(bitmap).clearFilters().generate(); - } - } catch (IOException | NullPointerException e) { - Log.e(TAG, "Fetching partial bitmap failed, trying old method", e); - } - } - - Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap(); - return Palette.from(wallpaper) - .setRegion(0, 0, wallpaper.getWidth(), statusBarHeight) - .clearFilters() - .generate(); - } - - @TargetApi(Build.VERSION_CODES.N) - private Palette getWallpaperPalette() { - WallpaperManager wallpaperManager = WallpaperManager.getInstance(this); - if (Utilities.ATLEAST_NOUGAT) { - try (ParcelFileDescriptor fd = wallpaperManager - .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) { - Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor()); - if (bitmap != null) { - return Palette.from(bitmap).clearFilters().generate(); - } - } catch (IOException | NullPointerException e) { - Log.e(TAG, "Fetching partial bitmap failed, trying old method", e); - } - } - - Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap(); - return Palette.from(wallpaper).clearFilters().generate(); - } -} diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java deleted file mode 100644 index 2d8bb869f4..0000000000 --- a/src/com/android/launcher3/dynamicui/ExtractedColors.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2016 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.dynamicui; - -import android.app.WallpaperManager; -import android.content.Context; -import android.graphics.Color; -import android.support.annotation.Nullable; -import android.support.v4.graphics.ColorUtils; -import android.support.v7.graphics.Palette; -import android.util.Log; - -import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Saves and loads colors extracted from the wallpaper, as well as the associated wallpaper id. - */ -public class ExtractedColors { - private static final String TAG = "ExtractedColors"; - - public static final int DEFAULT_LIGHT = Color.WHITE; - public static final int DEFAULT_DARK = Color.BLACK; - - // These color profile indices should NOT be changed, since they are used when saving and - // loading extracted colors. New colors should always be added at the end. - public static final int VERSION_INDEX = 0; - public static final int HOTSEAT_INDEX = 1; - public static final int STATUS_BAR_INDEX = 2; - public static final int WALLPAPER_VIBRANT_INDEX = 3; - public static final int ALLAPPS_GRADIENT_MAIN_INDEX = 4; - public static final int ALLAPPS_GRADIENT_SECONDARY_INDEX = 5; - - private static final int VERSION; - private static final int[] DEFAULT_VALUES; - - static { - if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - VERSION = 3; - DEFAULT_VALUES = new int[] { - VERSION, // VERSION_INDEX - 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha - DEFAULT_DARK, // STATUS_BAR_INDEX - 0xFFCCCCCC, // WALLPAPER_VIBRANT_INDEX - 0xFF000000, // ALLAPPS_GRADIENT_MAIN_INDEX - 0xFF000000 // ALLAPPS_GRADIENT_SECONDARY_INDEX - }; - } else if (FeatureFlags.QSB_IN_HOTSEAT) { - VERSION = 2; - DEFAULT_VALUES = new int[] { - VERSION, // VERSION_INDEX - 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha - DEFAULT_DARK, // STATUS_BAR_INDEX - 0xFFCCCCCC, // WALLPAPER_VIBRANT_INDEX - }; - } else { - VERSION = 1; - DEFAULT_VALUES = new int[] { - VERSION, // VERSION_INDEX - 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha - DEFAULT_DARK, // STATUS_BAR_INDEX - }; - } - } - - private static final String COLOR_SEPARATOR = ","; - - private final ArrayList mListeners = new ArrayList<>(); - private final int[] mColors; - - public ExtractedColors() { - // The first entry is reserved for the version number. - mColors = Arrays.copyOf(DEFAULT_VALUES, DEFAULT_VALUES.length); - } - - public void setColorAtIndex(int index, int color) { - if (index > VERSION_INDEX && index < mColors.length) { - mColors[index] = color; - } else { - Log.e(TAG, "Attempted to set a color at an invalid index " + index); - } - } - - /** - * Encodes {@link #mColors} as a comma-separated String. - */ - String encodeAsString() { - StringBuilder colorsStringBuilder = new StringBuilder(); - for (int color : mColors) { - colorsStringBuilder.append(color).append(COLOR_SEPARATOR); - } - return colorsStringBuilder.toString(); - } - - /** - * Loads colors and wallpaper id from {@link Utilities#getPrefs(Context)}. - * These were saved there in {@link ColorExtractionService}. - */ - public void load(Context context) { - String encodedString = Utilities.getPrefs(context).getString( - ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + ""); - - String[] splitColorsString = encodedString.split(COLOR_SEPARATOR); - if (splitColorsString.length == DEFAULT_VALUES.length && - Integer.parseInt(splitColorsString[VERSION_INDEX]) == VERSION) { - // Parse and apply the saved values. - for (int i = 0; i < mColors.length; i++) { - mColors[i] = Integer.parseInt(splitColorsString[i]); - } - } else { - // Leave the values as default values as the saved values may not be compatible. - ExtractionUtils.startColorExtractionService(context); - } - } - - /** @param index must be one of the index values defined at the top of this class. */ - public int getColor(int index) { - return mColors[index]; - } - - /** - * The hotseat's color is defined as follows: - * - 12% black for super light wallpaper - * - 18% white for super dark - * - 25% white otherwise - */ - public void updateHotseatPalette(Palette hotseatPalette) { - int hotseatColor; - if (hotseatPalette != null && ExtractionUtils.isSuperLight(hotseatPalette)) { - hotseatColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (0.12f * 255)); - } else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) { - hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255)); - } else { - hotseatColor = DEFAULT_VALUES[HOTSEAT_INDEX]; - } - setColorAtIndex(HOTSEAT_INDEX, hotseatColor); - } - - public void updateStatusBarPalette(Palette statusBarPalette) { - setColorAtIndex(STATUS_BAR_INDEX, ExtractionUtils.isSuperLight(statusBarPalette) ? - DEFAULT_LIGHT : DEFAULT_DARK); - } - - public void updateWallpaperThemePalette(@Nullable Palette wallpaperPalette) { - int defaultColor = DEFAULT_VALUES[WALLPAPER_VIBRANT_INDEX]; - setColorAtIndex(WALLPAPER_VIBRANT_INDEX, wallpaperPalette == null - ? defaultColor : wallpaperPalette.getVibrantColor(defaultColor)); - } - - public void addOnChangeListener(OnChangeListener listener) { - mListeners.add(listener); - } - - public void notifyChange() { - for (OnChangeListener listener : mListeners) { - listener.onExtractedColorsChanged(); - } - } - - /** - * Interface for listening for extracted color changes - */ - public interface OnChangeListener { - - void onExtractedColorsChanged(); - } -} diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java deleted file mode 100644 index cc0e0bed1d..0000000000 --- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2016 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.dynamicui; - -import android.annotation.TargetApi; -import android.app.WallpaperManager; -import android.app.job.JobInfo; -import android.app.job.JobScheduler; -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Color; -import android.os.Build; -import android.support.v4.graphics.ColorUtils; -import android.support.v7.graphics.Palette; - -import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; - -import java.util.List; - -/** - * Contains helper fields and methods related to extracting colors from the wallpaper. - */ -public class ExtractionUtils { - public static final String EXTRACTED_COLORS_PREFERENCE_KEY = "pref_extractedColors"; - public static final String WALLPAPER_ID_PREFERENCE_KEY = "pref_wallpaperId"; - - private static final float MIN_CONTRAST_RATIO = 2f; - - /** - * Extract colors in the :wallpaper-chooser process, if the wallpaper id has changed. - * When the new colors are saved in the LauncherProvider, - * Launcher will be notified in Launcher#onSettingsChanged(String, String). - */ - public static void startColorExtractionServiceIfNecessary(final Context context) { - if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - return; - } - // Run on a background thread, since the service is asynchronous anyway. - Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() { - @Override - public void run() { - if (hasWallpaperIdChanged(context)) { - startColorExtractionService(context); - } - } - }); - } - - /** Starts the {@link ColorExtractionService} without checking the wallpaper id */ - public static void startColorExtractionService(Context context) { - if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - return; - } - JobScheduler jobScheduler = (JobScheduler) context.getSystemService( - Context.JOB_SCHEDULER_SERVICE); - jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID, - new ComponentName(context, ColorExtractionService.class)) - .setMinimumLatency(0).build()); - } - - private static boolean hasWallpaperIdChanged(Context context) { - if (!Utilities.ATLEAST_NOUGAT) { - // TODO: update an id in sharedprefs in onWallpaperChanged broadcast, and read it here. - return false; - } - final SharedPreferences sharedPrefs = Utilities.getPrefs(context); - int wallpaperId = getWallpaperId(WallpaperManager.getInstance(context)); - int savedWallpaperId = sharedPrefs.getInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, -1); - return wallpaperId != savedWallpaperId; - } - - @TargetApi(Build.VERSION_CODES.N) - public static int getWallpaperId(WallpaperManager wallpaperManager) { - return Utilities.ATLEAST_NOUGAT ? - wallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM) : -1; - } - - public static boolean isSuperLight(Palette p) { - return !isLegibleOnWallpaper(Color.WHITE, p.getSwatches()); - } - - public static boolean isSuperDark(Palette p) { - return !isLegibleOnWallpaper(Color.BLACK, p.getSwatches()); - } - - /** - * Given a color, returns true if that color is legible on - * the given wallpaper color swatches, else returns false. - */ - private static boolean isLegibleOnWallpaper(int color, List wallpaperSwatches) { - int legiblePopulation = 0; - int illegiblePopulation = 0; - for (Palette.Swatch swatch : wallpaperSwatches) { - if (isLegible(color, swatch.getRgb())) { - legiblePopulation += swatch.getPopulation(); - } else { - illegiblePopulation += swatch.getPopulation(); - } - } - return legiblePopulation > illegiblePopulation; - } - - /** @return Whether the foreground color is legible on the background color. */ - private static boolean isLegible(int foreground, int background) { - background = ColorUtils.setAlphaComponent(background, 255); - return ColorUtils.calculateContrast(foreground, background) >= MIN_CONTRAST_RATIO; - } - -} diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java index 47c2ffb381..d6ef5b4c8d 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicator.java +++ b/src/com/android/launcher3/pageindicators/PageIndicator.java @@ -20,8 +20,6 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.FrameLayout; -import com.android.launcher3.dynamicui.ExtractedColors; - /** * Base class for a page indicator. */ @@ -74,8 +72,6 @@ public abstract class PageIndicator extends FrameLayout { public void setShouldAutoHide(boolean shouldAutoHide) {} - public void updateColor(ExtractedColors extractedColors) {} - @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == getCaretDrawable(); diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java index 6281fec030..5eedd92fd0 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java @@ -11,9 +11,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Looper; -import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; -import android.util.Log; import android.util.Property; import android.view.ViewConfiguration; import android.widget.ImageView; @@ -21,8 +19,6 @@ import android.widget.ImageView; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dynamicui.ExtractedColors; import com.android.launcher3.dynamicui.WallpaperColorInfo; /** @@ -31,9 +27,6 @@ import com.android.launcher3.dynamicui.WallpaperColorInfo; * The fraction is 1 / number of pages and the position is based on the progress of the page scroll. */ public class PageIndicatorLineCaret extends PageIndicator { - private static final String TAG = "PageIndicatorLine"; - - private static final int[] sTempCoords = new int[2]; private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration(); private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay(); @@ -218,32 +211,6 @@ public class PageIndicatorLineCaret extends PageIndicator { } } - /** - * The line's color will be: - * - mostly opaque white if the hotseat is white (ignoring alpha) - * - mostly opaque black if the hotseat is black (ignoring alpha) - */ - public void updateColor(ExtractedColors extractedColors) { - if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { - return; - } - int originalLineAlpha = mLinePaint.getAlpha(); - int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX); - if (color != Color.TRANSPARENT) { - color = ColorUtils.setAlphaComponent(color, 255); - if (color == Color.BLACK) { - mActiveAlpha = BLACK_ALPHA; - } else if (color == Color.WHITE) { - mActiveAlpha = WHITE_ALPHA; - } else { - Log.e(TAG, "Setting workspace page indicators to an unsupported color: #" - + Integer.toHexString(color)); - } - mLinePaint.setColor(color); - mLinePaint.setAlpha(originalLineAlpha); - } - } - private void animateLineToAlpha(int alpha) { if (alpha == mToAlpha) { // Ignore the new animation if it is going to the same alpha as the current animation. From fca6bc9dce9f78981a972a22a2e195e0e1f0a4cf Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 28 Sep 2017 16:28:34 -0700 Subject: [PATCH 033/885] Adding a utility method to simplify method tracing Change-Id: I79ef0aa5d65b933f4b7f0520fc8bac26e366da2d --- .../launcher3/FirstFrameAnimatorHelper.java | 10 +-- src/com/android/launcher3/Launcher.java | 88 +++++------------- .../android/launcher3/LauncherAppState.java | 2 - .../android/launcher3/LauncherProvider.java | 7 -- .../android/launcher3/model/LoaderTask.java | 60 ++++--------- .../android/launcher3/util/TraceHelper.java | 90 +++++++++++++++++++ 6 files changed, 130 insertions(+), 127 deletions(-) create mode 100644 src/com/android/launcher3/util/TraceHelper.java diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java index 3cbc989eb3..cea7e431a2 100644 --- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java +++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.TraceHelper; /* * This is a helper class that listens to updates from the corresponding animation. @@ -71,15 +72,12 @@ public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter if (sGlobalDrawListener != null) { view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener); } + + TraceHelper.beginSection("TICK"); sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() { - private long mTime = System.currentTimeMillis(); public void onDraw() { sGlobalFrameCounter++; - if (DEBUG) { - long newTime = System.currentTimeMillis(); - Log.d(TAG, "TICK " + (newTime - mTime)); - mTime = newTime; - } + TraceHelper.partitionSection("TICK", "Frame drawn"); } }; view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 99f3803b1c..d1c84b30e0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -130,6 +130,7 @@ import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.ViewOnDrawExecutor; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -142,7 +143,6 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -162,9 +162,7 @@ public class Launcher extends BaseActivity public static final String TAG = "Launcher"; static final boolean LOGD = false; - static final boolean DEBUG_WIDGETS = false; static final boolean DEBUG_STRICT_MODE = false; - static final boolean DEBUG_RESUME_TIME = false; private static final int REQUEST_CREATE_SHORTCUT = 1; private static final int REQUEST_CREATE_APPWIDGET = 5; @@ -352,9 +350,7 @@ public class Launcher extends BaseActivity .penaltyDeath() .build()); } - if (LauncherAppState.PROFILE_STARTUP) { - Trace.beginSection("Launcher-onCreate"); - } + TraceHelper.beginSection("Launcher-onCreate"); if (mLauncherCallbacks != null) { mLauncherCallbacks.preOnCreate(); @@ -365,6 +361,7 @@ public class Launcher extends BaseActivity overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText()); super.onCreate(savedInstanceState); + TraceHelper.partitionSection("Launcher-onCreate", "super call"); LauncherAppState app = LauncherAppState.getInstance(this); @@ -415,10 +412,6 @@ public class Launcher extends BaseActivity restoreState(savedInstanceState); - if (LauncherAppState.PROFILE_STARTUP) { - Trace.endSection(); - } - // We only load the page synchronously if the user rotates (or triggers a // configuration change) while launcher is in the foreground int currentScreen = PagedView.INVALID_RESTORE_PAGE; @@ -473,6 +466,8 @@ public class Launcher extends BaseActivity if (mLauncherCallbacks != null) { mLauncherCallbacks.onCreate(savedInstanceState); } + + TraceHelper.endSection("Launcher-onCreate"); } @Override @@ -887,17 +882,13 @@ public class Launcher extends BaseActivity @Override protected void onResume() { - long startTime = 0; - if (DEBUG_RESUME_TIME) { - startTime = System.currentTimeMillis(); - Log.v(TAG, "Launcher.onResume()"); - } - + TraceHelper.beginSection("ON_RESUME"); if (mLauncherCallbacks != null) { mLauncherCallbacks.preOnResume(); } - super.onResume(); + TraceHelper.partitionSection("ON_RESUME", "superCall"); + getUserEventDispatcher().resetElapsedSessionMillis(); // Restore the previous launcher state @@ -926,19 +917,10 @@ public class Launcher extends BaseActivity if (mBindOnResumeCallbacks.size() > 0) { // We might have postponed some bind calls until onResume (see waitUntilResume) -- // execute them here - long startTimeCallbacks = 0; - if (DEBUG_RESUME_TIME) { - startTimeCallbacks = System.currentTimeMillis(); - } - for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) { mBindOnResumeCallbacks.get(i).run(); } mBindOnResumeCallbacks.clear(); - if (DEBUG_RESUME_TIME) { - Log.d(TAG, "Time spent processing callbacks in onResume: " + - (System.currentTimeMillis() - startTimeCallbacks)); - } } if (mOnResumeCallbacks.size() > 0) { for (int i = 0; i < mOnResumeCallbacks.size(); i++) { @@ -962,10 +944,6 @@ public class Launcher extends BaseActivity getWorkspace().reinflateWidgetsIfNecessary(); } - if (DEBUG_RESUME_TIME) { - Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime)); - } - updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); mWorkspace.onResume(); @@ -983,6 +961,7 @@ public class Launcher extends BaseActivity mLauncherCallbacks.onResume(); } + TraceHelper.endSection("ON_RESUME"); } @Override @@ -1574,10 +1553,7 @@ public class Launcher extends BaseActivity @Override protected void onNewIntent(Intent intent) { - long startTime = 0; - if (DEBUG_RESUME_TIME) { - startTime = System.currentTimeMillis(); - } + TraceHelper.beginSection("NEW_INTENT"); super.onNewIntent(intent); boolean alreadyOnHome = mHasFocus && ((intent.getFlags() & @@ -1674,9 +1650,7 @@ public class Launcher extends BaseActivity } } - if (DEBUG_RESUME_TIME) { - Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime)); - } + TraceHelper.endSection("NEW_INTENT"); } @Override @@ -3043,10 +3017,7 @@ public class Launcher extends BaseActivity * Implementation of the method from LauncherModel.Callbacks. */ public void startBinding() { - if (LauncherAppState.PROFILE_STARTUP) { - Trace.beginSection("Starting page bind"); - } - + TraceHelper.beginSection("startBinding"); AbstractFloatingView.closeAllOpenViews(this); setWorkspaceLoading(true); @@ -3058,9 +3029,7 @@ public class Launcher extends BaseActivity if (mHotseat != null) { mHotseat.resetLayout(); } - if (LauncherAppState.PROFILE_STARTUP) { - Trace.endSection(); - } + TraceHelper.endSection("startBinding"); } @Override @@ -3264,10 +3233,7 @@ public class Launcher extends BaseActivity return view; } - final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0; - if (DEBUG_WIDGETS) { - Log.d(TAG, "bindAppWidget: " + item); - } + TraceHelper.beginSection("BIND_WIDGET"); final LauncherAppWidgetProviderInfo appWidgetInfo; @@ -3285,11 +3251,9 @@ public class Launcher extends BaseActivity if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { if (appWidgetInfo == null) { - if (DEBUG_WIDGETS) { - Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId - + " belongs to component " + item.providerName - + ", as the provider is null"); - } + Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId + + " belongs to component " + item.providerName + + ", as the provider is null"); getModelWriter().deleteItemFromDatabase(item); return null; } @@ -3350,11 +3314,6 @@ public class Launcher extends BaseActivity final AppWidgetHostView view; if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { - if (DEBUG_WIDGETS) { - Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " - + appWidgetInfo.provider); - } - // Verify that we own the widget if (appWidgetInfo == null) { FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); @@ -3370,10 +3329,7 @@ public class Launcher extends BaseActivity } prepareAppWidget(view, item); - if (DEBUG_WIDGETS) { - Log.d(TAG, "bound widget id="+item.appWidgetId+" in " - + (SystemClock.uptimeMillis()-start) + "ms"); - } + TraceHelper.endSection("BIND_WIDGET", "id=" + item.appWidgetId); return view; } @@ -3459,9 +3415,7 @@ public class Launcher extends BaseActivity if (waitUntilResume(r)) { return; } - if (LauncherAppState.PROFILE_STARTUP) { - Trace.beginSection("Page bind completed"); - } + TraceHelper.beginSection("finishBindingItems"); mWorkspace.restoreInstanceStateForRemainingPages(); setWorkspaceLoading(false); @@ -3480,9 +3434,7 @@ public class Launcher extends BaseActivity if (mLauncherCallbacks != null) { mLauncherCallbacks.finishBindingItems(false); } - if (LauncherAppState.PROFILE_STARTUP) { - Trace.endSection(); - } + TraceHelper.endSection("finishBindingItems"); } private boolean canRunNewAppsAnimation() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 1ffe41bc6c..7bebf445bf 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -42,8 +42,6 @@ import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING; public class LauncherAppState { - public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD; - // We do not need any synchronization for this variable as its only written on UI thread. private static LauncherAppState INSTANCE; diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index b31df98947..f222a97bb3 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -149,9 +149,6 @@ public class LauncherProvider extends ContentProvider { */ protected synchronized void createDbIfNotExists() { if (mOpenHelper == null) { - if (LauncherAppState.PROFILE_STARTUP) { - Trace.beginSection("Opening workspace DB"); - } mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler); if (RestoreDbTask.isPending(getContext())) { @@ -162,10 +159,6 @@ public class LauncherProvider extends ContentProvider { // executed again. RestoreDbTask.setPending(getContext(), false); } - - if (LauncherAppState.PROFILE_STARTUP) { - Trace.endSection(); - } } } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index e1b208a059..5386fb4a57 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -66,6 +66,7 @@ import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Provider; +import com.android.launcher3.util.TraceHelper; import java.util.ArrayList; import java.util.Collections; @@ -85,7 +86,6 @@ import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_I * - deep shortcuts within apps */ public class LoaderTask implements Runnable { - private static final boolean DEBUG_LOADERS = false; private static final String TAG = "LoaderTask"; private final LauncherAppState mApp; @@ -142,73 +142,64 @@ public class LoaderTask implements Runnable { } } + TraceHelper.beginSection(TAG); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { - long now = 0; - if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace"); + TraceHelper.partitionSection(TAG, "step 1.1: loading workspace"); loadWorkspace(); verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace"); + TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace"); mResults.bindWorkspace(); // Take a break - if (DEBUG_LOADERS) { - Log.d(TAG, "step 1 completed, wait for idle"); - now = SystemClock.uptimeMillis(); - } + TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle"); waitForIdle(); - if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); verifyNotStopped(); // second step - if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps"); + TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); loadAllApps(); - if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps"); + TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps"); verifyNotStopped(); mResults.bindAllApps(); verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache"); + TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache"); updateIconCache(); // Take a break - if (DEBUG_LOADERS) { - Log.d(TAG, "step 2 completed, wait for idle"); - now = SystemClock.uptimeMillis(); - } + TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle"); waitForIdle(); - if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); verifyNotStopped(); // third step - if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts"); + TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts"); loadDeepShortcuts(); verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts"); + TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts"); mResults.bindDeepShortcuts(); // Take a break - if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle"); + TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle"); waitForIdle(); verifyNotStopped(); // fourth step - if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets"); + TraceHelper.partitionSection(TAG, "step 4.1: loading widgets"); mBgDataModel.widgetsModel.update(mApp, null); verifyNotStopped(); - if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets"); + TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets"); mResults.bindWidgets(); transaction.commit(); } catch (CancellationException e) { // Loader stopped, ignore - if (DEBUG_LOADERS) { - Log.d(TAG, "Loader cancelled", e); - } + TraceHelper.partitionSection(TAG, "Cancelled"); } + TraceHelper.endSection(TAG); } public synchronized void stopLocked() { @@ -217,10 +208,6 @@ public class LoaderTask implements Runnable { } private void loadWorkspace() { - if (LauncherAppState.PROFILE_STARTUP) { - Trace.beginSection("Loading Workspace"); - } - final Context context = mApp.getContext(); final ContentResolver contentResolver = context.getContentResolver(); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); @@ -766,9 +753,6 @@ public class LoaderTask implements Runnable { LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens); } } - if (LauncherAppState.PROFILE_STARTUP) { - Trace.endSection(); - } } private void updateIconCache() { @@ -793,21 +777,13 @@ public class LoaderTask implements Runnable { } private void loadAllApps() { - final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - final List profiles = mUserManager.getUserProfiles(); // Clear the list of apps mBgAllAppsList.clear(); for (UserHandle user : profiles) { // Query for the set of apps - final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; final List apps = mLauncherApps.getActivityList(null, user); - if (DEBUG_LOADERS) { - Log.d(TAG, "getActivityList took " - + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); - Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); - } // Fail if we don't have any apps // TODO: Fix this. Only fail for the current user. if (apps == null || apps.isEmpty()) { @@ -834,10 +810,6 @@ public class LoaderTask implements Runnable { } mBgAllAppsList.added = new ArrayList<>(); - if (DEBUG_LOADERS) { - Log.d(TAG, "All apps loaded in in " - + (SystemClock.uptimeMillis() - loadTime) + "ms"); - } } private void loadDeepShortcuts() { diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java new file mode 100644 index 0000000000..5b66fcda62 --- /dev/null +++ b/src/com/android/launcher3/util/TraceHelper.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import android.os.SystemClock; +import android.os.Trace; +import android.util.ArrayMap; +import android.util.Log; +import android.util.MutableLong; + +import com.android.launcher3.config.FeatureFlags; + +/** + * A wrapper around {@link Trace} to allow easier proguarding for production builds. + * + * To enable any tracing log, execute the following command: + * $ adb shell setprop log.tag.TAGNAME VERBOSE + */ +public class TraceHelper { + + private static final boolean ENABLED = FeatureFlags.IS_DOGFOOD_BUILD; + + private static final boolean SYSTEM_TRACE = true; + private static final ArrayMap sUpTimes = + ENABLED ? new ArrayMap() : null; + + public static void beginSection(String sectionName) { + if (ENABLED) { + MutableLong time = sUpTimes.get(sectionName); + if (time == null) { + time = new MutableLong(Log.isLoggable(sectionName, Log.VERBOSE) ? 0 : -1); + sUpTimes.put(sectionName, time); + } + if (time.value >= 0) { + if (SYSTEM_TRACE) { + Trace.beginSection(sectionName); + } + time.value = SystemClock.uptimeMillis(); + } + } + } + + public static void partitionSection(String sectionName, String partition) { + if (ENABLED) { + MutableLong time = sUpTimes.get(sectionName); + if (time.value >= 0) { + + if (SYSTEM_TRACE) { + Trace.endSection(); + Trace.beginSection(sectionName); + } + + long now = SystemClock.uptimeMillis(); + Log.d(sectionName, partition + " : " + (now - time.value)); + time.value = now; + } + } + } + + public static void endSection(String sectionName) { + if (ENABLED) { + endSection(sectionName, "End"); + } + } + + public static void endSection(String sectionName, String msg) { + if (ENABLED) { + MutableLong time = sUpTimes.get(sectionName); + if (time.value >= 0) { + if (SYSTEM_TRACE) { + Trace.endSection(); + } + Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value)); + } + } + } +} From 326403e958344751539cefec62ecbc6d28abd2ce Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Oct 2017 12:45:10 -0700 Subject: [PATCH 034/885] Moving some calls off the UI thread This saves ~5ms in onNewIntent Bug: 67305604 Change-Id: Ic97727b1c526e50bd3c8a1d8f511e1d7fd5e05e7 --- .../android/launcher3/ExtendedEditText.java | 20 ++++- src/com/android/launcher3/Launcher.java | 16 ++-- .../search/AllAppsSearchBarController.java | 23 +----- .../launcher3/dragndrop/DragController.java | 5 +- .../graphics/DragPreviewProvider.java | 4 +- .../launcher3/util/UiThreadHelper.java | 76 +++++++++++++++++++ 6 files changed, 105 insertions(+), 39 deletions(-) create mode 100644 src/com/android/launcher3/util/UiThreadHelper.java diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java index 596aa8f7bc..403c8b8ba7 100644 --- a/src/com/android/launcher3/ExtendedEditText.java +++ b/src/com/android/launcher3/ExtendedEditText.java @@ -16,12 +16,16 @@ package com.android.launcher3; import android.content.Context; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.DragEvent; import android.view.KeyEvent; +import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; +import com.android.launcher3.util.UiThreadHelper; + /** * The edit text that reports back when the back key has been pressed. @@ -102,8 +106,7 @@ public class ExtendedEditText extends EditText { } public void dispatchBackKey() { - ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) - .hideSoftInputFromWindow(getWindowToken(), 0); + UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken()); if (mBackKeyListener != null) { mBackKeyListener.onBackKey(); } @@ -121,4 +124,17 @@ public class ExtendedEditText extends EditText { public boolean isSuggestionsEnabled() { return !mForceDisableSuggestions && super.isSuggestionsEnabled(); } + + public void reset() { + if (!TextUtils.isEmpty(getText())) { + setText(""); + } + if (isFocused()) { + View nextFocus = focusSearch(View.FOCUS_DOWN); + if (nextFocus != null) { + nextFocus.requestFocus(); + } + } + UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken()); + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8702660d2c..3a8d260c41 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; +import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; + import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -53,8 +56,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Process; import android.os.StrictMode; -import android.os.SystemClock; -import android.os.Trace; import android.os.UserHandle; import android.support.annotation.Nullable; import android.text.Selection; @@ -119,7 +120,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.ActivityResultInfo; -import com.android.launcher3.util.RunnableWithId; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ComponentKeyMapper; import com.android.launcher3.util.ItemInfoMatcher; @@ -127,11 +127,13 @@ import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; +import com.android.launcher3.util.RunnableWithId; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TraceHelper; +import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.ViewOnDrawExecutor; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -140,7 +142,6 @@ import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; import com.android.launcher3.widget.custom.CustomWidgetParser; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -150,9 +151,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.Executor; -import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; -import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; - /** * Default launcher application. */ @@ -1606,9 +1604,7 @@ public class Launcher extends BaseActivity final View v = getWindow().peekDecorView(); if (v != null && v.getWindowToken() != null) { - InputMethodManager imm = (InputMethodManager) getSystemService( - INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(v.getWindowToken(), 0); + UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken()); } // Reset the apps view diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index 63aa7be3a6..bf03a0ee1a 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -15,16 +15,13 @@ */ package com.android.launcher3.allapps.search; -import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.KeyEvent; -import android.view.View; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; @@ -50,7 +47,6 @@ public class AllAppsSearchBarController protected String mQuery; protected SearchAlgorithm mSearchAlgorithm; - protected InputMethodManager mInputMethodManager; public void setVisibility(int visibility) { mInput.setVisibility(visibility); @@ -68,10 +64,6 @@ public class AllAppsSearchBarController mInput.addTextChangedListener(this); mInput.setOnEditorActionListener(this); mInput.setOnBackKeyListener(this); - - mInputMethodManager = (InputMethodManager) - mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - mSearchAlgorithm = searchAlgorithm; } @@ -137,22 +129,9 @@ public class AllAppsSearchBarController * Resets the search bar state. */ public void reset() { - unfocusSearchField(); mCb.clearSearchResult(); - mInput.setText(""); + mInput.reset(); mQuery = null; - hideKeyboard(); - } - - protected void hideKeyboard() { - mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0); - } - - protected void unfocusSearchField() { - View nextFocus = mInput.focusSearch(View.FOCUS_DOWN); - if (nextFocus != null) { - nextFocus.requestFocus(); - } } /** diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index b8527148be..a7ed87fb6a 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -27,7 +27,6 @@ import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.inputmethod.InputMethodManager; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; @@ -39,6 +38,7 @@ import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TouchController; +import com.android.launcher3.util.UiThreadHelper; import java.util.ArrayList; @@ -138,8 +138,7 @@ public class DragController implements DragDriver.EventListener, TouchController } // Hide soft keyboard, if visible - mLauncher.getSystemService(InputMethodManager.class) - .hideSoftInputFromWindow(mWindowToken, 0); + UiThreadHelper.hideKeyboardAsync(mLauncher, mWindowToken); mOptions = options; if (mOptions.systemDndStartPoint != null) { diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java index 0989921b28..355c231f3e 100644 --- a/src/com/android/launcher3/graphics/DragPreviewProvider.java +++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java @@ -32,10 +32,10 @@ import android.view.View; import com.android.launcher3.BubbleTextView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetHostView; -import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.util.UiThreadHelper; import java.nio.ByteBuffer; @@ -151,7 +151,7 @@ public class DragPreviewProvider { } mOutlineGeneratorCallback = new OutlineGeneratorCallback(preview); - new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(mOutlineGeneratorCallback); + new Handler(UiThreadHelper.getBackgroundLooper()).post(mOutlineGeneratorCallback); } protected static Rect getDrawableBounds(Drawable d) { diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java new file mode 100644 index 0000000000..27140a1138 --- /dev/null +++ b/src/com/android/launcher3/util/UiThreadHelper.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.view.inputmethod.InputMethodManager; + +/** + * Utility class for offloading some class from UI thread + */ +public class UiThreadHelper { + + private static HandlerThread sHandlerThread; + private static Handler sHandler; + + private static final int MSG_HIDE_KEYBOARD = 1; + + public static Looper getBackgroundLooper() { + if (sHandlerThread == null) { + sHandlerThread = + new HandlerThread("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND); + sHandlerThread.start(); + } + return sHandlerThread.getLooper(); + } + + private static Handler getHandler(Context context) { + if (sHandler == null) { + sHandler = new Handler(getBackgroundLooper(), + new UiCallbacks(context.getApplicationContext())); + } + return sHandler; + } + + public static void hideKeyboardAsync(Context context, IBinder token) { + Message.obtain(getHandler(context), MSG_HIDE_KEYBOARD, token).sendToTarget(); + } + + private static class UiCallbacks implements Handler.Callback { + + private final InputMethodManager mIMM; + + UiCallbacks(Context context) { + mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + } + + @Override + public boolean handleMessage(Message message) { + switch (message.what) { + case MSG_HIDE_KEYBOARD: + mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0); + return true; + } + return false; + } + } +} From d66e3b6a6510000d3d48f65e82b5b83d30ecb639 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Oct 2017 15:26:42 -0700 Subject: [PATCH 035/885] Removing unnecessary layout pass happening due when chaning gradientView visibility Change-Id: I0d8f0c2c995885143156a27536b0a2185d5eb0b8 --- res/layout/gradient_bg.xml | 1 - .../android/launcher3/allapps/AllAppsTransitionController.java | 3 +-- src/com/android/launcher3/widget/WidgetsBottomSheet.java | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/res/layout/gradient_bg.xml b/res/layout/gradient_bg.xml index db448d7818..6c6626c1d2 100644 --- a/res/layout/gradient_bg.xml +++ b/res/layout/gradient_bg.xml @@ -20,5 +20,4 @@ android:id="@+id/gradient_bg" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="gone" launcher:layout_ignoreInsets="true" /> \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 3364c61eea..87779012ab 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -301,8 +301,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private void updateAllAppsBg(float progress) { // gradient if (mGradientView == null) { - mGradientView = (GradientView) mLauncher.findViewById(R.id.gradient_bg); - mGradientView.setVisibility(View.VISIBLE); + mGradientView = mLauncher.findViewById(R.id.gradient_bg); } mGradientView.setProgress(progress); } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 01101ac74c..aa0fb7bed3 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -108,7 +108,6 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab onWidgetsBound(); mLauncher.getDragLayer().addView(mGradientBackground); - mGradientBackground.setVisibility(VISIBLE); mLauncher.getDragLayer().addView(this); measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); setTranslationY(mTranslationYClosed); From a502aa313bb3c6f59d0c15e2586e23524ec027a0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Oct 2017 16:04:06 -0700 Subject: [PATCH 036/885] Removing buildLayer call on workspace pages. > buildLayer was followed by setLayerType(NONE) which was causing the layer to get destroyed immediately and hece was never useful. > Also removing mAnimatingViewIntoPlace as after setting this to true updateChildLayer was never being called. Change-Id: I08a6da25de002247c956308973f1675c0e61e15c --- src/com/android/launcher3/CellLayout.java | 4 -- src/com/android/launcher3/Launcher.java | 50 +--------------------- src/com/android/launcher3/Workspace.java | 51 +++++------------------ 3 files changed, 13 insertions(+), 92 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index f835748088..d92b934d1b 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -355,10 +355,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint); } - public void buildHardwareLayer() { - mShortcutsAndWidgets.buildLayer(); - } - public void setCellDimensions(int width, int height) { mFixedCellWidth = mCellWidth = width; mFixedCellHeight = mCellHeight = height; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3a8d260c41..5d63694d87 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -74,7 +74,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -302,14 +301,6 @@ public class Launcher extends BaseActivity // simply unregister this runnable. private Runnable mExitSpringLoadedModeRunnable; - @Thunk final Runnable mBuildLayersRunnable = new Runnable() { - public void run() { - if (mWorkspace != null) { - mWorkspace.buildPageHardwareLayers(); - } - } - }; - // Activity result which needs to be processed after workspace has loaded. private ActivityResultInfo mPendingActivityResult; /** @@ -961,6 +952,8 @@ public class Launcher extends BaseActivity mLauncherCallbacks.onResume(); } + clearTypedText(); + TraceHelper.endSection("ON_RESUME"); } @@ -1467,44 +1460,6 @@ public class Launcher extends BaseActivity } } - public void onWindowVisibilityChanged(int visibility) { - // 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 - if (visibility == View.VISIBLE) { - if (!mWorkspaceLoading) { - final ViewTreeObserver observer = mWorkspace.getViewTreeObserver(); - // We want to let Launcher draw itself at least once before we force it to build - // layers on all the workspace pages, so that transitioning to Launcher from other - // apps is nice and speedy. - observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() { - private boolean mStarted = false; - public void onDraw() { - if (mStarted) return; - mStarted = true; - // We delay the layer building a bit in order to give - // other message processing a time to run. In particular - // this avoids a delay in hiding the IME if it was - // currently shown, because doing that may involve - // some communication back with the app. - mWorkspace.postDelayed(mBuildLayersRunnable, 500); - final ViewTreeObserver.OnDrawListener listener = this; - mWorkspace.post(new Runnable() { - public void run() { - if (mWorkspace != null && - mWorkspace.getViewTreeObserver() != null) { - mWorkspace.getViewTreeObserver(). - removeOnDrawListener(listener); - } - } - }); - } - }); - } - clearTypedText(); - } - } - public DragLayer getDragLayer() { return mDragLayer; } @@ -1689,7 +1644,6 @@ public class Launcher extends BaseActivity super.onDestroy(); unregisterReceiver(mReceiver); - mWorkspace.removeCallbacks(mBuildLayersRunnable); mWorkspace.removeFolderListeners(); // Stop callbacks from LauncherModel diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index c82731ff83..430e0aa877 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -228,7 +228,6 @@ public class Workspace extends PagedView private State mState = State.NORMAL; private boolean mIsSwitchingState = false; - boolean mAnimatingViewIntoPlace = false; boolean mChildrenLayersEnabled = true; private boolean mStripScreensOnPageStopMoving = false; @@ -406,7 +405,7 @@ public class Workspace extends PagedView } } - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging @@ -459,7 +458,7 @@ public class Workspace extends PagedView removeExtraEmptyScreen(true, mDragSourceInternal != null); } - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); mLauncher.unlockScreenOrientation(false); // Re-enable any Un/InstallShortcutReceiver and now process any queued items @@ -1016,10 +1015,6 @@ public class Workspace extends PagedView || (mTransitionProgress > FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS); } - protected void onWindowVisibilityChanged (int visibility) { - mLauncher.onWindowVisibilityChanged(visibility); - } - @Override public boolean dispatchUnhandledMove(View focused, int direction) { if (workspaceInModalState() || !isFinishedSwitchingState()) { @@ -1109,12 +1104,12 @@ public class Workspace extends PagedView protected void onPageBeginTransition() { super.onPageBeginTransition(); - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); } protected void onPageEndTransition() { super.onPageEndTransition(); - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); if (mDragController.isDragging()) { if (workspaceInModalState()) { @@ -1494,9 +1489,9 @@ public class Workspace extends PagedView return mState == State.NORMAL || mState == State.SPRING_LOADED; } - @Thunk void updateChildrenLayersEnabled(boolean force) { + private void updateChildrenLayersEnabled() { boolean small = mState == State.OVERVIEW || mIsSwitchingState; - boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageInTransition(); + boolean enableChildrenLayers = small || isPageInTransition(); if (enableChildrenLayers != mChildrenLayersEnabled) { mChildrenLayersEnabled = enableChildrenLayers; @@ -1562,19 +1557,6 @@ public class Workspace extends PagedView } } - public void buildPageHardwareLayers() { - // force layers to be enabled just for the call to buildLayer - updateChildrenLayersEnabled(true); - if (getWindowToken() != null) { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - CellLayout cl = (CellLayout) getChildAt(i); - cl.buildHardwareLayer(); - } - } - updateChildrenLayersEnabled(false); - } - protected void onWallpaperTap(MotionEvent ev) { final int[] position = mTempXY; getLocationOnScreen(position); @@ -1773,12 +1755,12 @@ public class Workspace extends PagedView } invalidate(); // This will call dispatchDraw(), which calls getVisiblePages(). - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); } public void onEndStateTransition() { mIsSwitchingState = false; - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); mForceDrawAdjacentPages = false; mTransitionProgress = 1; } @@ -2262,16 +2244,6 @@ public class Workspace extends PagedView } final CellLayout parent = (CellLayout) cell.getParent().getParent(); - // Prepare it to be animated into its new position - // This must be called after the view has been re-parented - final Runnable onCompleteRunnable = new Runnable() { - @Override - public void run() { - mAnimatingViewIntoPlace = false; - updateChildrenLayersEnabled(false); - } - }; - mAnimatingViewIntoPlace = true; if (d.dragView.hasDrawn()) { if (droppedOnOriginalCellDuringTransition) { // Animate the item to its original position, while simultaneously exiting @@ -2290,12 +2262,11 @@ public class Workspace extends PagedView if (isWidget) { int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE : ANIMATE_INTO_POSITION_AND_DISAPPEAR; - animateWidgetDrop(info, parent, d.dragView, - onCompleteRunnable, animationType, cell, false); + animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false); } else { int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, - onCompleteRunnable, this); + null, this); } } else { d.deferDragViewCleanupPostAnimation = false; @@ -3147,7 +3118,7 @@ public class Workspace extends PagedView // hardware layers on children are enabled on startup, but should be disabled until // needed - updateChildrenLayersEnabled(false); + updateChildrenLayersEnabled(); } /** From 37920966888587900885c88a63785cb16567684c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 28 Sep 2017 13:43:24 -0700 Subject: [PATCH 037/885] Allowing the widgetBottomSheet to be dragged even when the touch is started from outside the panel Removing various instanceOf checks in onNewIntent and onBackPress and moving all the corresponding logging in the FloatingView This simplifies handling of panel specific log and avoids missing a particular panel type in the if-else statement. Bug: 64751884 Bug: 64751923 Change-Id: I98f5aae18560a64be73c9efcf495479740d49a00 --- res/layout/app_widget_resize_frame.xml | 71 ++++++++------- .../launcher3/AbstractFloatingView.java | 38 ++++---- .../launcher3/AppWidgetResizeFrame.java | 46 ++++++++-- src/com/android/launcher3/Launcher.java | 33 +------ src/com/android/launcher3/Workspace.java | 8 +- .../launcher3/dragndrop/DragLayer.java | 90 +------------------ src/com/android/launcher3/folder/Folder.java | 49 ++++++++-- .../popup/PopupContainerWithArrow.java | 38 +++++--- .../launcher3/widget/WidgetsBottomSheet.java | 34 +++---- 9 files changed, 188 insertions(+), 219 deletions(-) diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml index 874fecccf3..12561b634d 100644 --- a/res/layout/app_widget_resize_frame.xml +++ b/res/layout/app_widget_resize_frame.xml @@ -21,42 +21,47 @@ android:background="@drawable/widget_resize_shadow" android:foreground="@drawable/widget_resize_frame" android:foregroundTint="?attr/workspaceTextColor" - android:padding="0dp" > + android:padding="0dp"> - - + - - + + - - + + - - + + + + + + \ No newline at end of file diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 597e937039..49968189bb 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -25,6 +25,8 @@ import android.view.View; import android.widget.LinearLayout; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action; +import com.android.launcher3.util.TouchController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,18 +34,20 @@ import java.lang.annotation.RetentionPolicy; /** * Base class for a View which shows a floating UI on top of the launcher UI. */ -public abstract class AbstractFloatingView extends LinearLayout { +public abstract class AbstractFloatingView extends LinearLayout implements TouchController { @IntDef(flag = true, value = { TYPE_FOLDER, TYPE_POPUP_CONTAINER_WITH_ARROW, - TYPE_WIDGETS_BOTTOM_SHEET + TYPE_WIDGETS_BOTTOM_SHEET, + TYPE_WIDGET_RESIZE_FRAME }) @Retention(RetentionPolicy.SOURCE) public @interface FloatingViewType {} public static final int TYPE_FOLDER = 1 << 0; public static final int TYPE_POPUP_CONTAINER_WITH_ARROW = 1 << 1; public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2; + public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3; protected boolean mIsOpen; @@ -72,21 +76,7 @@ public abstract class AbstractFloatingView extends LinearLayout { protected abstract void handleClose(boolean animate); - /** - * If the view is current handling keyboard, return the active target, null otherwise - */ - public ExtendedEditText getActiveTextView() { - return null; - } - - - /** - * Any additional view (outside of this container) where touch should be allowed while this - * view is visible. - */ - public View getExtendedTouchView() { - return null; - } + public abstract void logActionCommand(int command); public final boolean isOpen() { return mIsOpen; @@ -97,6 +87,16 @@ public abstract class AbstractFloatingView extends LinearLayout { protected abstract boolean isOfType(@FloatingViewType int type); + public void onBackPressed() { + logActionCommand(Action.Command.BACK); + close(true); + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + return false; + } + protected static T getOpenView( Launcher launcher, @FloatingViewType int type) { DragLayer dragLayer = launcher.getDragLayer(); @@ -139,8 +139,6 @@ public abstract class AbstractFloatingView extends LinearLayout { public static AbstractFloatingView getTopOpenView(Launcher launcher) { return getOpenView(launcher, TYPE_FOLDER | TYPE_POPUP_CONTAINER_WITH_ARROW - | TYPE_WIDGETS_BOTTOM_SHEET); + | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME); } - - public abstract int getLogContainerType(); } diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index d0b1c3082c..1e95333ff2 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -14,15 +14,13 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.widget.FrameLayout; +import android.view.ViewGroup; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.FocusLogic; -import com.android.launcher3.util.TouchController; -public class AppWidgetResizeFrame extends FrameLayout - implements View.OnKeyListener, TouchController { +public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener { private static final int SNAP_DURATION = 150; private static final float DIMMED_HANDLE_ALPHA = 0f; private static final float RESIZE_THRESHOLD = 0.66f; @@ -108,12 +106,28 @@ public class AppWidgetResizeFrame extends FrameLayout protected void onFinishInflate() { super.onFinishInflate(); + ViewGroup content = (ViewGroup) getChildAt(0); for (int i = 0; i < HANDLE_COUNT; i ++) { - mDragHandles[i] = getChildAt(i); + mDragHandles[i] = content.getChildAt(i); } } - public void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout, + public static void showForWidget(LauncherAppWidgetHostView widget, CellLayout cellLayout) { + Launcher launcher = Launcher.getLauncher(cellLayout.getContext()); + AbstractFloatingView.closeAllOpenViews(launcher); + + DragLayer dl = launcher.getDragLayer(); + AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater() + .inflate(R.layout.app_widget_resize_frame, dl, false); + frame.setupForWidget(widget, cellLayout, dl); + ((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true; + + dl.addView(frame); + frame.mIsOpen = true; + frame.snapToWidget(false); + } + + private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) { mCellLayout = cellLayout; mWidgetView = widgetView; @@ -384,7 +398,7 @@ public class AppWidgetResizeFrame extends FrameLayout out.bottom = out.top + height; } - public void snapToWidget(boolean animate) { + private void snapToWidget(boolean animate) { getSnappedRectRelativeToDragLayer(sTmpRect); int newWidth = sTmpRect.width(); int newHeight = sTmpRect.height(); @@ -448,7 +462,7 @@ public class AppWidgetResizeFrame extends FrameLayout public boolean onKey(View v, int keyCode, KeyEvent event) { // Clear the frame and give focus to the widget host view when a directional key is pressed. if (FocusLogic.shouldConsume(keyCode)) { - mDragLayer.clearResizeFrame(); + close(false); mWidgetView.requestFocus(); return true; } @@ -498,9 +512,25 @@ public class AppWidgetResizeFrame extends FrameLayout if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) { return true; } + close(false); return false; } + @Override + protected void handleClose(boolean animate) { + mDragLayer.removeView(this); + } + + @Override + public void logActionCommand(int command) { + // TODO: Log this case. + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_WIDGET_RESIZE_FRAME) != 0; + } + /** * A mutable class for describing the range of two int values. */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3a8d260c41..4388eb4a0a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1091,9 +1091,6 @@ public class Launcher extends BaseActivity // Close any open floating view AbstractFloatingView.closeAllOpenViews(this); - // Stop resizing any widgets - mWorkspace.exitWidgetResizeMode(); - // Show the overview mode if we are on the workspace if (mState == State.WORKSPACE && !mWorkspace.isInOverviewMode() && !mWorkspace.isSwitchingState()) { @@ -1411,8 +1408,6 @@ public class Launcher extends BaseActivity public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (Intent.ACTION_SCREEN_OFF.equals(action)) { - mDragLayer.clearResizeFrame(); - // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) { @@ -1574,17 +1569,9 @@ public class Launcher extends BaseActivity // Note: There should be at most one log per method call. This is enforced implicitly // by using if-else statements. UserEventDispatcher ued = getUserEventDispatcher(); - - // TODO: Log this case. - mWorkspace.exitWidgetResizeMode(); - AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this); - if (topOpenView instanceof PopupContainerWithArrow) { - ued.logActionCommand(Action.Command.HOME_INTENT, - topOpenView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS); - } else if (topOpenView instanceof Folder) { - ued.logActionCommand(Action.Command.HOME_INTENT, - ((Folder) topOpenView).getFolderIcon(), ContainerType.FOLDER); + if (topOpenView != null) { + topOpenView.logActionCommand(Action.Command.HOME_INTENT); } else if (alreadyOnHome) { ued.logActionCommand(Action.Command.HOME_INTENT, mWorkspace.getState().containerType, mWorkspace.getCurrentPage()); @@ -2062,18 +2049,7 @@ public class Launcher extends BaseActivity UserEventDispatcher ued = getUserEventDispatcher(); AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); if (topView != null) { - if (topView.getActiveTextView() != null) { - topView.getActiveTextView().dispatchBackKey(); - } else { - if (topView instanceof PopupContainerWithArrow) { - ued.logActionCommand(Action.Command.BACK, - topView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS); - } else if (topView instanceof Folder) { - ued.logActionCommand(Action.Command.BACK, - ((Folder) topView).getFolderIcon(), ContainerType.FOLDER); - } - topView.close(true); - } + topView.onBackPressed(); } else if (isAppsViewVisible()) { ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS); showWorkspace(true); @@ -2084,9 +2060,6 @@ public class Launcher extends BaseActivity ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW); showWorkspace(true); } else { - // TODO: Log this case. - mWorkspace.exitWidgetResizeMode(); - // Back button is a no-op here, but give at least some feedback for the button press mWorkspace.showOutlinesTemporarily(); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index c82731ff83..74c96a310c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1593,11 +1593,6 @@ public class Workspace extends PagedView mOutlineProvider = outlineProvider; } - public void exitWidgetResizeMode() { - DragLayer dragLayer = mLauncher.getDragLayer(); - dragLayer.clearResizeFrame(); - } - public void onStartReordering() { super.onStartReordering(); // Reordering handles its own animations, disable the automatic ones. @@ -2237,8 +2232,7 @@ public class Workspace extends PagedView mDelayedResizeRunnable = new Runnable() { public void run() { if (!isPageInTransition()) { - DragLayer dragLayer = mLauncher.getDragLayer(); - dragLayer.addResizeFrame(hostView, cellLayout); + AppWidgetResizeFrame.showForWidget(hostView, cellLayout); } } }; diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index fde7995ce4..60ce3c36a6 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -24,13 +24,11 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Rect; import android.graphics.Region; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -42,13 +40,10 @@ import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.CellLayout; import com.android.launcher3.DropTargetBar; -import com.android.launcher3.ExtendedEditText; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppWidgetHostView; import com.android.launcher3.PinchToOverviewListener; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -59,11 +54,9 @@ import com.android.launcher3.dynamicui.WallpaperColorInfo; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.keyboard.ViewGroupFocusHelper; -import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TouchController; -import com.android.launcher3.widget.WidgetsBottomSheet; import java.util.ArrayList; @@ -81,10 +74,6 @@ public class DragLayer extends InsettableFrameLayout { private Launcher mLauncher; - // Variables relating to resizing widgets - private final boolean mIsRtl; - private AppWidgetResizeFrame mCurrentResizeFrame; - // Variables relating to animation of views after drop private ValueAnimator mDropAnim = null; private final TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); @@ -105,7 +94,6 @@ public class DragLayer extends InsettableFrameLayout { private float mBackgroundAlpha = 0; // Related to adjacent page hints - private final Rect mScrollChildPosition = new Rect(); private final ViewGroupFocusHelper mFocusIndicatorHelper; private final WallpaperColorInfo mWallpaperColorInfo; @@ -129,7 +117,6 @@ public class DragLayer extends InsettableFrameLayout { setMotionEventSplittingEnabled(false); setChildrenDrawingOrderEnabled(true); - mIsRtl = Utilities.isRtl(getResources()); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); mWallpaperColorInfo = WallpaperColorInfo.getInstance(getContext()); } @@ -159,10 +146,6 @@ public class DragLayer extends InsettableFrameLayout { ? null : new PinchToOverviewListener(mLauncher); } - public boolean isEventOverPageIndicator(MotionEvent ev) { - return isEventOverView(mLauncher.getWorkspace().getPageIndicator(), ev); - } - public boolean isEventOverHotseat(MotionEvent ev) { return isEventOverView(mLauncher.getHotseat(), ev); } @@ -180,36 +163,6 @@ public class DragLayer extends InsettableFrameLayout { return mHitRect.contains((int) ev.getX(), (int) ev.getY()); } - private boolean handleTouchDown(MotionEvent ev, boolean intercept) { - AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher); - if (topView != null && intercept) { - ExtendedEditText textView = topView.getActiveTextView(); - if (textView != null) { - if (!isEventOverView(textView, ev)) { - textView.dispatchBackKey(); - return true; - } - } else if (!isEventOverView(topView, ev)) { - if (isInAccessibleDrag()) { - // Do not close the container if in drag and drop. - if (!isEventOverDropTargetBar(ev)) { - return true; - } - } else { - mLauncher.getUserEventDispatcher().logActionTapOutside( - LoggerUtils.newContainerTarget(topView.getLogContainerType())); - topView.close(true); - - // We let touches on the original icon go through so that users can launch - // the app with one tap if they don't find a shortcut they want. - View extendedTouch = topView.getExtendedTouchView(); - return extendedTouch == null || !isEventOverView(extendedTouch, ev); - } - } - } - return false; - } - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); @@ -219,9 +172,6 @@ public class DragLayer extends InsettableFrameLayout { // dray layer even if mAllAppsController is NOT the active controller. // TODO: handle other input other than touch mAllAppsController.cancelDiscoveryAnimation(); - if (handleTouchDown(ev, true)) { - return true; - } } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (mTouchCompleteListener != null) { mTouchCompleteListener.onTouchComplete(); @@ -230,12 +180,10 @@ public class DragLayer extends InsettableFrameLayout { } mActiveController = null; - if (mCurrentResizeFrame != null - && mCurrentResizeFrame.onControllerInterceptTouchEvent(ev)) { - mActiveController = mCurrentResizeFrame; + AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher); + if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { + mActiveController = topView; return true; - } else { - clearResizeFrame(); } if (mDragController.onControllerInterceptTouchEvent(ev)) { @@ -248,12 +196,6 @@ public class DragLayer extends InsettableFrameLayout { return true; } - WidgetsBottomSheet widgetsBottomSheet = WidgetsBottomSheet.getOpen(mLauncher); - if (widgetsBottomSheet != null && widgetsBottomSheet.onControllerInterceptTouchEvent(ev)) { - mActiveController = widgetsBottomSheet; - return true; - } - if (mPinchListener != null && mPinchListener.onControllerInterceptTouchEvent(ev)) { // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.) mActiveController = mPinchListener; @@ -357,12 +299,7 @@ public class DragLayer extends InsettableFrameLayout { @Override public boolean onTouchEvent(MotionEvent ev) { int action = ev.getAction(); - - if (action == MotionEvent.ACTION_DOWN) { - if (handleTouchDown(ev, false)) { - return true; - } - } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (mTouchCompleteListener != null) { mTouchCompleteListener.onTouchComplete(); } @@ -542,25 +479,6 @@ public class DragLayer extends InsettableFrameLayout { } } - public void clearResizeFrame() { - if (mCurrentResizeFrame != null) { - removeView(mCurrentResizeFrame); - mCurrentResizeFrame = null; - } - } - - public void addResizeFrame(LauncherAppWidgetHostView widget, CellLayout cellLayout) { - clearResizeFrame(); - - mCurrentResizeFrame = (AppWidgetResizeFrame) LayoutInflater.from(mLauncher) - .inflate(R.layout.app_widget_resize_frame, this, false); - mCurrentResizeFrame.setupForWidget(widget, cellLayout, this); - ((LayoutParams) mCurrentResizeFrame.getLayoutParams()).customPosition = true; - - addView(mCurrentResizeFrame); - mCurrentResizeFrame.snapToWidget(false); - } - public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha, float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, int duration) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 64a2dabab5..de4de36e88 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -32,6 +32,7 @@ import android.view.FocusFinder; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; @@ -66,6 +67,7 @@ import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.pageindicators.PageIndicatorDots; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -368,11 +370,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC return false; } - @Override - public ExtendedEditText getActiveTextView() { - return isEditingName() ? mFolderName : null; - } - public FolderIcon getFolderIcon() { return mFolderIcon; } @@ -1532,7 +1529,45 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } @Override - public int getLogContainerType() { - return ContainerType.FOLDER; + public void logActionCommand(int command) { + mLauncher.getUserEventDispatcher().logActionCommand( + command, getFolderIcon(), ContainerType.FOLDER); + } + + @Override + public void onBackPressed() { + if (isEditingName()) { + mFolderName.dispatchBackKey(); + } else { + super.onBackPressed(); + } + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + DragLayer dl = mLauncher.getDragLayer(); + + if (isEditingName()) { + if (!dl.isEventOverView(mFolderName, ev)) { + mFolderName.dispatchBackKey(); + return true; + } + return false; + } else if (!dl.isEventOverView(this, ev)) { + if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) { + // Do not close the container if in drag and drop. + if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) { + return true; + } + } else { + mLauncher.getUserEventDispatcher().logActionTapOutside( + LoggerUtils.newContainerTarget(ContainerType.FOLDER)); + close(true); + return true; + } + } + } + return false; } } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 8441598cf7..409e5aefd5 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -16,6 +16,11 @@ package com.android.launcher3.popup; +import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS; +import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; +import static com.android.launcher3.userevent.nano.LauncherLogProto.Target; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -66,6 +71,7 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.graphics.TriangleShape; +import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.notification.NotificationItemView; import com.android.launcher3.notification.NotificationKeyData; import com.android.launcher3.shortcuts.DeepShortcutManager; @@ -81,11 +87,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; -import static com.android.launcher3.userevent.nano.LauncherLogProto.Target; - /** * A container for shortcuts to deep links within apps. */ @@ -593,11 +594,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra return arrowView; } - @Override - public View getExtendedTouchView() { - return mOriginalIcon; - } - /** * Determines when the deferred drag should be started. * @@ -950,7 +946,25 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra } @Override - public int getLogContainerType() { - return ContainerType.DEEPSHORTCUTS; + public void logActionCommand(int command) { + mLauncher.getUserEventDispatcher().logActionCommand( + command, mOriginalIcon, ContainerType.DEEPSHORTCUTS); + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + DragLayer dl = mLauncher.getDragLayer(); + if (!dl.isEventOverView(this, ev)) { + mLauncher.getUserEventDispatcher().logActionTapOutside( + LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS)); + close(true); + + // We let touches on the original icon go through so that users can launch + // the app with one tap if they don't find a shortcut they want. + return mOriginalIcon == null || !dl.isEventOverView(mOriginalIcon, ev); + } + } + return false; } } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 01101ac74c..432efa75ef 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -40,24 +40,23 @@ import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; -import com.android.launcher3.util.TouchController; import java.util.List; /** * Bottom sheet for the "Widgets" system shortcut in the long-press popup. */ -public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, TouchController, +public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener, DragController.DragListener { @@ -241,18 +240,6 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0; } - @Override - public int getLogContainerType() { - return LauncherLogProto.ContainerType.WIDGETS; // TODO: be more specific - } - - /** - * Returns a {@link WidgetsBottomSheet} which is already open or null - */ - public static WidgetsBottomSheet getOpen(Launcher launcher) { - return getOpenView(launcher, TYPE_WIDGETS_BOTTOM_SHEET); - } - @Override public void setInsets(Rect insets) { // Extend behind left, right, and bottom insets. @@ -301,6 +288,12 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab } } + @Override + public void logActionCommand(int command) { + // TODO: be more specific + mLauncher.getUserEventDispatcher().logActionCommand(command, ContainerType.WIDGETS); + } + @Override public boolean onControllerTouchEvent(MotionEvent ev) { return mSwipeDetector.onTouchEvent(ev); @@ -308,6 +301,15 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_UP) { + // If we got ACTION_UP without ever returning true on intercept, + // the user never started dragging the bottom sheet. + if (!mLauncher.getDragLayer().isEventOverView(this, ev)) { + close(true); + return false; + } + } + int directionsToDetectScroll = mSwipeDetector.isIdleState() ? SwipeDetector.DIRECTION_NEGATIVE : 0; mSwipeDetector.setDetectableScrollConditions( From 1ce9c476f73f30b55dd08e962506f51dc06ddac8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 3 Oct 2017 16:17:32 -0700 Subject: [PATCH 038/885] Removing some methods from the DragSource This makes is easier to create new DragSource and sets up proper default values in DragOptions Change-Id: I6cb0b1df41b9730cf29f785fe85fe7f0b573ee3a --- .../android/launcher3/ButtonDropTarget.java | 6 +++--- .../android/launcher3/DeleteDropTarget.java | 10 +++++----- src/com/android/launcher3/DragSource.java | 16 --------------- src/com/android/launcher3/InfoDropTarget.java | 4 ++-- .../launcher3/UninstallDropTarget.java | 2 +- src/com/android/launcher3/Workspace.java | 18 ++--------------- .../allapps/AllAppsContainerView.java | 20 ++++--------------- .../dragndrop/BaseItemDragListener.java | 15 -------------- .../launcher3/dragndrop/DragOptions.java | 3 +++ src/com/android/launcher3/folder/Folder.java | 16 +-------------- .../popup/PopupContainerWithArrow.java | 15 -------------- .../widget/WidgetsContainerView.java | 19 ------------------ 12 files changed, 21 insertions(+), 123 deletions(-) diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 632e490590..3759300624 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -178,7 +178,7 @@ public abstract class ButtonDropTarget extends TextView @Override public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { - mActive = supportsDrop(dragObject.dragSource, dragObject.dragInfo); + mActive = supportsDrop(dragObject.dragInfo); mDrawable.setColorFilter(null); if (mCurrentColorAnim != null) { mCurrentColorAnim.cancel(); @@ -194,10 +194,10 @@ public abstract class ButtonDropTarget extends TextView @Override public final boolean acceptDrop(DragObject dragObject) { - return supportsDrop(dragObject.dragSource, dragObject.dragInfo); + return supportsDrop(dragObject.dragInfo); } - protected abstract boolean supportsDrop(DragSource source, ItemInfo info); + protected abstract boolean supportsDrop(ItemInfo info); @Override public boolean isDropEnabled() { diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 4dcb64f01d..fdd4f34bd5 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -46,7 +46,7 @@ public class DeleteDropTarget extends ButtonDropTarget { @Override public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { super.onDragStart(dragObject, options); - setTextBasedOnDragSource(dragObject.dragSource); + setTextBasedOnDragSource(dragObject.dragInfo); } /** @return true for items that should have a "Remove" action in accessibility. */ @@ -57,16 +57,16 @@ public class DeleteDropTarget extends ButtonDropTarget { } @Override - protected boolean supportsDrop(DragSource source, ItemInfo info) { + protected boolean supportsDrop(ItemInfo info) { return true; } /** - * Set the drop target's text to either "Remove" or "Cancel" depending on the drag source. + * Set the drop target's text to either "Remove" or "Cancel" depending on the drag item. */ - public void setTextBasedOnDragSource(DragSource dragSource) { + private void setTextBasedOnDragSource(ItemInfo item) { if (!TextUtils.isEmpty(mText)) { - mText = getResources().getString(dragSource.supportsDeleteDropTarget() + mText = getResources().getString(item.id != ItemInfo.NO_ID ? R.string.remove_drop_target_label : android.R.string.cancel); requestLayout(); diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java index dcd8f589a6..c6106c23e4 100644 --- a/src/com/android/launcher3/DragSource.java +++ b/src/com/android/launcher3/DragSource.java @@ -26,22 +26,6 @@ import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; */ public interface DragSource extends LogContainerProvider { - /** - * @return whether items dragged from this source supports 'App Info' - */ - boolean supportsAppInfoDropTarget(); - - /** - * @return whether items dragged from this source supports 'Delete' drop target (e.g. to remove - * a shortcut.) If this returns false, the drop target will say "Cancel" instead of "Remove." - */ - boolean supportsDeleteDropTarget(); - - /* - * @return the scale of the icons over the workspace icon size - */ - float getIntrinsicIconScaleFactor(); - /** * A callback made back to the source after an item from this source has been dropped on a * DropTarget. diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index f919dd052c..eb6a7045ef 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -99,8 +99,8 @@ public class InfoDropTarget extends UninstallDropTarget { } @Override - protected boolean supportsDrop(DragSource source, ItemInfo info) { - return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info); + protected boolean supportsDrop(ItemInfo info) { + return supportsDrop(getContext(), info); } public static boolean supportsDrop(Context context, ItemInfo info) { diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 902fd3439a..3f7de0604b 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -44,7 +44,7 @@ public class UninstallDropTarget extends ButtonDropTarget { } @Override - protected boolean supportsDrop(DragSource source, ItemInfo info) { + protected boolean supportsDrop(ItemInfo info) { return supportsDrop(getContext(), info); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 74c96a310c..d003fa44a2 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1869,7 +1869,7 @@ public class Workspace extends PagedView DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, dragObject, dragVisualizeOffset, dragRect, scale, dragOptions); - dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); + dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor); return dv; } @@ -1881,6 +1881,7 @@ public class Workspace extends PagedView /** * {@inheritDoc} */ + @Override public boolean acceptDrop(DragObject d) { // If it's an external drop (e.g. from All Apps), check if it should be accepted CellLayout dropTargetLayout = mDropToLayout; @@ -3240,21 +3241,6 @@ public class Workspace extends PagedView } } - @Override - public float getIntrinsicIconScaleFactor() { - return 1f; - } - - @Override - public boolean supportsAppInfoDropTarget() { - return true; - } - - @Override - public boolean supportsDeleteDropTarget() { - return true; - } - public boolean isDropEnabled() { return true; } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 4eba5c6df6..03e9c395c6 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -290,24 +290,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc dragController.removeDragListener(this); } }); - mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions()); - return false; - } - @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; + DragOptions options = new DragOptions(); + options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx; + mLauncher.getWorkspace().beginDragShared(v, this, options); + return false; } @Override diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java index d0f2629aa7..727fb51ce2 100644 --- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java @@ -158,21 +158,6 @@ public abstract class BaseItemDragListener implements } } - @Override - public boolean supportsAppInfoDropTarget() { - return false; - } - - @Override - public boolean supportsDeleteDropTarget() { - return false; - } - - @Override - public float getIntrinsicIconScaleFactor() { - return 1f; - } - @Override public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java index 9433aadc7e..f108f8b535 100644 --- a/src/com/android/launcher3/dragndrop/DragOptions.java +++ b/src/com/android/launcher3/dragndrop/DragOptions.java @@ -34,6 +34,9 @@ public class DragOptions { /** Determines when a pre-drag should transition to a drag. By default, this is immediate. */ public PreDragCondition preDragCondition = null; + /** Scale of the icons over the workspace icon size. */ + public float intrinsicIconScaleFactor = 1f; + /** * Specifies a condition that must be met before DragListener#onDragStart() is called. * By default, there is no condition and onDragStart() is called immediately following diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index de4de36e88..e182da140f 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -706,6 +706,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mContent.setCurrentPage(0); } + @Override public boolean acceptDrop(DragObject d) { final ItemInfo item = d.dragInfo; final int itemType = item.itemType; @@ -938,21 +939,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } } - @Override - public float getIntrinsicIconScaleFactor() { - return 1f; - } - - @Override - public boolean supportsAppInfoDropTarget() { - return true; - } - - @Override - public boolean supportsDeleteDropTarget() { - return true; - } - private void updateItemLocationsInDatabaseBatch() { ArrayList list = getItemsInReadingOrder(); ArrayList items = new ArrayList(); diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 409e5aefd5..5c49b4bdf3 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -792,21 +792,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra return mReduceHeightAnimatorSet; } - @Override - public boolean supportsAppInfoDropTarget() { - return true; - } - - @Override - public boolean supportsDeleteDropTarget() { - return false; - } - - @Override - public float getIntrinsicIconScaleFactor() { - return 1f; - } - @Override public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index acec3dd3b8..39dd0d498e 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -196,25 +196,6 @@ public class WidgetsContainerView extends BaseContainerView // Drag related handling methods that implement {@link DragSource} interface. // - @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 onDropCompleted(View target, DragObject d, boolean isFlingToDelete, boolean success) { From d0d070339938013f5cb9f652d68d7bb454e15f52 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 4 Oct 2017 10:40:28 -0700 Subject: [PATCH 039/885] Removing predicted apps reset on every onResume Predicted apps should be pushed by the called whenever they change instead of Launcher polling them on every UI update. Bug: 67305604 Change-Id: Ibd3d809b09b7d8fd39036f69367e8580fb90dcef --- src/com/android/launcher3/Launcher.java | 34 +++---------------- .../android/launcher3/LauncherCallbacks.java | 5 --- .../allapps/AllAppsContainerView.java | 7 ---- .../allapps/AllAppsTransitionController.java | 9 ++--- .../launcher3/testing/LauncherExtension.java | 9 ----- 5 files changed, 8 insertions(+), 56 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9d31492daf..030d625124 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -867,17 +867,10 @@ public class Launcher extends BaseActivity 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 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 */, !launchedFromApp /* updatePredictedApps */); + showAppsView(false /* animated */); } else if (mOnResumeState == State.WIDGETS) { showWidgetsView(false, false); } - if (mOnResumeState != State.APPS) { - tryAndUpdatePredictedApps(); - } mOnResumeState = State.NONE; mPaused = false; @@ -2108,7 +2101,7 @@ public class Launcher extends BaseActivity if (!isAppsViewVisible()) { getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, ControlType.ALL_APPS_BUTTON); - showAppsView(true /* animated */, true /* updatePredictedApps */); + showAppsView(true /* animated */); } else { showWorkspace(true); } @@ -2673,11 +2666,8 @@ public class Launcher extends BaseActivity /** * Shows the apps view. */ - public void showAppsView(boolean animated, boolean updatePredictedApps) { + public void showAppsView(boolean animated) { markAppsViewShown(); - if (updatePredictedApps) { - tryAndUpdatePredictedApps(); - } showAppsOrWidgets(State.APPS, animated); } @@ -2797,7 +2787,7 @@ public class Launcher extends BaseActivity public void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { - showAppsView(true /* animated */, false /* updatePredictedApps */); + showAppsView(true /* animated */); } else if (mState == State.WIDGETS_SPRING_LOADED) { showWidgetsView(true, false); } else if (mState == State.WORKSPACE_SPRING_LOADED) { @@ -2805,19 +2795,6 @@ public class Launcher extends BaseActivity } } - /** - * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was - * resumed. - */ - public void tryAndUpdatePredictedApps() { - if (mLauncherCallbacks != null) { - List> apps = mLauncherCallbacks.getPredictedApps(); - if (apps != null) { - mAppsView.setPredictedApps(apps); - } - } - } - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); @@ -3532,7 +3509,6 @@ public class Launcher extends BaseActivity // Update AllApps if (mAppsView != null) { mAppsView.removeApps(appInfos); - tryAndUpdatePredictedApps(); } } @@ -3696,7 +3672,7 @@ public class Launcher extends BaseActivity switch (keyCode) { case KeyEvent.KEYCODE_A: if (mState == State.WORKSPACE) { - showAppsView(true, true); + showAppsView(true); return true; } break; diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index d1e2b621a3..2c9a23fa98 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -19,14 +19,10 @@ package com.android.launcher3; import android.content.Intent; import android.os.Bundle; import android.view.Menu; -import android.view.View; - -import com.android.launcher3.util.ComponentKeyMapper; 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 @@ -87,5 +83,4 @@ public interface LauncherCallbacks { */ boolean shouldMoveToDefaultScreenOnHomeIntent(); boolean hasSettings(); - List> getPredictedApps(); } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 4eba5c6df6..246a77a172 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -114,13 +114,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } } - /** - * 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/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index f249c90c9b..2cc0781199 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -216,7 +216,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect Action.Direction.UP, containerType); } - mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */); + mLauncher.showAppsView(true /* animated */); if (hasSpringAnimationHandler()) { mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */); // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) @@ -239,7 +239,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect Action.Direction.UP, containerType); } - mLauncher.showAppsView(true, /* animated */ false /* updatePredictedApps */); + mLauncher.showAppsView(true /* animated */); } } } @@ -256,10 +256,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect // Initialize values that should not change until #onDragEnd mStatusBarHeight = mLauncher.getDragLayer().getInsets().top; mHotseat.setVisibility(View.VISIBLE); - if (!mLauncher.isAllAppsVisible()) { - mLauncher.tryAndUpdatePredictedApps(); - mAppsView.setVisibility(View.VISIBLE); - } + mAppsView.setVisibility(View.VISIBLE); } } diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index 355963bfeb..a1a4d75557 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -7,13 +7,10 @@ import android.view.Menu; import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherCallbacks; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.ComponentKeyMapper; 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 @@ -149,12 +146,6 @@ public class LauncherExtension extends Launcher { return false; } - @Override - public List> getPredictedApps() { - // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS - return new ArrayList<>(); - } - @Override public void onAttachedToWindow() { } From d0c3d3a53c4df1669d53dc7111e71a86a340a2c2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 3 Oct 2017 13:39:42 -0700 Subject: [PATCH 040/885] Moving the wallpaper offset interpolator to background thread Bug: 67305604 Bug: 25321240 Change-Id: I2d667a4d1a4b91ec2a11e5e171f8a1d1c4222b33 --- src/com/android/launcher3/Launcher.java | 1 - src/com/android/launcher3/Workspace.java | 4 - .../util/WallpaperOffsetInterpolator.java | 307 ++++++++++-------- 3 files changed, 170 insertions(+), 142 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4388eb4a0a..6342bc0639 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -945,7 +945,6 @@ public class Launcher extends BaseActivity } updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); - mWorkspace.onResume(); // Process any items that were added while Launcher was away. InstallShortcutReceiver.disableAndFlushInstallQueue( diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 74c96a310c..53110dccac 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1459,10 +1459,6 @@ public class Workspace extends PagedView mWallpaperOffset.setWindowToken(null); } - protected void onResume() { - mWallpaperOffset.onResume(); - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mUnlockWallpaperFromDefaultPageOnLayout) { diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java index f99efcec94..ec494f1818 100644 --- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java +++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java @@ -1,9 +1,15 @@ package com.android.launcher3.util; import android.app.WallpaperManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; import android.os.IBinder; +import android.os.Message; +import android.os.SystemClock; import android.util.Log; -import android.view.Choreographer; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -13,60 +19,30 @@ import com.android.launcher3.Workspace; /** * Utility class to handle wallpaper scrolling along with workspace. */ -public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback { +public class WallpaperOffsetInterpolator extends BroadcastReceiver { + + private static final int[] sTempInt = new int[2]; private static final String TAG = "WPOffsetInterpolator"; private static final int ANIMATION_DURATION = 250; // Don't use all the wallpaper for parallax until you have at least this many pages private static final int MIN_PARALLAX_PAGE_SPAN = 4; - private final Choreographer mChoreographer; - private final Interpolator mInterpolator; - private final WallpaperManager mWallpaperManager; private final Workspace mWorkspace; private final boolean mIsRtl; + private final Handler mHandler; + private boolean mRegistered = false; private IBinder mWindowToken; private boolean mWallpaperIsLiveWallpaper; - private float mLastSetWallpaperOffsetSteps = 0; - private float mFinalOffset = 0.0f; - private float mCurrentOffset = 0.5f; // to force an initial update - private boolean mWaitingForUpdate; private boolean mLockedToDefaultPage; - - private boolean mAnimating; - private long mAnimationStartTime; - private float mAnimationStartOffset; - int mNumScreens; - int mNumPagesForWallpaperParallax; + private int mNumScreens; public WallpaperOffsetInterpolator(Workspace workspace) { - mChoreographer = Choreographer.getInstance(); - mInterpolator = new DecelerateInterpolator(1.5f); - mWorkspace = workspace; - mWallpaperManager = WallpaperManager.getInstance(workspace.getContext()); mIsRtl = Utilities.isRtl(workspace.getResources()); - } - - @Override - public void doFrame(long frameTimeNanos) { - updateOffset(false); - } - - private void updateOffset(boolean force) { - if (mWaitingForUpdate || force) { - mWaitingForUpdate = false; - if (computeScrollOffset() && mWindowToken != null) { - try { - mWallpaperManager.setWallpaperOffsets(mWindowToken, getCurrX(), 0.5f); - setWallpaperOffsetSteps(); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Error updating wallpaper offset: " + e); - } - } - } + mHandler = new OffsetHandler(workspace.getContext()); } /** @@ -80,46 +56,25 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback return mLockedToDefaultPage; } - public boolean computeScrollOffset() { - final float oldOffset = mCurrentOffset; - if (mAnimating) { - long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime; - float t0 = durationSinceAnimation / (float) ANIMATION_DURATION; - float t1 = mInterpolator.getInterpolation(t0); - mCurrentOffset = mAnimationStartOffset + - (mFinalOffset - mAnimationStartOffset) * t1; - mAnimating = durationSinceAnimation < ANIMATION_DURATION; - } else { - mCurrentOffset = mFinalOffset; - } - - if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) { - scheduleUpdate(); - } - if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) { - return true; - } - return false; - } - /** + * Computes the wallpaper offset as an int ratio (out[0] / out[1]) + * * TODO: do different behavior if it's a live wallpaper? */ - public float wallpaperOffsetForScroll(int scroll) { + private void wallpaperOffsetForScroll(int scroll, int numScrollingPages, final int[] out) { + out[1] = 1; + // To match the default wallpaper behavior in the system, we default to either the left // or right edge on initialization - int numScrollingPages = getNumScreensExcludingEmpty(); if (mLockedToDefaultPage || numScrollingPages <= 1) { - return mIsRtl ? 1f : 0f; + out[0] = mIsRtl ? 1 : 0; + return; } // Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace // screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN) - if (mWallpaperIsLiveWallpaper) { - mNumPagesForWallpaperParallax = numScrollingPages; - } else { - mNumPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages); - } + int numPagesForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollingPages : + Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages); // Offset by the custom screen int leftPageIndex; @@ -136,106 +91,184 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback int leftPageScrollX = mWorkspace.getScrollForPage(leftPageIndex); int rightPageScrollX = mWorkspace.getScrollForPage(rightPageIndex); int scrollRange = rightPageScrollX - leftPageScrollX; - if (scrollRange == 0) { - return 0f; + if (scrollRange <= 0) { + out[0] = 0; + return; } // 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 = scroll - leftPageScrollX - mWorkspace.getLayoutTransitionOffsetForPage(0); - float offset = Utilities.boundToRange((float) adjustedScroll / scrollRange, 0f, 1f); + adjustedScroll = Utilities.boundToRange(adjustedScroll, 0, scrollRange); + out[1] = (numPagesForWallpaperParallax - 1) * scrollRange; // The offset is now distributed 0..1 between the left and right pages that we care about, // so we just map that between the pages that we are using for parallax - float rtlOffset = 0; + int rtlOffset = 0; if (mIsRtl) { // In RTL, the pages are right aligned, so adjust the offset from the end - rtlOffset = (float) ((mNumPagesForWallpaperParallax - 1) - (numScrollingPages - 1)) / - (mNumPagesForWallpaperParallax - 1); + rtlOffset = out[1] - (numScrollingPages - 1) * scrollRange; } - return rtlOffset + offset * - ((float) (numScrollingPages - 1) / (mNumPagesForWallpaperParallax - 1)); + out[0] = rtlOffset + adjustedScroll * (numScrollingPages - 1); } - private float wallpaperOffsetForCurrentScroll() { - return wallpaperOffsetForScroll(mWorkspace.getScrollX()); - } - - private int numEmptyScreensToIgnore() { - int numScrollingPages = mWorkspace.getChildCount(); - if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) { - return 1; - } else { - return 0; - } + public float wallpaperOffsetForScroll(int scroll) { + wallpaperOffsetForScroll(scroll, getNumScreensExcludingEmpty(), sTempInt); + return ((float) sTempInt[0]) / sTempInt[1]; } private int getNumScreensExcludingEmpty() { - return mWorkspace.getChildCount() - numEmptyScreensToIgnore(); + int numScrollingPages = mWorkspace.getChildCount(); + if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) { + return numScrollingPages - 1; + } else { + return numScrollingPages; + } } public void syncWithScroll() { - float offset = wallpaperOffsetForCurrentScroll(); - setFinalX(offset); - updateOffset(true); - } - - public float getCurrX() { - return mCurrentOffset; - } - - public float getFinalX() { - return mFinalOffset; - } - - private void animateToFinal() { - mAnimating = true; - mAnimationStartOffset = mCurrentOffset; - mAnimationStartTime = System.currentTimeMillis(); - } - - private void setWallpaperOffsetSteps() { - // Set wallpaper offset steps (1 / (number of screens - 1)) - float xOffset = 1.0f / (mNumPagesForWallpaperParallax - 1); - if (xOffset != mLastSetWallpaperOffsetSteps) { - mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f); - mLastSetWallpaperOffsetSteps = xOffset; - } - } - - public void setFinalX(float x) { - scheduleUpdate(); - mFinalOffset = Math.max(0f, Math.min(x, 1f)); - if (getNumScreensExcludingEmpty() != mNumScreens) { - if (mNumScreens > 0 && Float.compare(mCurrentOffset, mFinalOffset) != 0) { - // Don't animate if we're going from 0 screens, or if the final offset is the same - // as the current offset - animateToFinal(); + int numScreens = getNumScreensExcludingEmpty(); + wallpaperOffsetForScroll(mWorkspace.getScrollX(), numScreens, sTempInt); + Message msg = Message.obtain(mHandler, MSG_UPDATE_OFFSET, sTempInt[0], sTempInt[1], + mWindowToken); + if (numScreens != mNumScreens) { + if (mNumScreens > 0) { + // Don't animate if we're going from 0 screens + msg.what = MSG_START_ANIMATION; } - mNumScreens = getNumScreensExcludingEmpty(); + mNumScreens = numScreens; + updateOffset(); } + msg.sendToTarget(); } - private void scheduleUpdate() { - if (!mWaitingForUpdate) { - mChoreographer.postFrameCallback(this); - mWaitingForUpdate = true; + private void updateOffset() { + int numPagesForWallpaperParallax; + if (mWallpaperIsLiveWallpaper) { + numPagesForWallpaperParallax = mNumScreens; + } else { + numPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens); } + Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, numPagesForWallpaperParallax, 0, + mWindowToken).sendToTarget(); } public void jumpToFinal() { - mCurrentOffset = mFinalOffset; - } - - public void onResume() { - mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null; - // Force the wallpaper offset steps to be set again, because another app might have changed - // them - mLastSetWallpaperOffsetSteps = 0f; + Message.obtain(mHandler, MSG_JUMP_TO_FINAL, mWindowToken).sendToTarget(); } public void setWindowToken(IBinder token) { mWindowToken = token; + if (mWindowToken == null && mRegistered) { + mWorkspace.getContext().unregisterReceiver(this); + mRegistered = false; + } else if (mWindowToken != null && !mRegistered) { + mWorkspace.getContext() + .registerReceiver(this, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)); + onReceive(mWorkspace.getContext(), null); + mRegistered = true; + } + } + + @Override + public void onReceive(Context context, Intent intent) { + mWallpaperIsLiveWallpaper = + WallpaperManager.getInstance(mWorkspace.getContext()).getWallpaperInfo() != null; + updateOffset(); + } + + private static final int MSG_START_ANIMATION = 1; + private static final int MSG_UPDATE_OFFSET = 2; + private static final int MSG_APPLY_OFFSET = 3; + private static final int MSG_SET_NUM_PARALLAX = 4; + private static final int MSG_JUMP_TO_FINAL = 5; + + private static class OffsetHandler extends Handler { + + private final Interpolator mInterpolator; + private final WallpaperManager mWM; + + private float mCurrentOffset = 0.5f; // to force an initial update + private boolean mAnimating; + private long mAnimationStartTime; + private float mAnimationStartOffset; + + private float mFinalOffset; + private float mOffsetX; + + public OffsetHandler(Context context) { + super(UiThreadHelper.getBackgroundLooper()); + mInterpolator = new DecelerateInterpolator(1.5f); + mWM = WallpaperManager.getInstance(context); + } + + @Override + public void handleMessage(Message msg) { + final IBinder token = (IBinder) msg.obj; + if (token == null) { + return; + } + + switch (msg.what) { + case MSG_START_ANIMATION: { + mAnimating = true; + mAnimationStartOffset = mCurrentOffset; + mAnimationStartTime = msg.getWhen(); + // Follow through + } + case MSG_UPDATE_OFFSET: + mFinalOffset = ((float) msg.arg1) / msg.arg2; + // Follow through + case MSG_APPLY_OFFSET: { + float oldOffset = mCurrentOffset; + if (mAnimating) { + long durationSinceAnimation = SystemClock.uptimeMillis() + - mAnimationStartTime; + float t0 = durationSinceAnimation / (float) ANIMATION_DURATION; + float t1 = mInterpolator.getInterpolation(t0); + mCurrentOffset = mAnimationStartOffset + + (mFinalOffset - mAnimationStartOffset) * t1; + mAnimating = durationSinceAnimation < ANIMATION_DURATION; + } else { + mCurrentOffset = mFinalOffset; + } + + if (Float.compare(mCurrentOffset, oldOffset) != 0) { + setOffsetSafely(token); + // Force the wallpaper offset steps to be set again, because another app + // might have changed them + mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f); + } + if (mAnimating) { + // If we are animating, keep updating the offset + Message.obtain(this, MSG_APPLY_OFFSET, token).sendToTarget(); + } + return; + } + case MSG_SET_NUM_PARALLAX: { + // Set wallpaper offset steps (1 / (number of screens - 1)) + mOffsetX = 1.0f / (msg.arg1 - 1); + mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f); + return; + } + case MSG_JUMP_TO_FINAL: { + if (Float.compare(mCurrentOffset, mFinalOffset) != 0) { + mCurrentOffset = mFinalOffset; + setOffsetSafely(token); + } + mAnimating = false; + return; + } + } + } + + private void setOffsetSafely(IBinder token) { + try { + mWM.setWallpaperOffsets(token, mCurrentOffset, 0.5f); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error updating wallpaper offset: " + e); + } + } } } \ No newline at end of file From ba47faae6a84231e01af3f5147ddf05a2dabe216 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 4 Oct 2017 16:50:57 -0700 Subject: [PATCH 041/885] Moving widget reinflation to push model Instead of checking every widget, a widget which needs reinflation would post a callback on launcher avoiding unnecessary loops in onResume Bug: 67305604 Change-Id: I53e08d6f4795f7b716a9debca5c0c68dd25a7afe --- src/com/android/launcher3/Launcher.java | 13 +-- .../launcher3/LauncherAppWidgetHost.java | 1 - .../launcher3/LauncherAppWidgetHostView.java | 84 +++++++++++++++---- .../launcher3/PendingAppWidgetHostView.java | 4 +- src/com/android/launcher3/Workspace.java | 24 ------ 5 files changed, 71 insertions(+), 55 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9d31492daf..7688b9d092 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -908,14 +908,6 @@ public class Launcher extends BaseActivity mWaitingForResume.setStayPressed(false); } - // It is possible that widgets can receive updates while launcher is not in the foreground. - // Consequently, the widgets will be inflated in the orientation of the foreground activity - // (framework issue). On resuming, we ensure that any widgets are inflated for the current - // orientation. - if (!isWorkspaceLoading()) { - getWorkspace().reinflateWidgetsIfNecessary(); - } - updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); mWorkspace.onResume(); @@ -3257,7 +3249,10 @@ public class Launcher extends BaseActivity info.pendingItemInfo = null; } - mWorkspace.reinflateWidgetsIfNecessary(); + if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) { + view.reinflate(); + } + getModelWriter().updateItemInDatabase(info); return info; } diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 819f23faeb..70440fa309 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -122,7 +122,6 @@ public class LauncherAppWidgetHost extends AppWidgetHost { context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(appWidget.initialLayout, lahv); lahv.setAppWidget(0, appWidget); - lahv.updateLastInflationOrientation(); return lahv; } else { try { diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 7fe1308764..6f953e5f0a 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; +import android.content.res.Configuration; import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; @@ -58,10 +59,14 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView private final CheckLongPressHelper mLongPressHelper; private final StylusEventHelper mStylusEventHelper; - private final Context mContext; + private final Launcher mLauncher; + + private static final int DONT_REINFLATE = 0; + private static final int REINFLATE_ON_RESUME = 1; + private static final int REINFLATE_ON_CONFIG_CHANGE = 2; @ViewDebug.ExportedProperty(category = "launcher") - private int mPreviousOrientation; + private int mReinflateStatus; private float mSlop; @@ -85,11 +90,11 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView public LauncherAppWidgetHostView(Context context) { super(context); - mContext = context; + mLauncher = Launcher.getLauncher(context); mLongPressHelper = new CheckLongPressHelper(this, this); mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); mInflater = LayoutInflater.from(context); - setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate()); + setAccessibilityDelegate(mLauncher.getAccessibilityDelegate()); setBackgroundResource(R.drawable.widget_internal_focus_bg); if (Utilities.ATLEAST_OREO) { @@ -112,18 +117,28 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView 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 - updateLastInflationOrientation(); super.updateAppWidget(remoteViews); // The provider info or the views might have changed. checkIfAutoAdvance(); + + // It is possible that widgets can receive updates while launcher is not in the foreground. + // Consequently, the widgets will be inflated for the orientation of the foreground activity + // (framework issue). On resuming, we ensure that any widgets are inflated for the current + // orientation. + if (mReinflateStatus == DONT_REINFLATE && !isSameOrientation()) { + mReinflateStatus = REINFLATE_ON_RESUME; + if (!mLauncher.waitUntilResume(new ReInflateRunnable())) { + mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE; + } + } + } + + private boolean isSameOrientation() { + return mLauncher.getResources().getConfiguration().orientation == + mLauncher.getOrientation(); } private boolean checkScrollableRecursively(ViewGroup viewGroup) { @@ -142,14 +157,6 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView return false; } - public boolean isReinflateRequired(int orientation) { - // Re-inflate is required if the orientation has changed since last inflated. - if (mPreviousOrientation != orientation) { - return true; - } - return false; - } - public boolean onInterceptTouchEvent(MotionEvent ev) { // Just in case the previous long press hasn't been cleared, we make sure to start fresh // on touch down. @@ -473,4 +480,45 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView public PointF getTranslationForCentering() { return mTranslationForCentering; } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + if (mReinflateStatus == REINFLATE_ON_CONFIG_CHANGE) { + // We are finally in the same orientation + reinflateIfNecessary(); + } + } + + private void reinflateIfNecessary() { + if (!isSameOrientation()) { + // We cannot reinflate yet, wait until next config change + mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE; + return; + } + + mReinflateStatus = DONT_REINFLATE; + if (isAttachedToWindow()) { + LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag(); + reinflate(); + } + } + + public void reinflate() { + LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag(); + // Remove and rebind the current widget (which was inflated in the wrong + // orientation), but don't delete it from the database + mLauncher.removeItem(this, info, false /* deleteFromDb */); + mLauncher.bindAppWidget(info); + } + + private class ReInflateRunnable implements Runnable { + @Override + public void run() { + if (mReinflateStatus == REINFLATE_ON_RESUME) { + reinflateIfNecessary(); + } + } + } } diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index c2d5501693..b86d413185 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -110,9 +110,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView mClickListener = l; } - @Override - public boolean isReinflateRequired(int orientation) { - // Re inflate is required any time the widget restore status changes + public boolean isReinflateIfNeeded() { return mStartState != mInfo.restoreStatus; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b80bdc0c96..7297ee12e9 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1043,30 +1043,6 @@ public class Workspace extends PagedView return super.onInterceptTouchEvent(ev); } - protected void reinflateWidgetsIfNecessary() { - final int clCount = getChildCount(); - for (int i = 0; i < clCount; i++) { - CellLayout cl = (CellLayout) getChildAt(i); - ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets(); - final int itemCount = swc.getChildCount(); - for (int j = 0; j < itemCount; j++) { - View v = swc.getChildAt(j); - - if (v instanceof LauncherAppWidgetHostView - && v.getTag() instanceof LauncherAppWidgetInfo) { - LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag(); - LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) v; - if (lahv.isReinflateRequired(mLauncher.getOrientation())) { - // Remove and rebind the current widget (which was inflated in the wrong - // orientation), but don't delete it from the database - mLauncher.removeItem(lahv, info, false /* deleteFromDb */); - mLauncher.bindAppWidget(info); - } - } - } - } - } - @Override protected void determineScrollingStart(MotionEvent ev) { if (!isFinishedSwitchingState()) return; From 830578ff2299c04d1732d9474ef9a0984700d79b Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 29 Sep 2017 09:58:10 -0700 Subject: [PATCH 042/885] Cancel notification group summary when all children are cancelled. This requires maintaining notification group information by mapping groupKey's to the summary key and child keys. Bug: 65100024 Change-Id: Idd352ce5e243a0762bf30a9c79d36681456a1b17 --- .../notification/NotificationGroup.java | 52 +++++++++++++++++++ .../notification/NotificationListener.java | 33 +++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/com/android/launcher3/notification/NotificationGroup.java diff --git a/src/com/android/launcher3/notification/NotificationGroup.java b/src/com/android/launcher3/notification/NotificationGroup.java new file mode 100644 index 0000000000..bce211754d --- /dev/null +++ b/src/com/android/launcher3/notification/NotificationGroup.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.notification; + +import java.util.HashSet; +import java.util.Set; + +/** + * Contains data related to a group of notifications, like the group summary key and the child keys. + */ +public class NotificationGroup { + private String mGroupSummaryKey; + private Set mChildKeys; + + public NotificationGroup() { + mChildKeys = new HashSet<>(); + } + + public void setGroupSummaryKey(String groupSummaryKey) { + mGroupSummaryKey = groupSummaryKey; + } + + public String getGroupSummaryKey() { + return mGroupSummaryKey; + } + + public void addChildKey(String childKey) { + mChildKeys.add(childKey); + } + + public void removeChildKey(String childKey) { + mChildKeys.remove(childKey); + } + + public boolean isEmpty() { + return mChildKeys.isEmpty(); + } +} diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java index 91266263f1..7b70df77e4 100644 --- a/src/com/android/launcher3/notification/NotificationListener.java +++ b/src/com/android/launcher3/notification/NotificationListener.java @@ -38,7 +38,9 @@ import com.android.launcher3.util.SettingsObserver; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING; @@ -66,6 +68,8 @@ public class NotificationListener extends NotificationListenerService { private final Handler mWorkerHandler; private final Handler mUiHandler; private final Ranking mTempRanking = new Ranking(); + /** Maps groupKey's to the corresponding group of notifications. */ + private final Map mNotificationGroupMap = new HashMap<>(); private SettingsObserver mNotificationBadgingObserver; @@ -227,6 +231,15 @@ public class NotificationListener extends NotificationListenerService { NotificationKeyData.fromNotification(sbn)); mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey) .sendToTarget(); + + NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey()); + if (notificationGroup != null) { + notificationGroup.removeChildKey(sbn.getKey()); + if (notificationGroup.isEmpty()) { + cancelNotification(notificationGroup.getGroupSummaryKey()); + mNotificationGroupMap.remove(sbn.getGroupKey()); + } + } } /** This makes a potentially expensive binder call and should be run on a background thread. */ @@ -264,18 +277,34 @@ public class NotificationListener extends NotificationListenerService { } private boolean shouldBeFilteredOut(StatusBarNotification sbn) { + Notification notification = sbn.getNotification(); + + boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0; + if (sbn.isGroup()) { + // Maintain group info so we can cancel the summary when the last child is canceled. + NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey()); + if (notificationGroup == null) { + notificationGroup = new NotificationGroup(); + mNotificationGroupMap.put(sbn.getGroupKey(), notificationGroup); + } + if (isGroupHeader) { + notificationGroup.setGroupSummaryKey(sbn.getKey()); + } else { + notificationGroup.addChildKey(sbn.getKey()); + } + } + getCurrentRanking().getRanking(sbn.getKey(), mTempRanking); if (!mTempRanking.canShowBadge()) { return true; } - Notification notification = sbn.getNotification(); if (mTempRanking.getChannel().getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { // Special filtering for the default, legacy "Miscellaneous" channel. if ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) { return true; } } - boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0; + CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE); CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT); boolean missingTitleAndText = TextUtils.isEmpty(title) && TextUtils.isEmpty(text); From 53b993688f3c536d76bc5040989d307dcc811fbe Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Oct 2017 14:58:56 -0700 Subject: [PATCH 043/885] Instead of posponing state change until onResume, applying the state change directly in onCreate and onNewIntent() > State UI do not depend on view measurement and each state is able to adapt itself based on measure/layout. We do not need to wait for first layout pass before chaning the state. Bug: 67305604 Change-Id: I8906e3245cc07aac0038fe630e2a16875d4e8949 --- src/com/android/launcher3/Launcher.java | 39 +++++-------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ba31926c28..2e893654c6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -119,7 +119,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.ActivityResultInfo; import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.ComponentKeyMapper; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; @@ -203,7 +202,7 @@ public class Launcher extends BaseActivity static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; /** The different states that Launcher can be in. */ - enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED, + enum State { WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED } @Thunk State mState = State.WORKSPACE; @@ -250,11 +249,6 @@ public class Launcher extends BaseActivity // that results in widgets being inflated in the wrong orientation. private int mOrientation; - // We set the state in both onCreate and then onNewIntent in some cases, which causes both - // scroll issues (because the workspace may not have been measured yet) and extra work. - // Instead, just save the state that we need to restore Launcher to, and commit it in onResume. - private State mOnResumeState = State.NONE; - private SpannableStringBuilder mDefaultKeySsb = null; @Thunk boolean mWorkspaceLoading = true; @@ -862,17 +856,6 @@ public class Launcher extends BaseActivity TraceHelper.partitionSection("ON_RESUME", "superCall"); getUserEventDispatcher().resetElapsedSessionMillis(); - - // Restore the previous launcher state - if (mOnResumeState == State.WORKSPACE) { - showWorkspace(false); - } else if (mOnResumeState == State.APPS) { - showAppsView(false /* animated */); - } else if (mOnResumeState == State.WIDGETS) { - showWidgetsView(false, false); - } - mOnResumeState = State.NONE; - mPaused = false; if (mOnResumeNeedsLoad) { setWorkspaceLoading(true); @@ -1086,8 +1069,10 @@ public class Launcher extends BaseActivity State[] stateValues = State.values(); State state = (stateOrdinal >= 0 && stateOrdinal < stateValues.length) ? stateValues[stateOrdinal] : State.WORKSPACE; - if (state == State.APPS || state == State.WIDGETS) { - mOnResumeState = state; + if (state == State.APPS) { + showAppsView(false /* animated */); + } else if (state == State.WIDGETS) { + showWidgetsView(false, false); } PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS); @@ -1499,15 +1484,7 @@ public class Launcher extends BaseActivity // In all these cases, only animate if we're already on home AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome); - exitSpringLoadedDragMode(); - - // If we are already on home, then just animate back to the workspace, - // otherwise, just wait until onResume to set the state back to Workspace - if (alreadyOnHome) { - showWorkspace(true); - } else { - mOnResumeState = State.WORKSPACE; - } + showWorkspace(alreadyOnHome /* animated */); final View v = getWindow().peekDecorView(); if (v != null && v.getWindowToken() != null) { @@ -2551,11 +2528,11 @@ public class Launcher extends BaseActivity } public boolean isAppsViewVisible() { - return (mState == State.APPS) || (mOnResumeState == State.APPS); + return mState == State.APPS; } public boolean isWidgetsViewVisible() { - return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS); + return mState == State.WIDGETS; } @Override From 3dce5f3f507d0fbd27ab9c62ad66e0259abf22b3 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Oct 2017 11:40:05 -0700 Subject: [PATCH 044/885] Merging duplicate logic for deferred drop handling in Worksace and Folder > All the logic is contained in UninstallDropTarget > Also fixing a bug were mWaitingForResume was not cleared Bug: 34692289 Change-Id: I617475ce53062902d6817954fb608198e6e03d3c --- src/com/android/launcher3/BubbleTextView.java | 10 +- .../android/launcher3/ButtonDropTarget.java | 2 +- src/com/android/launcher3/DropTarget.java | 3 +- src/com/android/launcher3/InfoDropTarget.java | 35 ++-- src/com/android/launcher3/Launcher.java | 59 +++--- .../launcher3/UninstallDropTarget.java | 172 ++++++++++-------- src/com/android/launcher3/Workspace.java | 44 +---- .../LauncherAccessibilityDelegate.java | 2 +- .../launcher3/dragndrop/DragController.java | 2 +- .../launcher3/dragndrop/DragOptions.java | 6 + src/com/android/launcher3/folder/Folder.java | 45 +---- .../launcher3/popup/SystemShortcut.java | 2 +- 12 files changed, 170 insertions(+), 212 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 485125e03e..cd72fbace7 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -42,6 +42,7 @@ import android.widget.TextView; import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.folder.FolderIcon; @@ -59,7 +60,7 @@ import java.text.NumberFormat; * because we want to make the bubble taller than the text and TextView's clip is * too aggressive. */ -public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver { +public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback { private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_ALL_APPS = 1; @@ -330,6 +331,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver { refreshDrawableState(); } + @Override + public void onLauncherResume() { + // Reset the pressed state of icon that was locked in the press state while activity + // was launching + setStayPressed(false); + } + void clearPressedBackground() { setPressed(false); setStayPressed(false); diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 3759300624..ffc2b02e2b 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -215,7 +215,7 @@ public abstract class ButtonDropTarget extends TextView * On drop animate the dropView to the icon. */ @Override - public void onDrop(final DragObject d) { + public void onDrop(final DragObject d, final DragOptions options) { final DragLayer dragLayer = mLauncher.getDragLayer(); final Rect from = new Rect(); dragLayer.getViewRectRelativeToSelf(d.dragView, from); diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 7d047d748d..2307b89246 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.graphics.Rect; import com.android.launcher3.accessibility.DragViewStateAnnouncer; +import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; /** @@ -106,7 +107,7 @@ public interface DropTarget { /** * Handle an object being dropped on the DropTarget */ - void onDrop(DragObject dragObject); + void onDrop(DragObject dragObject, DragOptions options); void onDragEnter(DragObject dragObject); diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index eb6a7045ef..f78cde5b8b 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -49,28 +49,29 @@ public class InfoDropTarget extends UninstallDropTarget { } @Override - public void completeDrop(DragObject d) { - DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback - ? (DropTargetResultCallback) d.dragSource : null; - startDetailsActivityForInfo(d.dragInfo, mLauncher, callback); + protected ComponentName performDropAction(DragObject d) { + return performDropAction(mLauncher, d.dragInfo, null, null); } /** * @return Whether the activity was started. */ public static boolean startDetailsActivityForInfo( - ItemInfo info, Launcher launcher, DropTargetResultCallback callback) { - return startDetailsActivityForInfo(info, launcher, callback, null, null); + ItemInfo info, Launcher launcher, Rect sourceBounds, Bundle opts) { + return performDropAction(launcher, info, sourceBounds, opts) != null; } - public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher, - DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) { + /** + * Performs the drop action and returns the target component for the dragObject or null if + * the action was not performed. + */ + private static ComponentName performDropAction(Context context, ItemInfo info, + Rect sourceBounds, Bundle opts) { if (info instanceof PromiseAppInfo) { PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info; - launcher.startActivity(promiseAppInfo.getMarketIntent()); - return true; + context.startActivity(promiseAppInfo.getMarketIntent()); + return null; } - boolean result = false; ComponentName componentName = null; if (info instanceof AppInfo) { componentName = ((AppInfo) info).componentName; @@ -83,19 +84,15 @@ public class InfoDropTarget extends UninstallDropTarget { } if (componentName != null) { try { - LauncherAppsCompat.getInstance(launcher) + LauncherAppsCompat.getInstance(context) .showAppDetailsForProfile(componentName, info.user, sourceBounds, opts); - result = true; + return componentName; } catch (SecurityException | ActivityNotFoundException e) { - Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); + Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Unable to launch settings", e); } } - - if (callback != null) { - sendUninstallResult(launcher, result, componentName, info.user, callback); - } - return result; + return null; } @Override diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ba31926c28..1e3626d75a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -263,7 +263,8 @@ public class Launcher extends BaseActivity private boolean mOnResumeNeedsLoad; private final ArrayList mBindOnResumeCallbacks = new ArrayList<>(); - private final ArrayList mOnResumeCallbacks = new ArrayList<>(); + private OnResumeCallback mOnResumeCallback; + private ViewOnDrawExecutor mPendingExecutor; private LauncherModel mModel; @@ -288,11 +289,6 @@ public class Launcher extends BaseActivity // it from the context. private SharedPreferences mSharedPrefs; - // 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; - // Exiting spring loaded mode happens with a delay. This runnable object triggers the // state transition. If another state transition happened during this delay, // simply unregister this runnable. @@ -887,19 +883,8 @@ public class Launcher extends BaseActivity } mBindOnResumeCallbacks.clear(); } - if (mOnResumeCallbacks.size() > 0) { - for (int i = 0; i < mOnResumeCallbacks.size(); i++) { - mOnResumeCallbacks.get(i).run(); - } - mOnResumeCallbacks.clear(); - } - // Reset the pressed state of icons that were locked in the press state while activities - // were launching - if (mWaitingForResume != null) { - // Resets the previous workspace icon press state - mWaitingForResume.setStayPressed(false); - } + setOnResumeCallback(null); updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); @@ -2127,11 +2112,7 @@ public class Launcher extends BaseActivity private void startMarketIntentForPackage(View v, String packageName) { ItemInfo item = (ItemInfo) v.getTag(); Intent intent = PackageManagerHelper.getMarketIntent(packageName); - boolean success = startActivitySafely(v, intent, item); - if (success && v instanceof BubbleTextView) { - mWaitingForResume = (BubbleTextView) v; - mWaitingForResume.setStayPressed(true); - } + startActivitySafely(v, intent, item); } /** @@ -2201,13 +2182,8 @@ public class Launcher extends BaseActivity if (intent == null) { throw new IllegalArgumentException("Input must have a valid intent"); } - boolean success = startActivitySafely(v, intent, item); + startActivitySafely(v, intent, item); getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115 - - if (success && v instanceof BubbleTextView) { - mWaitingForResume = (BubbleTextView) v; - mWaitingForResume.setStayPressed(true); - } } /** @@ -2438,6 +2414,16 @@ public class Launcher extends BaseActivity LauncherAppsCompat.getInstance(this).startActivityForProfile( intent.getComponent(), user, intent.getSourceBounds(), optsBundle); } + + if (v instanceof BubbleTextView) { + // 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. + BubbleTextView btv = (BubbleTextView) v; + btv.setStayPressed(true); + setOnResumeCallback(btv); + } return true; } catch (ActivityNotFoundException|SecurityException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); @@ -2831,8 +2817,11 @@ public class Launcher extends BaseActivity } } - public void addOnResumeCallback(Runnable run) { - mOnResumeCallbacks.add(run); + public void setOnResumeCallback(OnResumeCallback callback) { + if (mOnResumeCallback != null) { + mOnResumeCallback.onLauncherResume(); + } + mOnResumeCallback = callback; } /** @@ -3710,4 +3699,12 @@ public class Launcher extends BaseActivity } } } + + /** + * Callback for listening for onResume + */ + public interface OnResumeCallback { + + void onLauncherResume(); + } } diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 3f7de0604b..e8c452877e 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -12,9 +12,13 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.AttributeSet; import android.util.Log; +import android.view.View; import android.widget.Toast; +import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import java.net.URISyntaxException; @@ -91,98 +95,110 @@ public class UninstallDropTarget extends ButtonDropTarget { } @Override - public void onDrop(DragObject d) { - // Differ item deletion - if (d.dragSource instanceof DropTargetSource) { - ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity(); + public void onDrop(DragObject d, DragOptions options) { + // Defer onComplete + if (options.deferCompleteForUninstall) { + d.dragSource = new DeferredOnComplete(d.dragSource, getContext()); } - super.onDrop(d); + super.onDrop(d, options); } @Override public void completeDrop(final DragObject d) { - DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback - ? (DropTargetResultCallback) d.dragSource : null; - startUninstallActivity(mLauncher, d.dragInfo, callback); + ComponentName target = performDropAction(d); + if (d.dragSource instanceof DeferredOnComplete) { + DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource; + if (target != null) { + deferred.mPackageName = target.getPackageName(); + mLauncher.setOnResumeCallback(deferred); + } else { + deferred.sendFailure(); + } + } } - public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { - return startUninstallActivity(launcher, info, null); + /** + * Performs the drop action and returns the target component for the dragObject or null if + * the action was not performed. + */ + protected ComponentName performDropAction(DragObject d) { + return performDropAction(mLauncher, d.dragInfo); } - public static boolean startUninstallActivity( - final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) { - final ComponentName cn = getUninstallTarget(launcher, info); - - boolean canUninstall; + /** + * Performs the drop action and returns the target component for the dragObject or null if + * the action was not performed. + */ + private static ComponentName performDropAction(Context context, ItemInfo info) { + ComponentName cn = getUninstallTarget(context, info); if (cn == null) { // System applications cannot be installed. For now, show a toast explaining that. // We may give them the option of disabling apps this way. - Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); - canUninstall = false; - } else { - try { - Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0) - .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) - .putExtra(Intent.EXTRA_USER, info.user); - launcher.startActivity(i); - canUninstall = true; - } catch (URISyntaxException e) { - Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info); - canUninstall = false; + Toast.makeText(context, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); + return null; + } + try { + Intent i = Intent.parseUri(context.getString(R.string.delete_package_intent), 0) + .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) + .putExtra(Intent.EXTRA_USER, info.user); + context.startActivity(i); + return cn; + } catch (URISyntaxException e) { + Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info); + return null; + } + } + + public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { + return performDropAction(launcher, info) != null; + } + + /** + * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until + * {@link #onLauncherResume} + */ + private class DeferredOnComplete implements DragSource, OnResumeCallback { + + private final DragSource mOriginal; + private final Context mContext; + + private String mPackageName; + private DragObject mDragObject; + + public DeferredOnComplete(DragSource original, Context context) { + mOriginal = original; + mContext = context; + } + + @Override + public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, + boolean success) { + mDragObject = d; + } + + @Override + public void fillInLogContainerData(View v, ItemInfo info, Target target, + Target targetParent) { + mOriginal.fillInLogContainerData(v, info, target, targetParent); + } + + @Override + public void onLauncherResume() { + // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well. + if (LauncherAppsCompat.getInstance(mContext) + .getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, + mDragObject.dragInfo.user) == null) { + mDragObject.dragSource = mOriginal; + mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false, true); + } else { + sendFailure(); } } - if (callback != null) { - sendUninstallResult(launcher, canUninstall, cn, info.user, callback); + + public void sendFailure() { + mDragObject.dragSource = mOriginal; + mDragObject.cancelled = true; + mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false, false); } - return canUninstall; - } - - /** - * Notifies the {@param callback} whether the uninstall was successful or not. - * - * Since there is no direct callback for an uninstall request, we check the package existence - * when the launch resumes next time. This assumes that the uninstall activity will finish only - * after the task is completed - */ - protected static void sendUninstallResult( - final Launcher launcher, boolean activityStarted, - final ComponentName cn, final UserHandle user, - final DropTargetResultCallback callback) { - if (activityStarted) { - final Runnable checkIfUninstallWasSuccess = new Runnable() { - @Override - public void run() { - // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well. - boolean uninstallSuccessful = LauncherAppsCompat.getInstance(launcher) - .getApplicationInfo(cn.getPackageName(), - PackageManager.MATCH_UNINSTALLED_PACKAGES, user) == null; - callback.onDragObjectRemoved(uninstallSuccessful); - } - }; - launcher.addOnResumeCallback(checkIfUninstallWasSuccess); - } else { - callback.onDragObjectRemoved(false); - } - } - - public interface DropTargetResultCallback { - /** - * A drag operation was complete. - * @param isRemoved true if the drag object should be removed, false otherwise. - */ - void onDragObjectRemoved(boolean isRemoved); - } - - /** - * Interface defining an object that can provide uninstallable drag objects. - */ - public interface DropTargetSource extends DropTargetResultCallback { - - /** - * 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 fff075ef10..9794e31bf0 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -55,7 +55,6 @@ import android.widget.Toast; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; -import com.android.launcher3.UninstallDropTarget.DropTargetSource; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.OverviewAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; @@ -99,7 +98,7 @@ import java.util.Set; public class Workspace extends PagedView implements DropTarget, DragSource, View.OnTouchListener, DragController.DragListener, ViewGroup.OnHierarchyChangeListener, - Insettable, DropTargetSource { + Insettable { private static final String TAG = "Launcher.Workspace"; /** The value that {@link #mTransitionProgress} must be greater than for @@ -280,10 +279,6 @@ public class Workspace extends PagedView private float mCurrentScale; private float mTransitionProgress; - @Thunk Runnable mDeferredAction; - private boolean mDeferDropAfterUninstall; - private boolean mUninstallSuccessful; - // State related to Launcher Overlay LauncherOverlay mLauncherOverlay; boolean mScrollInteractionBegan; @@ -466,7 +461,6 @@ public class Workspace extends PagedView InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext()); mOutlineProvider = null; - mDragInfo = null; mDragSourceInternal = null; mLauncher.onInteractionEnd(); } @@ -1752,6 +1746,7 @@ public class Workspace extends PagedView } }); } + options.deferCompleteForUninstall = true; beginDragShared(child, this, options); } @@ -2050,7 +2045,7 @@ public class Workspace extends PagedView @Override public void prepareAccessibilityDrop() { } - public void onDrop(final DragObject d) { + public void onDrop(final DragObject d, DragOptions options) { mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter); CellLayout dropTargetLayout = mDropToLayout; @@ -3093,21 +3088,8 @@ public class Workspace extends PagedView */ public void onDropCompleted(final View target, final DragObject d, final boolean isFlingToDelete, final boolean success) { - if (mDeferDropAfterUninstall) { - final CellLayout.CellInfo dragInfo = mDragInfo; - mDeferredAction = new Runnable() { - public void run() { - mDragInfo = dragInfo; // Restore the drag info that was cleared in onDragEnd() - onDropCompleted(target, d, isFlingToDelete, success); - mDeferredAction = null; - } - }; - return; - } - boolean beingCalledAfterUninstall = mDeferredAction != null; - - if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) { + if (success) { if (target != this && mDragInfo != null) { removeWorkspaceItem(mDragInfo.cell); } @@ -3121,8 +3103,7 @@ public class Workspace extends PagedView + "Workspace#onDropCompleted. Please file a bug. "); } } - if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful)) - && mDragInfo != null && mDragInfo.cell != null) { + if (d.cancelled && mDragInfo != null && mDragInfo.cell != null) { mDragInfo.cell.setVisibility(VISIBLE); } mDragInfo = null; @@ -3169,21 +3150,6 @@ public class Workspace extends PagedView }); } - @Override - public void deferCompleteDropAfterUninstallActivity() { - mDeferDropAfterUninstall = true; - } - - /// maybe move this into a smaller part - @Override - public void onDragObjectRemoved(boolean success) { - mDeferDropAfterUninstall = false; - mUninstallSuccessful = success; - if (mDeferredAction != null) { - mDeferredAction.run(); - } - } - public boolean isDropEnabled() { return true; } diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index c695758b2f..583492e5a3 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -152,7 +152,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host); return true; } else if (action == INFO) { - InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null); + InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null, null); return true; } else if (action == UNINSTALL) { return UninstallDropTarget.startUninstallActivity(mLauncher, item); diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index a7ed87fb6a..db8f903afd 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -575,7 +575,7 @@ public class DragController implements DragDriver.EventListener, TouchController if (flingAnimation != null) { flingAnimation.run(); } else if (!mIsInPreDrag) { - dropTarget.onDrop(mDragObject); + dropTarget.onDrop(mDragObject, mOptions); } accepted = true; } diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java index f108f8b535..550f948309 100644 --- a/src/com/android/launcher3/dragndrop/DragOptions.java +++ b/src/com/android/launcher3/dragndrop/DragOptions.java @@ -37,6 +37,12 @@ public class DragOptions { /** Scale of the icons over the workspace icon size. */ public float intrinsicIconScaleFactor = 1f; + /** + * Whether or not to defer {@link com.android.launcher3.DragSource#onDropCompleted} until + * uninstall result is available when dropped on uninstall drop target. + */ + public boolean deferCompleteForUninstall = false; + /** * Specifies a condition that must be met before DragListener#onDragStart() is called. * By default, there is no condition and onDragStart() is called immediately following diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index e182da140f..8d75db4f97 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -58,7 +58,6 @@ import com.android.launcher3.OnAlarmListener; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; -import com.android.launcher3.UninstallDropTarget.DropTargetSource; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; @@ -84,8 +83,7 @@ import java.util.List; */ public class Folder extends AbstractFloatingView implements DragSource, View.OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener, DragListener, DropTargetSource, - ExtendedEditText.OnBackKeyListener { + View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener { private static final String TAG = "Launcher.Folder"; /** @@ -172,10 +170,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC @ViewDebug.ExportedProperty(category = "launcher") private boolean mDestroyed; - @Thunk Runnable mDeferredAction; - private boolean mDeferDropAfterUninstall; - private boolean mUninstallSuccessful; - // Folder scrolling private int mScrollAreaOffset; @@ -289,6 +283,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } }); } + options.deferCompleteForUninstall = true; mLauncher.getWorkspace().beginDragShared(v, this, options); } @@ -856,22 +851,8 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC public void onDropCompleted(final View target, final DragObject d, final boolean isFlingToDelete, final boolean success) { - if (mDeferDropAfterUninstall) { - Log.d(TAG, "Deferred handling drop because waiting for uninstall."); - mDeferredAction = new Runnable() { - public void run() { - onDropCompleted(target, d, isFlingToDelete, success); - mDeferredAction = null; - } - }; - return; - } - boolean beingCalledAfterUninstall = mDeferredAction != null; - boolean successfulDrop = - success && (!beingCalledAfterUninstall || mUninstallSuccessful); - - if (successfulDrop) { + if (success) { if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) { replaceFolderWithFinalItem(); } @@ -893,7 +874,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC if (target != this) { if (mOnExitAlarm.alarmPending()) { mOnExitAlarm.cancelAlarm(); - if (!successfulDrop) { + if (!success) { mSuppressFolderDeletion = true; } mScrollPauseAlarm.cancelAlarm(); @@ -920,25 +901,11 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC if (!isFlingToDelete) { // Fling to delete already exits spring loaded mode after the animation finishes. - mLauncher.exitSpringLoadedDragModeDelayed(successfulDrop, + mLauncher.exitSpringLoadedDragModeDelayed(success, Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); } } - @Override - public void deferCompleteDropAfterUninstallActivity() { - mDeferDropAfterUninstall = true; - } - - @Override - public void onDragObjectRemoved(boolean success) { - mDeferDropAfterUninstall = false; - mUninstallSuccessful = success; - if (mDeferredAction != null) { - mDeferredAction.run(); - } - } - private void updateItemLocationsInDatabaseBatch() { ArrayList list = getItemsInReadingOrder(); ArrayList items = new ArrayList(); @@ -1190,7 +1157,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } } - public void onDrop(DragObject d) { + public void onDrop(DragObject d, DragOptions options) { Runnable cleanUpRunnable = null; // If we are coming from All Apps space, we defer removing the extra empty screen diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index a342500760..3f7bf42140 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -92,7 +92,7 @@ public abstract class SystemShortcut extends ItemInfo { public void onClick(View view) { Rect sourceBounds = launcher.getViewBounds(view); Bundle opts = launcher.getActivityLaunchOptions(view); - InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null, sourceBounds, opts); + InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts); launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, ControlType.APPINFO_TARGET, view); } From 91498abf7552f15624283366be86f91304ad92a2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Oct 2017 15:57:40 -0700 Subject: [PATCH 045/885] Moving install queue updates to worker thread. This avoids acquiring a lock for upating the sharedPrefs during onResume as all the logic runs on a single thread. Bug: 67305604 Change-Id: I1bbea382da9fafb403b4e9508f393f78db28478d --- .../launcher3/InstallShortcutReceiver.java | 185 ++++++++---------- src/com/android/launcher3/LauncherModel.java | 5 +- .../model/AddWorkspaceItemsTask.java | 14 +- .../model/AddWorkspaceItemsTaskTest.java | 2 +- 4 files changed, 95 insertions(+), 111 deletions(-) diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 0370777d17..df1eec6610 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -27,7 +27,9 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.os.Parcelable; import android.os.Process; import android.os.UserHandle; @@ -61,6 +63,9 @@ import java.util.Set; public class InstallShortcutReceiver extends BroadcastReceiver { + private static final int MSG_ADD_TO_QUEUE = 1; + private static final int MSG_FLUSH_QUEUE = 2; + public static final int FLAG_ACTIVITY_PAUSED = 1; public static final int FLAG_LOADER_RUNNING = 2; public static final int FLAG_DRAG_AND_DROP = 4; @@ -93,73 +98,98 @@ public class InstallShortcutReceiver extends BroadcastReceiver { public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; - private static final Object sLock = new Object(); + private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) { - private static void addToInstallQueue( - SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) { - synchronized(sLock) { - String encoded = info.encodeToString(); - if (encoded != null) { - Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); - strings = (strings != null) ? new HashSet<>(strings) : new HashSet(1); - strings.add(encoded); - sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply(); + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ADD_TO_QUEUE: { + Pair pair = + (Pair) msg.obj; + String encoded = pair.second.encodeToString(); + SharedPreferences prefs = Utilities.getPrefs(pair.first); + Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); + strings = (strings != null) ? new HashSet<>(strings) : new HashSet(1); + strings.add(encoded); + prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply(); + return; + } + case MSG_FLUSH_QUEUE: { + Context context = (Context) msg.obj; + LauncherModel model = LauncherAppState.getInstance(context).getModel(); + if (model.getCallback() == null) { + // Launcher not loaded + return; + } + + ArrayList> installQueue = new ArrayList<>(); + SharedPreferences prefs = Utilities.getPrefs(context); + Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); + if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); + if (strings == null) { + return; + } + + LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + for (String encoded : strings) { + PendingInstallShortcutInfo info = decode(encoded, context); + if (info == null) { + continue; + } + + String pkg = getIntentPackage(info.launchIntent); + if (!TextUtils.isEmpty(pkg) + && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) { + if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: " + + info.launchIntent); + continue; + } + + // Generate a shortcut info to add into the model + installQueue.add(info.getItemInfo()); + } + prefs.edit().remove(APPS_PENDING_INSTALL).apply(); + if (!installQueue.isEmpty()) { + model.addAndBindAddedWorkspaceItems(installQueue); + } + return; + } } } - } + }; public static void removeFromInstallQueue(Context context, HashSet packageNames, UserHandle user) { if (packageNames.isEmpty()) { return; } + Preconditions.assertWorkerThread(); + SharedPreferences sp = Utilities.getPrefs(context); - synchronized(sLock) { - Set strings = sp.getStringSet(APPS_PENDING_INSTALL, null); - if (DBG) { - Log.d(TAG, "APPS_PENDING_INSTALL: " + strings - + ", removing packages: " + packageNames); - } - if (Utilities.isEmpty(strings)) { - return; - } - Set newStrings = new HashSet<>(strings); - Iterator newStringsIter = newStrings.iterator(); - while (newStringsIter.hasNext()) { - String encoded = newStringsIter.next(); - try { - Decoder decoder = new Decoder(encoded, context); - if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) && - user.equals(decoder.user)) { - newStringsIter.remove(); - } - } catch (JSONException | URISyntaxException e) { - Log.d(TAG, "Exception reading shortcut to add: " + e); + Set strings = sp.getStringSet(APPS_PENDING_INSTALL, null); + if (DBG) { + Log.d(TAG, "APPS_PENDING_INSTALL: " + strings + + ", removing packages: " + packageNames); + } + if (Utilities.isEmpty(strings)) { + return; + } + Set newStrings = new HashSet<>(strings); + Iterator newStringsIter = newStrings.iterator(); + while (newStringsIter.hasNext()) { + String encoded = newStringsIter.next(); + try { + Decoder decoder = new Decoder(encoded, context); + if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) && + user.equals(decoder.user)) { newStringsIter.remove(); } + } catch (JSONException | URISyntaxException e) { + Log.d(TAG, "Exception reading shortcut to add: " + e); + newStringsIter.remove(); } - sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply(); - } - } - - private static ArrayList getAndClearInstallQueue(Context context) { - SharedPreferences sharedPrefs = Utilities.getPrefs(context); - synchronized(sLock) { - ArrayList infos = new ArrayList<>(); - Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); - if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); - if (strings == null) { - return infos; - } - for (String encoded : strings) { - PendingInstallShortcutInfo info = decode(encoded, context); - if (info != null) { - infos.add(info); - } - } - sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet()).apply(); - return infos; } + sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply(); } public void onReceive(Context context, Intent data) { @@ -256,7 +286,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) { // Queue the item up for adding if launcher has not loaded properly yet - addToInstallQueue(Utilities.getPrefs(context), info); + Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget(); flushInstallQueue(context); } @@ -269,17 +299,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } static void flushInstallQueue(Context context) { - LauncherModel model = LauncherAppState.getInstance(context).getModel(); - boolean launcherNotLoaded = model.getCallback() == null; - if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) { + if (sInstallQueueDisabledFlags != 0) { return; } - - ArrayList items = getAndClearInstallQueue(context); - if (!items.isEmpty()) { - model.addAndBindAddedWorkspaceItems( - new LazyShortcutsProvider(context.getApplicationContext(), items)); - } + Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget(); } /** @@ -601,42 +624,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver { return new PendingInstallShortcutInfo(info, original.mContext); } - private static class LazyShortcutsProvider extends Provider>> { - - private final Context mContext; - private final ArrayList mPendingItems; - - public LazyShortcutsProvider(Context context, ArrayList items) { - mContext = context; - mPendingItems = items; - } - - /** - * This must be called on the background thread as this requires multiple calls to - * packageManager and icon cache. - */ - @Override - public ArrayList> get() { - Preconditions.assertNonUiThread(); - ArrayList> installQueue = new ArrayList<>(); - LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext); - for (PendingInstallShortcutInfo pendingInfo : mPendingItems) { - // If the intent specifies a package, make sure the package exists - String packageName = getIntentPackage(pendingInfo.launchIntent); - if (!TextUtils.isEmpty(packageName) && !launcherApps.isPackageEnabledForProfile( - packageName, pendingInfo.user)) { - if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: " - + pendingInfo.launchIntent); - continue; - } - - // Generate a shortcut info to add into the model - installQueue.add(pendingInfo.getItemInfo()); - } - return installQueue; - } - } - private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 74a5bac995..3e2236682c 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -192,9 +192,8 @@ public class LauncherModel extends BroadcastReceiver /** * Adds the provided items to the workspace. */ - public void addAndBindAddedWorkspaceItems( - Provider>> appsProvider) { - enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider)); + public void addAndBindAddedWorkspaceItems(List> itemList) { + enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList)); } public ModelWriter getWriter(boolean hasVerticalHotseat) { diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 42926fa3ea..a33a039c0e 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -38,7 +38,6 @@ import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo; -import com.android.launcher3.util.Provider; import java.util.ArrayList; import java.util.List; @@ -47,19 +46,18 @@ import java.util.List; */ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { - private final Provider>> mAppsProvider; + private final List> mItemList; /** - * @param appsProvider items to add on the workspace + * @param itemList items to add on the workspace */ - public AddWorkspaceItemsTask(Provider>> appsProvider) { - mAppsProvider = appsProvider; + public AddWorkspaceItemsTask(List> itemList) { + mItemList = itemList; } @Override public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { - List> workspaceApps = mAppsProvider.get(); - if (workspaceApps.isEmpty()) { + if (mItemList.isEmpty()) { return; } Context context = app.getContext(); @@ -75,7 +73,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { synchronized(dataModel) { List filteredItems = new ArrayList<>(); - for (Pair entry : workspaceApps) { + for (Pair entry : mItemList) { ItemInfo item = entry.first; if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java index 82f34e43de..a486cebc25 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java @@ -55,7 +55,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { for (ItemInfo item : items) { list.add(Pair.create(item, null)); } - return new AddWorkspaceItemsTask(Provider.of(list)) { + return new AddWorkspaceItemsTask(list) { @Override protected void updateScreens(Context context, ArrayList workspaceScreens) { } From 00c95b84472a8af8153b389805b7cedf5a5be28a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 3 Oct 2017 10:29:23 -0700 Subject: [PATCH 046/885] Removing some obsolete callbacks Change-Id: I3cf19a13ff145923f2973e23c24033f196324aa3 --- src/com/android/launcher3/Launcher.java | 87 +------------------ .../android/launcher3/LauncherCallbacks.java | 9 -- .../LauncherStateTransitionAnimation.java | 4 +- src/com/android/launcher3/Workspace.java | 2 - .../launcher3/testing/LauncherExtension.java | 29 ------- 5 files changed, 3 insertions(+), 128 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d8cb06ce22..2945b22943 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -474,13 +474,6 @@ public class Launcher extends BaseActivity private LauncherCallbacks mLauncherCallbacks; - public void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onPostCreate(savedInstanceState); - } - } - public void onInsetsChanged(Rect insets) { mDeviceProfile.updateInsets(insets); mDeviceProfile.layout(this, true /* notifyListeners */); @@ -845,9 +838,6 @@ public class Launcher extends BaseActivity @Override protected void onResume() { TraceHelper.beginSection("ON_RESUME"); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.preOnResume(); - } super.onResume(); TraceHelper.partitionSection("ON_RESUME", "superCall"); @@ -868,9 +858,6 @@ public class Launcher extends BaseActivity } setOnResumeCallback(null); - - updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); - // Process any items that were added while Launcher was away. InstallShortcutReceiver.disableAndFlushInstallQueue( InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); @@ -1500,11 +1487,7 @@ public class Launcher extends BaseActivity // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage // animation. if (isActionMain) { - boolean callbackAllowsMoveToDefaultScreen = - mLauncherCallbacks == null || mLauncherCallbacks - .shouldMoveToDefaultScreenOnHomeIntent(); - if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive() - && callbackAllowsMoveToDefaultScreen) { + if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) { mWorkspace.post(new Runnable() { @Override @@ -1711,25 +1694,11 @@ public class Launcher extends BaseActivity } private void setWorkspaceLoading(boolean value) { - boolean isLocked = isWorkspaceLocked(); mWorkspaceLoading = value; - if (isLocked != isWorkspaceLocked()) { - onWorkspaceLockedChanged(); - } } public void setWaitingForResult(PendingRequestArgs args) { - boolean isLocked = isWorkspaceLocked(); mPendingRequestArgs = args; - if (isLocked != isWorkspaceLocked()) { - onWorkspaceLockedChanged(); - } - } - - protected void onWorkspaceLockedChanged() { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onWorkspaceLockedChanged(); - } } void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, @@ -2245,44 +2214,6 @@ public class Launcher extends BaseActivity mDragLayer.onAccessibilityStateChanged(enabled); } - /** - * Called when the user stops interacting with the launcher. - * This implies that the user is now on the homescreen and is not doing housekeeping. - */ - protected void onInteractionEnd() { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onInteractionEnd(); - } - } - - /** - * Called when the user starts interacting with the launcher. - * The possible interactions are: - * - open all apps - * - reorder an app shortcut, or a widget - * - open the overview mode. - * This is a good time to stop doing things that only make sense - * when the user is on the homescreen and not doing housekeeping. - */ - protected void onInteractionBegin() { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onInteractionBegin(); - } - } - - /** 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 with an - // overlay - boolean fromStateWithOverlay = fromState != Workspace.State.NORMAL; - boolean toStateWithOverlay = toState != Workspace.State.NORMAL; - if (toStateWithOverlay) { - onInteractionBegin(); - } else if (fromStateWithOverlay) { - onInteractionEnd(); - } - } - private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { try { StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); @@ -2683,18 +2614,6 @@ public class Launcher extends BaseActivity return true; } - /** - * Updates the workspace and interaction state on state change, and return the animation to this - * new state. - */ - public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, - boolean animated, AnimationLayerSet layerViews) { - Workspace.State fromState = mWorkspace.getState(); - Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews); - updateInteraction(fromState, toState); - return anim; - } - public void enterSpringLoadedDragMode() { if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name())); if (isStateSpringLoaded()) { @@ -3273,10 +3192,6 @@ public class Launcher extends BaseActivity InstallShortcutReceiver.FLAG_LOADER_RUNNING, this); NotificationListener.setNotificationsChangedListener(mPopupDataProvider); - - if (mLauncherCallbacks != null) { - mLauncherCallbacks.finishBindingItems(false); - } TraceHelper.endSection("finishBindingItems"); } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 2c9a23fa98..78d753ac86 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -39,14 +39,12 @@ public interface LauncherCallbacks { */ void preOnCreate(); void onCreate(Bundle savedInstanceState); - void preOnResume(); void onResume(); void onStart(); void onStop(); void onPause(); void onDestroy(); void onSaveInstanceState(Bundle outState); - void onPostCreate(Bundle savedInstanceState); void onNewIntent(Intent intent); void onActivityResult(int requestCode, int resultCode, Intent data); void onRequestPermissionsResult(int requestCode, String[] permissions, @@ -64,13 +62,7 @@ public interface LauncherCallbacks { * Extension points for providing custom behavior on certain user interactions. */ void onLauncherProviderChange(); - void finishBindingItems(final boolean upgradePath); void bindAllApplications(ArrayList apps); - void onInteractionBegin(); - void onInteractionEnd(); - - @Deprecated - void onWorkspaceLockedChanged(); /** * Starts a search with {@param initialQuery}. Return false if search was not started. @@ -81,6 +73,5 @@ public interface LauncherCallbacks { /* * Extensions points for adding / replacing some other aspects of the Launcher experience. */ - boolean shouldMoveToDefaultScreenOnHomeIntent(); boolean hasSettings(); } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index e2474900da..be0ed0cef4 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -361,8 +361,8 @@ public class LauncherStateTransitionAnimation { AnimationLayerSet layerViews) { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, - animated, layerViews); + Animator workspaceAnim = mLauncher.getWorkspace(). + setStateWithAnimation(toWorkspaceState, animated, layerViews); if (animated && initialized) { // Play the workspace animation diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 0ae47224ee..1f26965044 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -402,7 +402,6 @@ public class Workspace extends PagedView updateChildrenLayersEnabled(); mLauncher.lockScreenOrientation(); - mLauncher.onInteractionBegin(); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); @@ -462,7 +461,6 @@ public class Workspace extends PagedView mOutlineProvider = null; mDragSourceInternal = null; - mLauncher.onInteractionEnd(); } /** diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index a1a4d75557..c40e1fbe4f 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -35,10 +35,6 @@ public class LauncherExtension extends Launcher { public void onCreate(Bundle savedInstanceState) { } - @Override - public void preOnResume() { - } - @Override public void onResume() { } @@ -63,10 +59,6 @@ public class LauncherExtension extends Launcher { public void onSaveInstanceState(Bundle outState) { } - @Override - public void onPostCreate(Bundle savedInstanceState) { - } - @Override public void onNewIntent(Intent intent) { } @@ -110,37 +102,16 @@ public class LauncherExtension extends Launcher { public void onLauncherProviderChange() { } - @Override - public void finishBindingItems(boolean upgradePath) { - } - @Override public void bindAllApplications(ArrayList apps) { } - @Override - public void onWorkspaceLockedChanged() { - } - - @Override - public void onInteractionBegin() { - } - - @Override - public void onInteractionEnd() { - } - @Override public boolean startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData) { return false; } - @Override - public boolean shouldMoveToDefaultScreenOnHomeIntent() { - return true; - } - @Override public boolean hasSettings() { return false; From 207f7d7f86ec858969c25c1829965311a7eea58a Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 6 Oct 2017 13:46:55 -0700 Subject: [PATCH 047/885] Handle null small icon It shouldn't be possible to have a null small icon, but apparently there are cases where we get one anyway. Might as well handle it gracefully instead of crashing. Bug: 67156108 Change-Id: I01ad0251920f7f531a3019eb694946c3d295f9de --- src/com/android/launcher3/notification/NotificationInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java index 1b7c87b229..f54a50d459 100644 --- a/src/com/android/launcher3/notification/NotificationInfo.java +++ b/src/com/android/launcher3/notification/NotificationInfo.java @@ -73,7 +73,7 @@ public class NotificationInfo implements View.OnClickListener { if (icon == null) { // Use the small icon. icon = notification.getSmallIcon(); - mIconDrawable = icon.loadDrawable(context); + mIconDrawable = icon == null ? null : icon.loadDrawable(context); mIconColor = statusBarNotification.getNotification().color; mIsIconLarge = false; } else { From 10a1bd0e652ec7ea3e3ee861fc0d72261a33a3fd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 9 Oct 2017 14:56:21 -0700 Subject: [PATCH 048/885] Converting PopupContainerWithArrow into a base class so that it is easier to create other types of popup Bug: 67585158 Change-Id: I966ae7bb90f941951b26feaf71b3ea30c3f3c0cc --- .../launcher3/AbstractFloatingView.java | 6 +- src/com/android/launcher3/Launcher.java | 10 +- .../keyboard/CustomActionsPopup.java | 4 +- .../NotificationFooterLayout.java | 10 +- .../notification/NotificationInfo.java | 3 +- .../launcher3/popup/BaseActionPopup.java | 599 ++++++++++++++++++ .../popup/PopupContainerWithArrow.java | 564 +++-------------- .../launcher3/popup/PopupDataProvider.java | 15 +- .../launcher3/popup/PopupItemView.java | 2 +- 9 files changed, 701 insertions(+), 512 deletions(-) create mode 100644 src/com/android/launcher3/popup/BaseActionPopup.java diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 49968189bb..0fbad522ec 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -38,14 +38,14 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch @IntDef(flag = true, value = { TYPE_FOLDER, - TYPE_POPUP_CONTAINER_WITH_ARROW, + TYPE_ACTION_POPUP, TYPE_WIDGETS_BOTTOM_SHEET, TYPE_WIDGET_RESIZE_FRAME }) @Retention(RetentionPolicy.SOURCE) public @interface FloatingViewType {} public static final int TYPE_FOLDER = 1 << 0; - public static final int TYPE_POPUP_CONTAINER_WITH_ARROW = 1 << 1; + public static final int TYPE_ACTION_POPUP = 1 << 1; public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2; public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3; @@ -138,7 +138,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } public static AbstractFloatingView getTopOpenView(Launcher launcher) { - return getOpenView(launcher, TYPE_FOLDER | TYPE_POPUP_CONTAINER_WITH_ARROW + return getOpenView(launcher, TYPE_FOLDER | TYPE_ACTION_POPUP | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME); } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2945b22943..1bb4807b58 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -87,7 +87,6 @@ import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; -import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.LauncherAppsCompatVO; @@ -110,6 +109,7 @@ import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pageindicators.PageIndicator; +import com.android.launcher3.popup.BaseActionPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.shortcuts.DeepShortcutManager; @@ -1347,9 +1347,9 @@ public class Launcher extends BaseActivity mWorkspace.updateIconBadges(updatedBadges); mAppsView.updateIconBadges(updatedBadges); - PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this); - if (popup != null) { - popup.updateNotificationHeader(updatedBadges); + BaseActionPopup popup = BaseActionPopup.getOpen(Launcher.this); + if (popup instanceof PopupContainerWithArrow) { + ((PopupContainerWithArrow) popup).updateNotificationHeader(updatedBadges); } } }; @@ -3558,7 +3558,7 @@ public class Launcher extends BaseActivity && mAccessibilityDelegate.performAction(focusedView, (ItemInfo) focusedView.getTag(), LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) { - PopupContainerWithArrow.getOpen(this).requestFocus(); + BaseActionPopup.getOpen(this).requestFocus(); return true; } break; diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java index 938955ca2c..150522ef6f 100644 --- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java +++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java @@ -27,7 +27,7 @@ import android.widget.PopupMenu.OnMenuItemClickListener; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; -import com.android.launcher3.popup.PopupContainerWithArrow; +import com.android.launcher3.popup.BaseActionPopup; import java.util.ArrayList; import java.util.Collections; @@ -46,7 +46,7 @@ public class CustomActionsPopup implements OnMenuItemClickListener { public CustomActionsPopup(Launcher launcher, View icon) { mLauncher = launcher; mIcon = icon; - PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher); + BaseActionPopup container = BaseActionPopup.getOpen(launcher); if (container != null) { mDelegate = container.getAccessibilityDelegate(); } else { diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java index ad07d37bd5..3cf3ff62cf 100644 --- a/src/com/android/launcher3/notification/NotificationFooterLayout.java +++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java @@ -37,6 +37,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.anim.PropertyResetListener; +import com.android.launcher3.popup.BaseActionPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import java.util.ArrayList; @@ -193,16 +194,17 @@ public class NotificationFooterLayout extends FrameLayout { private void removeViewFromIconRow(View child) { mIconRow.removeView(child); - mNotifications.remove((NotificationInfo) child.getTag()); + mNotifications.remove(child.getTag()); updateOverflowEllipsisVisibility(); if (mIconRow.getChildCount() == 0) { // There are no more icons in the footer, so hide it. - PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen( + BaseActionPopup popup = BaseActionPopup.getOpen( Launcher.getLauncher(getContext())); - if (popup != null) { + if (popup instanceof PopupContainerWithArrow) { final int newHeight = getResources().getDimensionPixelSize( R.dimen.notification_empty_footer_height); - Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight, + Animator collapseFooter = ((PopupContainerWithArrow) popup) + .reduceNotificationViewHeight(getHeight() - newHeight, getResources().getInteger(R.integer.config_removeNotificationViewDuration)); collapseFooter.addListener(new AnimatorListenerAdapter() { @Override diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java index 6e36f4f51b..8ef10e356b 100644 --- a/src/com/android/launcher3/notification/NotificationInfo.java +++ b/src/com/android/launcher3/notification/NotificationInfo.java @@ -31,7 +31,6 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.graphics.IconPalette; -import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.util.PackageUserKey; /** @@ -110,7 +109,7 @@ public class NotificationInfo implements View.OnClickListener { launcher.getPopupDataProvider().cancelNotification(notificationKey); } AbstractFloatingView.closeOpenContainer(launcher, AbstractFloatingView - .TYPE_POPUP_CONTAINER_WITH_ARROW); + .TYPE_ACTION_POPUP); } public Drawable getIconForBackground(Context context, int background) { diff --git a/src/com/android/launcher3/popup/BaseActionPopup.java b/src/com/android/launcher3/popup/BaseActionPopup.java new file mode 100644 index 0000000000..7ffe2ef8bc --- /dev/null +++ b/src/com/android/launcher3/popup/BaseActionPopup.java @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.popup; + +import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS; + +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.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.CornerPathEffect; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.drawable.ShapeDrawable; +import android.os.Build; +import android.support.annotation.IntDef; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.accessibility.AccessibilityEvent; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.TextView; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; +import com.android.launcher3.anim.PropertyListBuilder; +import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.graphics.TriangleShape; +import com.android.launcher3.logging.LoggerUtils; +import com.android.launcher3.notification.NotificationItemView; +import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.shortcuts.ShortcutsItemView; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.Themes; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Set; + +/** + * Base popup container for showing shortcuts to deep links within apps. + */ +@TargetApi(Build.VERSION_CODES.N) +public class BaseActionPopup extends AbstractFloatingView { + + public static final int ROUNDED_TOP_CORNERS = 1 << 0; + public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1; + + @IntDef(flag = true, value = { + ROUNDED_TOP_CORNERS, + ROUNDED_BOTTOM_CORNERS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RoundedCornerFlags {} + + protected final Launcher mLauncher; + protected final LauncherAccessibilityDelegate mAccessibilityDelegate; + private final boolean mIsRtl; + + public ShortcutsItemView mShortcutsItemView; + + protected V mOriginalIcon; + private final Rect mTempRect = new Rect(); + private PointF mInterceptTouchDown = new PointF(); + private boolean mIsLeftAligned; + protected boolean mIsAboveIcon; + protected View mArrow; + private int mGravity; + + protected Animator mOpenCloseAnimator; + protected boolean mDeferContainerRemoval; + private final Rect mStartRect = new Rect(); + private final Rect mEndRect = new Rect(); + + public BaseActionPopup(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = Launcher.getLauncher(context); + + mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher); + mIsRtl = Utilities.isRtl(getResources()); + } + + public BaseActionPopup(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BaseActionPopup(Context context) { + this(context, null, 0); + } + + public LauncherAccessibilityDelegate getAccessibilityDelegate() { + return mAccessibilityDelegate; + } + + protected PopupItemView getItemViewAt(int index) { + if (!mIsAboveIcon) { + // Opening down, so arrow is the first view. + index++; + } + return (PopupItemView) getChildAt(index); + } + + protected int getItemCount() { + // All children except the arrow are items. + return getChildCount() - 1; + } + + protected void animateOpen() { + setVisibility(View.VISIBLE); + mIsOpen = true; + + final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet(); + final Resources res = getResources(); + final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration); + final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator(); + + // Rectangular reveal. + int itemsTotalHeight = 0; + for (int i = 0; i < getItemCount(); i++) { + itemsTotalHeight += getItemViewAt(i).getMeasuredHeight(); + } + Point startPoint = computeAnimStartPoint(itemsTotalHeight); + int top = mIsAboveIcon ? getPaddingTop() : startPoint.y; + float radius = getItemViewAt(0).getBackgroundRadius(); + mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y); + mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight); + final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider + (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false); + revealAnim.setDuration(revealDuration); + revealAnim.setInterpolator(revealInterpolator); + + Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1); + fadeIn.setDuration(revealDuration); + fadeIn.setInterpolator(revealInterpolator); + openAnim.play(fadeIn); + + // Animate the arrow. + mArrow.setScaleX(0); + mArrow.setScaleY(0); + Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger( + R.integer.config_popupArrowOpenDuration)); + + openAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mOpenCloseAnimator = null; + Utilities.sendCustomAccessibilityEvent( + BaseActionPopup.this, + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, + getContext().getString(R.string.action_deep_shortcut)); + } + }); + + mOpenCloseAnimator = openAnim; + openAnim.playSequentially(revealAnim, arrowScale); + openAnim.start(); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + enforceContainedWithinScreen(l, r); + } + + private void enforceContainedWithinScreen(int left, int right) { + DragLayer dragLayer = mLauncher.getDragLayer(); + if (getTranslationX() + left < 0 || + getTranslationX() + right > dragLayer.getWidth()) { + // If we are still off screen, center horizontally too. + mGravity |= Gravity.CENTER_HORIZONTAL; + } + + if (Gravity.isHorizontal(mGravity)) { + setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2); + } + if (Gravity.isVertical(mGravity)) { + setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2); + } + } + + /** + * Returns the point at which the center of the arrow merges with the first popup item. + */ + private Point computeAnimStartPoint(int itemsTotalHeight) { + int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ? + R.dimen.popup_arrow_horizontal_center_start: + R.dimen.popup_arrow_horizontal_center_end); + if (!mIsLeftAligned) { + arrowCenterX = getMeasuredWidth() - arrowCenterX; + } + int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() + - itemsTotalHeight; + // The y-coordinate of edge between the arrow and the first popup item. + int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight); + return new Point(arrowCenterX, arrowEdge); + } + + /** + * Orients this container above or below the given icon, aligning with the left or right. + * + * These are the preferred orientations, in order (RTL prefers right-aligned over left): + * - Above and left-aligned + * - Above and right-aligned + * - Below and left-aligned + * - Below and right-aligned + * + * So we always align left if there is enough horizontal space + * and align above if there is enough vertical space. + */ + protected void orientAboutIcon(int arrowHeight) { + int width = getMeasuredWidth(); + int height = getMeasuredHeight() + arrowHeight; + + DragLayer dragLayer = mLauncher.getDragLayer(); + dragLayer.getDescendantRectRelativeToSelf(mOriginalIcon, mTempRect); + Rect insets = dragLayer.getInsets(); + + // Align left (right in RTL) if there is room. + int leftAlignedX = mTempRect.left + mOriginalIcon.getPaddingLeft(); + int rightAlignedX = mTempRect.right - width - mOriginalIcon.getPaddingRight(); + int x = leftAlignedX; + boolean canBeLeftAligned = leftAlignedX + width + insets.left + < dragLayer.getRight() - insets.right; + boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left; + if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) { + x = rightAlignedX; + } + mIsLeftAligned = x == leftAlignedX; + if (mIsRtl) { + x -= dragLayer.getWidth() - width; + } + + // Offset x so that the arrow and shortcut icons are center-aligned with the original icon. + int iconWidth = mOriginalIcon.getWidth() + - mOriginalIcon.getTotalPaddingLeft() - mOriginalIcon.getTotalPaddingRight(); + iconWidth *= mOriginalIcon.getScaleX(); + Resources resources = getResources(); + int xOffset; + if (isAlignedWithStart()) { + // Aligning with the shortcut icon. + int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size); + int shortcutPaddingStart = resources.getDimensionPixelSize( + R.dimen.popup_padding_start); + xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart; + } else { + // Aligning with the drag handle. + int shortcutDragHandleWidth = resources.getDimensionPixelSize( + R.dimen.deep_shortcut_drag_handle_size); + int shortcutPaddingEnd = resources.getDimensionPixelSize( + R.dimen.popup_padding_end); + xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd; + } + x += mIsLeftAligned ? xOffset : -xOffset; + + // Open above icon if there is room. + int iconHeight = getIconHeightForPopupPlacement(); + int y = mTempRect.top + mOriginalIcon.getPaddingTop() - height; + mIsAboveIcon = y > dragLayer.getTop() + insets.top; + if (!mIsAboveIcon) { + y = mTempRect.top + mOriginalIcon.getPaddingTop() + iconHeight; + } + + // Insets are added later, so subtract them now. + if (mIsRtl) { + x += insets.right; + } else { + x -= insets.left; + } + y -= insets.top; + + mGravity = 0; + if (y + height > dragLayer.getBottom() - insets.bottom) { + // The container is opening off the screen, so just center it in the drag layer instead. + mGravity = Gravity.CENTER_VERTICAL; + // Put the container next to the icon, preferring the right side in ltr (left in rtl). + int rightSide = leftAlignedX + iconWidth - insets.left; + int leftSide = rightAlignedX - iconWidth - insets.left; + if (!mIsRtl) { + if (rightSide + width < dragLayer.getRight()) { + x = rightSide; + mIsLeftAligned = true; + } else { + x = leftSide; + mIsLeftAligned = false; + } + } else { + if (leftSide > dragLayer.getLeft()) { + x = leftSide; + mIsLeftAligned = false; + } else { + x = rightSide; + mIsLeftAligned = true; + } + } + mIsAboveIcon = true; + } + + setX(x); + setY(y); + } + + protected int getIconHeightForPopupPlacement() { + return mOriginalIcon.getHeight(); + } + + protected boolean isAlignedWithStart() { + return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl; + } + + /** + * Adds an arrow view pointing at the original icon. + * @param horizontalOffset the horizontal offset of the arrow, so that it + * points at the center of the original icon + */ + protected View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) { + LayoutParams layoutParams = new LayoutParams(width, height); + if (mIsLeftAligned) { + layoutParams.gravity = Gravity.LEFT; + layoutParams.leftMargin = horizontalOffset; + } else { + layoutParams.gravity = Gravity.RIGHT; + layoutParams.rightMargin = horizontalOffset; + } + if (mIsAboveIcon) { + layoutParams.topMargin = verticalOffset; + } else { + layoutParams.bottomMargin = verticalOffset; + } + + View arrowView = new View(getContext()); + if (Gravity.isVertical(mGravity)) { + // This is only true if there wasn't room for the container next to the icon, + // so we centered it instead. In that case we don't want to show the arrow. + arrowView.setVisibility(INVISIBLE); + } else { + ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create( + width, height, !mIsAboveIcon)); + Paint arrowPaint = arrowDrawable.getPaint(); + arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary)); + // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable. + int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius); + arrowPaint.setPathEffect(new CornerPathEffect(radius)); + arrowView.setBackground(arrowDrawable); + arrowView.setElevation(getElevation()); + } + addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams); + return arrowView; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mInterceptTouchDown.set(ev.getX(), ev.getY()); + return false; + } + // Stop sending touch events to deep shortcut views if user moved beyond touch slop. + return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY()) + > ViewConfiguration.get(getContext()).getScaledTouchSlop(); + } + + protected ObjectAnimator createArrowScaleAnim(float scale) { + return LauncherAnimUtils.ofPropertyValuesHolder( + mArrow, new PropertyListBuilder().scale(scale).build()); + } + + @Override + protected void handleClose(boolean animate) { + if (animate) { + animateClose(); + } else { + closeComplete(); + } + } + + protected void animateClose() { + if (!mIsOpen) { + return; + } + mEndRect.setEmpty(); + if (mOpenCloseAnimator != null) { + Outline outline = new Outline(); + getOutlineProvider().getOutline(this, outline); + outline.getRect(mEndRect); + mOpenCloseAnimator.cancel(); + } + mIsOpen = false; + + final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet(); + prepareCloseAnimator(closeAnim); + + closeAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mOpenCloseAnimator = null; + if (mDeferContainerRemoval) { + setVisibility(INVISIBLE); + } else { + closeComplete(); + } + } + }); + mOpenCloseAnimator = closeAnim; + closeAnim.start(); + } + + protected void prepareCloseAnimator(AnimatorSet closeAnim) { + final Resources res = getResources(); + final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator(); + + // Rectangular reveal (reversed). + int itemsTotalHeight = 0; + for (int i = 0; i < getItemCount(); i++) { + itemsTotalHeight += getItemViewAt(i).getMeasuredHeight(); + } + Point startPoint = computeAnimStartPoint(itemsTotalHeight); + int top = mIsAboveIcon ? getPaddingTop() : startPoint.y; + float radius = getItemViewAt(0).getBackgroundRadius(); + mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y); + if (mEndRect.isEmpty()) { + mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight); + } + final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider( + radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true); + revealAnim.setInterpolator(revealInterpolator); + closeAnim.play(revealAnim); + + Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0); + fadeOut.setInterpolator(revealInterpolator); + closeAnim.play(fadeOut); + closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration)); + } + + /** + * Closes the folder without animation. + */ + protected void closeComplete() { + if (mOpenCloseAnimator != null) { + mOpenCloseAnimator.cancel(); + mOpenCloseAnimator = null; + } + mIsOpen = false; + mDeferContainerRemoval = false; + mLauncher.getDragLayer().removeView(this); + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_ACTION_POPUP) != 0; + } + + /** + * Returns a DeepShortcutsContainer which is already open or null + */ + public static BaseActionPopup getOpen(Launcher launcher) { + return getOpenView(launcher, TYPE_ACTION_POPUP); + } + + @Override + public void logActionCommand(int command) { + mLauncher.getUserEventDispatcher().logActionCommand( + command, mOriginalIcon, ContainerType.DEEPSHORTCUTS); + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + DragLayer dl = mLauncher.getDragLayer(); + if (!dl.isEventOverView(this, ev)) { + mLauncher.getUserEventDispatcher().logActionTapOutside( + LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS)); + close(true); + + // We let touches on the original icon go through so that users can launch + // the app with one tap if they don't find a shortcut they want. + return mOriginalIcon == null || !dl.isEventOverView(mOriginalIcon, ev); + } + } + return false; + } + + public void populateAndShow(V originalIcon, PopupPopulator.Item[] itemsToPopulate) { + setVisibility(View.INVISIBLE); + mLauncher.getDragLayer().addView(this); + + final Resources resources = getResources(); + final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width); + final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height); + final int arrowVerticalOffset = resources.getDimensionPixelSize( + R.dimen.popup_arrow_vertical_offset); + + mOriginalIcon = originalIcon; + + // Add dummy views first, and populate with real info when ready. + addDummyViews(itemsToPopulate); + + measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + orientAboutIcon(arrowHeight + arrowVerticalOffset); + + boolean reverseOrder = mIsAboveIcon; + if (reverseOrder) { + removeAllViews(); + mShortcutsItemView = null; + itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate); + addDummyViews(itemsToPopulate); + + measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + orientAboutIcon(arrowHeight + arrowVerticalOffset); + } + + // Add the arrow. + final int arrowHorizontalOffset = resources.getDimensionPixelSize(isAlignedWithStart() ? + R.dimen.popup_arrow_horizontal_offset_start : + R.dimen.popup_arrow_horizontal_offset_end); + mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight); + mArrow.setPivotX(arrowWidth / 2); + mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight); + + measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + animateOpen(); + } + + protected void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate) { + final LayoutInflater inflater = mLauncher.getLayoutInflater(); + int shortcutsItemRoundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS; + int numItems = itemTypesToPopulate.length; + for (int i = 0; i < numItems; i++) { + PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i]; + PopupPopulator.Item prevItemTypeToPopulate = + i > 0 ? itemTypesToPopulate[i - 1] : null; + PopupPopulator.Item nextItemTypeToPopulate = + i < numItems - 1 ? itemTypesToPopulate[i + 1] : null; + final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false); + + boolean shouldUnroundTopCorners = prevItemTypeToPopulate != null + && itemTypeToPopulate.isShortcut ^ prevItemTypeToPopulate.isShortcut; + boolean shouldUnroundBottomCorners = nextItemTypeToPopulate != null + && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut; + + onViewInflated(item, itemTypeToPopulate, + shouldUnroundTopCorners, shouldUnroundBottomCorners); + + if (itemTypeToPopulate.isShortcut) { + if (mShortcutsItemView == null) { + mShortcutsItemView = (ShortcutsItemView) inflater.inflate( + R.layout.shortcuts_item, this, false); + addView(mShortcutsItemView); + if (shouldUnroundTopCorners) { + shortcutsItemRoundedCorners &= ~ROUNDED_TOP_CORNERS; + } + } + mShortcutsItemView.addShortcutView(item, itemTypeToPopulate); + if (shouldUnroundBottomCorners) { + shortcutsItemRoundedCorners &= ~ROUNDED_BOTTOM_CORNERS; + } + } else { + addView(item); + } + } + int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary); + mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners); + } + + protected void onViewInflated(View view, PopupPopulator.Item itemType, + boolean shouldUnroundTopCorners, boolean shouldUnroundBottomCorners) { + + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 5c49b4bdf3..68b547d88b 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -25,32 +25,17 @@ 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.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; -import android.graphics.CornerPathEffect; -import android.graphics.Outline; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.drawable.ShapeDrawable; import android.os.Build; import android.os.Handler; import android.os.Looper; -import android.support.annotation.IntDef; import android.util.AttributeSet; -import android.view.Gravity; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; -import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AccelerateDecelerateInterpolator; -import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; @@ -59,81 +44,42 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherModel; import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; -import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; -import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.anim.PropertyResetListener; -import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.dragndrop.DragController; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.graphics.IconPalette; -import com.android.launcher3.graphics.TriangleShape; -import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.notification.NotificationItemView; import com.android.launcher3.notification.NotificationKeyData; +import com.android.launcher3.popup.PopupPopulator.Item; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.shortcuts.ShortcutsItemView; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Themes; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** - * A container for shortcuts to deep links within apps. + * A container for shortcuts to deep links and notifications associated with an app. */ @TargetApi(Build.VERSION_CODES.N) -public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource, +public class PopupContainerWithArrow extends BaseActionPopup implements DragSource, DragController.DragListener { - public static final int ROUNDED_TOP_CORNERS = 1 << 0; - public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1; - - @IntDef(flag = true, value = { - ROUNDED_TOP_CORNERS, - ROUNDED_BOTTOM_CORNERS - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RoundedCornerFlags {} - - protected final Launcher mLauncher; private final int mStartDragThreshold; - private LauncherAccessibilityDelegate mAccessibilityDelegate; - private final boolean mIsRtl; - public ShortcutsItemView mShortcutsItemView; private NotificationItemView mNotificationItemView; - - protected BubbleTextView mOriginalIcon; - private final Rect mTempRect = new Rect(); - private PointF mInterceptTouchDown = new PointF(); - private boolean mIsLeftAligned; - protected boolean mIsAboveIcon; - private View mArrow; - private int mGravity; - - protected Animator mOpenCloseAnimator; - private boolean mDeferContainerRemoval; private AnimatorSet mReduceHeightAnimatorSet; - private final Rect mStartRect = new Rect(); - private final Rect mEndRect = new Rect(); + private int mNumNotifications; public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mLauncher = Launcher.getLauncher(context); - mStartDragThreshold = getResources().getDimensionPixelSize( R.dimen.deep_shortcuts_start_drag_threshold); - mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher); - mIsRtl = Utilities.isRtl(getResources()); } public PopupContainerWithArrow(Context context, AttributeSet attrs) { @@ -144,10 +90,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra this(context, null, 0); } - public LauncherAccessibilityDelegate getAccessibilityDelegate() { - return mAccessibilityDelegate; - } - /** * Shows the notifications and deep shortcuts associated with {@param icon}. * @return the container if shown or null. @@ -174,49 +116,24 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra final PopupContainerWithArrow container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate( R.layout.popup_container, launcher.getDragLayer(), false); - container.setVisibility(View.INVISIBLE); - launcher.getDragLayer().addView(container); container.populateAndShow(icon, shortcutIds, notificationKeys, systemShortcuts); return container; } - public void populateAndShow(final BubbleTextView originalIcon, final List shortcutIds, + private void populateAndShow(final BubbleTextView originalIcon, final List shortcutIds, final List notificationKeys, List systemShortcuts) { - final Resources resources = getResources(); - final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width); - final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height); - final int arrowVerticalOffset = resources.getDimensionPixelSize( - R.dimen.popup_arrow_vertical_offset); - - mOriginalIcon = originalIcon; - - // Add dummy views first, and populate with real info when ready. + mNumNotifications = notificationKeys.size(); PopupPopulator.Item[] itemsToPopulate = PopupPopulator .getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts); - addDummyViews(itemsToPopulate, notificationKeys.size()); - - measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset); - - boolean reverseOrder = mIsAboveIcon; - if (reverseOrder) { - removeAllViews(); - mNotificationItemView = null; - mShortcutsItemView = null; - itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate); - addDummyViews(itemsToPopulate, notificationKeys.size()); - - measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset); - } + populateAndShow(originalIcon, itemsToPopulate); ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag(); List shortcutViews = mShortcutsItemView == null ? Collections.EMPTY_LIST - : mShortcutsItemView.getDeepShortcutViews(reverseOrder); + : mShortcutsItemView.getDeepShortcutViews(mIsAboveIcon); List systemShortcutViews = mShortcutsItemView == null ? Collections.EMPTY_LIST - : mShortcutsItemView.getSystemShortcutViews(reverseOrder); + : mShortcutsItemView.getSystemShortcutViews(mIsAboveIcon); if (mNotificationItemView != null) { updateNotificationHeader(); } @@ -232,17 +149,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra numNotifications, originalIcon.getContentDescription().toString())); } - // Add the arrow. - final int arrowHorizontalOffset = resources.getDimensionPixelSize(isAlignedWithStart() ? - R.dimen.popup_arrow_horizontal_offset_start : - R.dimen.popup_arrow_horizontal_offset_end); - mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight); - mArrow.setPivotX(arrowWidth / 2); - mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight); - - measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - animateOpen(); - mLauncher.getDragController().addDragListener(this); mOriginalIcon.forceHideBadge(true); @@ -254,6 +160,60 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra systemShortcuts, systemShortcutViews)); } + @Override + protected void addDummyViews(Item[] itemTypesToPopulate) { + mNotificationItemView = null; + super.addDummyViews(itemTypesToPopulate); + if (mNumNotifications > 0) { + mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS); + } + } + + @Override + protected void onViewInflated(View view, Item itemType, + boolean shouldUnroundTopCorners, boolean shouldUnroundBottomCorners) { + if (itemType == PopupPopulator.Item.NOTIFICATION) { + mNotificationItemView = (NotificationItemView) view; + boolean notificationFooterHasIcons = mNumNotifications > 1; + int footerHeight = getResources().getDimensionPixelSize( + notificationFooterHasIcons ? R.dimen.notification_footer_height + : R.dimen.notification_empty_footer_height); + view.findViewById(R.id.footer).getLayoutParams().height = footerHeight; + if (notificationFooterHasIcons) { + mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE); + } + + int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS; + if (shouldUnroundTopCorners) { + roundedCorners &= ~ROUNDED_TOP_CORNERS; + mNotificationItemView.findViewById(R.id.gutter_top).setVisibility(VISIBLE); + } + if (shouldUnroundBottomCorners) { + roundedCorners &= ~ROUNDED_BOTTOM_CORNERS; + mNotificationItemView.findViewById(R.id.gutter_bottom).setVisibility(VISIBLE); + } + int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary); + mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners); + + mNotificationItemView.getMainView().setAccessibilityDelegate(mAccessibilityDelegate); + } else if (itemType == PopupPopulator.Item.SHORTCUT) { + view.setAccessibilityDelegate(mAccessibilityDelegate); + } + + if (itemType != PopupPopulator.Item.SYSTEM_SHORTCUT_ICON && itemType.isShortcut + && mNumNotifications > 0) { + int prevHeight = view.getLayoutParams().height; + // Condense shortcuts height when there are notifications. + view.getLayoutParams().height = getResources().getDimensionPixelSize( + R.dimen.bg_popup_item_condensed_height); + if (view instanceof DeepShortcutView) { + float iconScale = (float) view.getLayoutParams().height / prevHeight; + ((DeepShortcutView) view).getIconView().setScaleX(iconScale); + ((DeepShortcutView) view).getIconView().setScaleY(iconScale); + } + } + } + private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate, int numNotifications) { final Resources res = getResources(); final LayoutInflater inflater = mLauncher.getLayoutInflater(); @@ -337,261 +297,18 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra } } - protected PopupItemView getItemViewAt(int index) { - if (!mIsAboveIcon) { - // Opening down, so arrow is the first view. - index++; + @Override + protected void onWidgetsBound() { + if (mShortcutsItemView != null) { + mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon); } - return (PopupItemView) getChildAt(index); - } - - protected int getItemCount() { - // All children except the arrow are items. - return getChildCount() - 1; - } - - private void animateOpen() { - setVisibility(View.VISIBLE); - mIsOpen = true; - - final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet(); - final Resources res = getResources(); - final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration); - final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator(); - - // Rectangular reveal. - int itemsTotalHeight = 0; - for (int i = 0; i < getItemCount(); i++) { - itemsTotalHeight += getItemViewAt(i).getMeasuredHeight(); - } - Point startPoint = computeAnimStartPoint(itemsTotalHeight); - int top = mIsAboveIcon ? getPaddingTop() : startPoint.y; - float radius = getItemViewAt(0).getBackgroundRadius(); - mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y); - mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight); - final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider - (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false); - revealAnim.setDuration(revealDuration); - revealAnim.setInterpolator(revealInterpolator); - - Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1); - fadeIn.setDuration(revealDuration); - fadeIn.setInterpolator(revealInterpolator); - openAnim.play(fadeIn); - - // Animate the arrow. - mArrow.setScaleX(0); - mArrow.setScaleY(0); - Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger( - R.integer.config_popupArrowOpenDuration)); - - openAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mOpenCloseAnimator = null; - Utilities.sendCustomAccessibilityEvent( - PopupContainerWithArrow.this, - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - getContext().getString(R.string.action_deep_shortcut)); - } - }); - - mOpenCloseAnimator = openAnim; - openAnim.playSequentially(revealAnim, arrowScale); - openAnim.start(); } @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - enforceContainedWithinScreen(l, r); - - } - - private void enforceContainedWithinScreen(int left, int right) { - DragLayer dragLayer = mLauncher.getDragLayer(); - if (getTranslationX() + left < 0 || - getTranslationX() + right > dragLayer.getWidth()) { - // If we are still off screen, center horizontally too. - mGravity |= Gravity.CENTER_HORIZONTAL; - } - - if (Gravity.isHorizontal(mGravity)) { - setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2); - } - if (Gravity.isVertical(mGravity)) { - setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2); - } - } - - /** - * Returns the point at which the center of the arrow merges with the first popup item. - */ - private Point computeAnimStartPoint(int itemsTotalHeight) { - int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ? - R.dimen.popup_arrow_horizontal_center_start: - R.dimen.popup_arrow_horizontal_center_end); - if (!mIsLeftAligned) { - arrowCenterX = getMeasuredWidth() - arrowCenterX; - } - int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - - itemsTotalHeight; - // The y-coordinate of edge between the arrow and the first popup item. - int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight); - return new Point(arrowCenterX, arrowEdge); - } - - /** - * Orients this container above or below the given icon, aligning with the left or right. - * - * These are the preferred orientations, in order (RTL prefers right-aligned over left): - * - Above and left-aligned - * - Above and right-aligned - * - Below and left-aligned - * - Below and right-aligned - * - * So we always align left if there is enough horizontal space - * and align above if there is enough vertical space. - */ - private void orientAboutIcon(BubbleTextView icon, int arrowHeight) { - int width = getMeasuredWidth(); - int height = getMeasuredHeight() + arrowHeight; - - DragLayer dragLayer = mLauncher.getDragLayer(); - dragLayer.getDescendantRectRelativeToSelf(icon, mTempRect); - Rect insets = dragLayer.getInsets(); - - // Align left (right in RTL) if there is room. - int leftAlignedX = mTempRect.left + icon.getPaddingLeft(); - int rightAlignedX = mTempRect.right - width - icon.getPaddingRight(); - int x = leftAlignedX; - boolean canBeLeftAligned = leftAlignedX + width + insets.left - < dragLayer.getRight() - insets.right; - boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left; - if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) { - x = rightAlignedX; - } - mIsLeftAligned = x == leftAlignedX; - if (mIsRtl) { - x -= dragLayer.getWidth() - width; - } - - // Offset x so that the arrow and shortcut icons are center-aligned with the original icon. - int iconWidth = icon.getWidth() - icon.getTotalPaddingLeft() - icon.getTotalPaddingRight(); - iconWidth *= icon.getScaleX(); - Resources resources = getResources(); - int xOffset; - if (isAlignedWithStart()) { - // Aligning with the shortcut icon. - int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size); - int shortcutPaddingStart = resources.getDimensionPixelSize( - R.dimen.popup_padding_start); - xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart; - } else { - // Aligning with the drag handle. - int shortcutDragHandleWidth = resources.getDimensionPixelSize( - R.dimen.deep_shortcut_drag_handle_size); - int shortcutPaddingEnd = resources.getDimensionPixelSize( - R.dimen.popup_padding_end); - xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd; - } - x += mIsLeftAligned ? xOffset : -xOffset; - - // Open above icon if there is room. - int iconHeight = icon.getIcon() != null - ? icon.getIcon().getBounds().height() - : icon.getHeight(); - int y = mTempRect.top + icon.getPaddingTop() - height; - mIsAboveIcon = y > dragLayer.getTop() + insets.top; - if (!mIsAboveIcon) { - y = mTempRect.top + icon.getPaddingTop() + iconHeight; - } - - // Insets are added later, so subtract them now. - if (mIsRtl) { - x += insets.right; - } else { - x -= insets.left; - } - y -= insets.top; - - mGravity = 0; - if (y + height > dragLayer.getBottom() - insets.bottom) { - // The container is opening off the screen, so just center it in the drag layer instead. - mGravity = Gravity.CENTER_VERTICAL; - // Put the container next to the icon, preferring the right side in ltr (left in rtl). - int rightSide = leftAlignedX + iconWidth - insets.left; - int leftSide = rightAlignedX - iconWidth - insets.left; - if (!mIsRtl) { - if (rightSide + width < dragLayer.getRight()) { - x = rightSide; - mIsLeftAligned = true; - } else { - x = leftSide; - mIsLeftAligned = false; - } - } else { - if (leftSide > dragLayer.getLeft()) { - x = leftSide; - mIsLeftAligned = false; - } else { - x = rightSide; - mIsLeftAligned = true; - } - } - mIsAboveIcon = true; - } - - setX(x); - setY(y); - } - - private boolean isAlignedWithStart() { - return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl; - } - - /** - * Adds an arrow view pointing at the original icon. - * @param horizontalOffset the horizontal offset of the arrow, so that it - * points at the center of the original icon - */ - private View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) { - LayoutParams layoutParams = new LayoutParams(width, height); - if (mIsLeftAligned) { - layoutParams.gravity = Gravity.LEFT; - layoutParams.leftMargin = horizontalOffset; - } else { - layoutParams.gravity = Gravity.RIGHT; - layoutParams.rightMargin = horizontalOffset; - } - if (mIsAboveIcon) { - layoutParams.topMargin = verticalOffset; - } else { - layoutParams.bottomMargin = verticalOffset; - } - - View arrowView = new View(getContext()); - if (Gravity.isVertical(mGravity)) { - // This is only true if there wasn't room for the container next to the icon, - // so we centered it instead. In that case we don't want to show the arrow. - arrowView.setVisibility(INVISIBLE); - } else { - ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create( - width, height, !mIsAboveIcon)); - Paint arrowPaint = arrowDrawable.getPaint(); - // Note that we have to use getChildAt() instead of getItemViewAt(), - // since the latter expects the arrow which hasn't been added yet. - PopupItemView itemAttachedToArrow = (PopupItemView) - (getChildAt(mIsAboveIcon ? getChildCount() - 1 : 0)); - arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary)); - // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable. - int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius); - arrowPaint.setPathEffect(new CornerPathEffect(radius)); - arrowView.setBackground(arrowDrawable); - arrowView.setElevation(getElevation()); - } - addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams); - return arrowView; + protected int getIconHeightForPopupPlacement() { + return mOriginalIcon.getIcon() != null + ? mOriginalIcon.getIcon().getBounds().height() + : mOriginalIcon.getHeight(); } /** @@ -638,17 +355,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra }; } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mInterceptTouchDown.set(ev.getX(), ev.getY()); - return false; - } - // Stop sending touch events to deep shortcut views if user moved beyond touch slop. - return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY()) - > ViewConfiguration.get(getContext()).getScaledTouchSlop(); - } - /** * Updates the notification header if the original icon's badge updated. */ @@ -719,18 +425,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra badgeInfo.getNotificationKeys())); } - @Override - protected void onWidgetsBound() { - if (mShortcutsItemView != null) { - mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon); - } - } - - private ObjectAnimator createArrowScaleAnim(float scale) { - return LauncherAnimUtils.ofPropertyValuesHolder( - mArrow, new PropertyListBuilder().scale(scale).build()); - } - public Animator reduceNotificationViewHeight(int heightToRemove, int duration) { return adjustItemHeights(heightToRemove, 0, duration); } @@ -832,124 +526,20 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra } @Override - protected void handleClose(boolean animate) { - if (animate) { - animateClose(); - } else { - closeComplete(); - } - } - - protected void animateClose() { - if (!mIsOpen) { - return; - } - mEndRect.setEmpty(); - if (mOpenCloseAnimator != null) { - Outline outline = new Outline(); - getOutlineProvider().getOutline(this, outline); - outline.getRect(mEndRect); - mOpenCloseAnimator.cancel(); - } - mIsOpen = false; - - final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet(); - final Resources res = getResources(); - final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration); - final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator(); - - // Rectangular reveal (reversed). - int itemsTotalHeight = 0; - for (int i = 0; i < getItemCount(); i++) { - itemsTotalHeight += getItemViewAt(i).getMeasuredHeight(); - } - Point startPoint = computeAnimStartPoint(itemsTotalHeight); - int top = mIsAboveIcon ? getPaddingTop() : startPoint.y; - float radius = getItemViewAt(0).getBackgroundRadius(); - mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y); - if (mEndRect.isEmpty()) { - mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight); - } - final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider( - radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true); - revealAnim.setDuration(revealDuration); - revealAnim.setInterpolator(revealInterpolator); - closeAnim.play(revealAnim); - - Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0); - fadeOut.setDuration(revealDuration); - fadeOut.setInterpolator(revealInterpolator); - closeAnim.play(fadeOut); - + protected void prepareCloseAnimator(AnimatorSet closeAnim) { // Animate original icon's text back in. - Animator fadeText = mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */); - fadeText.setDuration(revealDuration); - closeAnim.play(fadeText); + closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */)); - closeAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mOpenCloseAnimator = null; - if (mDeferContainerRemoval) { - setVisibility(INVISIBLE); - } else { - closeComplete(); - } - } - }); - mOpenCloseAnimator = closeAnim; - closeAnim.start(); mOriginalIcon.forceHideBadge(false); + super.prepareCloseAnimator(closeAnim); } - /** - * Closes the folder without animation. - */ + @Override protected void closeComplete() { - if (mOpenCloseAnimator != null) { - mOpenCloseAnimator.cancel(); - mOpenCloseAnimator = null; - } - mIsOpen = false; - mDeferContainerRemoval = false; mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible()); mOriginalIcon.forceHideBadge(false); + mLauncher.getDragController().removeDragListener(this); - mLauncher.getDragLayer().removeView(this); - } - - @Override - protected boolean isOfType(int type) { - return (type & TYPE_POPUP_CONTAINER_WITH_ARROW) != 0; - } - - /** - * Returns a DeepShortcutsContainer which is already open or null - */ - public static PopupContainerWithArrow getOpen(Launcher launcher) { - return getOpenView(launcher, TYPE_POPUP_CONTAINER_WITH_ARROW); - } - - @Override - public void logActionCommand(int command) { - mLauncher.getUserEventDispatcher().logActionCommand( - command, mOriginalIcon, ContainerType.DEEPSHORTCUTS); - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - DragLayer dl = mLauncher.getDragLayer(); - if (!dl.isEventOverView(this, ev)) { - mLauncher.getUserEventDispatcher().logActionTapOutside( - LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS)); - close(true); - - // We let touches on the original icon go through so that users can launch - // the app with one tap if they don't find a shortcut they want. - return mOriginalIcon == null || !dl.isEventOverView(mOriginalIcon, ev); - } - } - return false; + super.closeComplete(); } } diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index c921b4b827..aeb7134790 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -102,11 +102,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan mPackageUserToBadgeInfos.remove(removedPackageUserKey); } updateLauncherIconBadges(Utilities.singletonHashSet(removedPackageUserKey)); - - PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher); - if (openContainer != null) { - openContainer.trimNotifications(mPackageUserToBadgeInfos); - } + trimNotifications(mPackageUserToBadgeInfos); } } @@ -143,10 +139,13 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan if (!updatedBadges.isEmpty()) { updateLauncherIconBadges(updatedBadges.keySet()); } + trimNotifications(updatedBadges); + } - PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher); - if (openContainer != null) { - openContainer.trimNotifications(updatedBadges); + private void trimNotifications(Map updatedBadges) { + BaseActionPopup openContainer = BaseActionPopup.getOpen(mLauncher); + if (openContainer instanceof PopupContainerWithArrow) { + ((PopupContainerWithArrow) openContainer).trimNotifications(updatedBadges); } } diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java index 8ec051b304..75c3f26cfd 100644 --- a/src/com/android/launcher3/popup/PopupItemView.java +++ b/src/com/android/launcher3/popup/PopupItemView.java @@ -32,7 +32,7 @@ import android.widget.FrameLayout; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.popup.PopupContainerWithArrow.RoundedCornerFlags; +import com.android.launcher3.popup.BaseActionPopup.RoundedCornerFlags; import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS; import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS; From f1fbc3fbe78997f141e2770221fe5ab1b1e68014 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 10 Oct 2017 15:21:15 -0700 Subject: [PATCH 049/885] Converting widget panel into a floating view > The widget panel is only inflated when needed > Using the swipe up/down interaction for widgets tray > Removing additional view wrappers from all-apps > Widget tray is preserved across activity recreation > Launcher no longer has WIDGET state, the actual code around the states will be removed in a follow-up cl Bug: 67678570 Bug: 67585158 Change-Id: Ia29a7c33ec81e6c53cc24e2906b7022b6f41755b --- res/layout-land/launcher.xml | 6 - res/layout-port/launcher.xml | 6 - res/layout-sw720dp/launcher.xml | 6 - res/layout/all_apps.xml | 63 +- ...idgets_view.xml => widgets_full_sheet.xml} | 51 +- res/values/attrs.xml | 7 - res/values/config.xml | 2 - .../launcher3/AbstractFloatingView.java | 23 +- .../android/launcher3/BaseContainerView.java | 218 ------- .../android/launcher3/BaseRecyclerView.java | 36 +- src/com/android/launcher3/Launcher.java | 113 ++-- src/com/android/launcher3/LauncherModel.java | 3 +- .../LauncherStateTransitionAnimation.java | 594 ++++-------------- .../launcher3/WidgetPreviewLoader.java | 11 +- .../allapps/AllAppsContainerView.java | 120 ++-- .../AllAppsRecyclerViewContainerView.java | 73 --- .../allapps/AllAppsTransitionController.java | 2 +- .../anim/CircleRevealOutlineProvider.java | 53 -- .../launcher3/model/BaseModelUpdateTask.java | 6 +- .../launcher3/model/LoaderResults.java | 6 +- .../android/launcher3/model/WidgetsModel.java | 27 +- .../launcher3/popup/PopupDataProvider.java | 30 + .../launcher3/popup/SystemShortcut.java | 5 +- .../views/RecyclerViewFastScroller.java | 6 +- .../launcher3/widget/BaseWidgetSheet.java | 288 +++++++++ .../android/launcher3/widget/WidgetCell.java | 33 +- .../launcher3/widget/WidgetsBottomSheet.java | 199 +----- .../widget/WidgetsContainerView.java | 243 ------- .../launcher3/widget/WidgetsDiffReporter.java | 37 +- .../launcher3/widget/WidgetsFullSheet.java | 222 +++++++ .../launcher3/widget/WidgetsListAdapter.java | 85 +-- .../launcher3/widget/WidgetsRecyclerView.java | 16 +- .../widget/WidgetsListAdapterTest.java | 47 +- 33 files changed, 1026 insertions(+), 1611 deletions(-) rename res/layout/{widgets_view.xml => widgets_full_sheet.xml} (56%) delete mode 100644 src/com/android/launcher3/BaseContainerView.java delete mode 100644 src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java delete mode 100644 src/com/android/launcher3/anim/CircleRevealOutlineProvider.java create mode 100644 src/com/android/launcher3/widget/BaseWidgetSheet.java delete mode 100644 src/com/android/launcher3/widget/WidgetsContainerView.java create mode 100644 src/com/android/launcher3/widget/WidgetsFullSheet.java diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index ac440fc01e..0c7999ec78 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -60,12 +60,6 @@ android:id="@+id/overview_panel" android:visibility="gone" /> - - - - - - + android:clipChildren="true" + android:clipToPadding="false" + android:focusable="true" + android:focusableInTouchMode="true" + android:saveEnabled="false" > - + - - - + android:descendantFocusability="afterDescendants" + android:focusable="true" + android:overScrollMode="never" /> - - + + - - + - - - + android:layout_alignParentBottom="true" + android:background="?attr/allAppsNavBarScrimColor" /> \ No newline at end of file diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_full_sheet.xml similarity index 56% rename from res/layout/widgets_view.xml rename to res/layout/widgets_full_sheet.xml index 4f3c7c8df0..153529968f 100644 --- a/res/layout/widgets_view.xml +++ b/res/layout/widgets_full_sheet.xml @@ -1,5 +1,5 @@ - - - + android:orientation="vertical" + android:theme="?attr/widgetsTheme" > - + android:layout_height="match_parent" /> + android:background="?android:attr/colorPrimary" + android:elevation="4dp"> + android:layout_height="match_parent" + android:clipToPadding="false" + /> - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index e87397bb4b..ad5f0b880f 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -63,13 +63,6 @@ - - - - - - diff --git a/res/values/config.xml b/res/values/config.xml index f8faf988ac..27d4655b9b 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -57,10 +57,8 @@ - 220 320 300 - 60 diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 0fbad522ec..62e0fb1bcd 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.support.annotation.IntDef; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; @@ -40,7 +41,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch TYPE_FOLDER, TYPE_ACTION_POPUP, TYPE_WIDGETS_BOTTOM_SHEET, - TYPE_WIDGET_RESIZE_FRAME + TYPE_WIDGET_RESIZE_FRAME, + TYPE_WIDGETS_FULL_SHEET }) @Retention(RetentionPolicy.SOURCE) public @interface FloatingViewType {} @@ -48,6 +50,10 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_ACTION_POPUP = 1 << 1; public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2; public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3; + public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4; + + public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP + | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET; protected boolean mIsOpen; @@ -121,24 +127,31 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } } - public static void closeAllOpenViews(Launcher launcher, boolean animate) { + public static void closeOpenViews(Launcher launcher, boolean animate, + @FloatingViewType int type) { DragLayer dragLayer = launcher.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, // and will be one of the last views. for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { View child = dragLayer.getChildAt(i); if (child instanceof AbstractFloatingView) { - ((AbstractFloatingView) child).close(animate); + AbstractFloatingView abs = (AbstractFloatingView) child; + if (abs.isOfType(type)) { + abs.close(animate); + } } } } + public static void closeAllOpenViews(Launcher launcher, boolean animate) { + closeOpenViews(launcher, animate, TYPE_ALL); + } + public static void closeAllOpenViews(Launcher launcher) { closeAllOpenViews(launcher, true); } public static AbstractFloatingView getTopOpenView(Launcher launcher) { - return getOpenView(launcher, TYPE_FOLDER | TYPE_ACTION_POPUP - | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME); + return getOpenView(launcher, TYPE_ALL); } } diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java deleted file mode 100644 index 82175b721d..0000000000 --- a/src/com/android/launcher3/BaseContainerView.java +++ /dev/null @@ -1,218 +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.content.Context; -import android.content.res.TypedArray; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.widget.FrameLayout; - -import com.android.launcher3.allapps.AllAppsContainerView; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.util.TransformingTouchDelegate; - -/** - * A base container view, which supports resizing. - */ -public abstract class BaseContainerView extends FrameLayout - implements DeviceProfile.LauncherLayoutChangeListener { - - private static final Rect sBgPaddingRect = new Rect(); - - protected final Drawable mBaseDrawable; - - private View mRevealView; - private View mContent; - - private TransformingTouchDelegate mTouchDelegate; - - private final PointF mLastTouchDownPosPx = new PointF(-1.0f, -1.0f); - - 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); - - if (this instanceof AllAppsContainerView) { - mBaseDrawable = new ColorDrawable(); - } else { - TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.BaseContainerView, defStyleAttr, 0); - mBaseDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground); - a.recycle(); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); - grid.addLauncherLayoutChangedListener(this); - - View touchDelegateTargetView = getTouchDelegateTargetView(); - if (touchDelegateTargetView != null) { - mTouchDelegate = new TransformingTouchDelegate(touchDelegateTargetView); - ((View) touchDelegateTargetView.getParent()).setTouchDelegate(mTouchDelegate); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); - grid.removeLauncherLayoutChangedListener(this); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mContent = findViewById(R.id.main_content); - mRevealView = findViewById(R.id.reveal_view); - - updatePaddings(); - } - - @Override - public void onLauncherLayoutChanged() { - updatePaddings(); - } - - /** - * Calculate the background padding as it can change due to insets/content padding change. - */ - private void updatePaddings() { - DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); - int[] padding = grid.getContainerPadding(); - - int paddingLeft = padding[0]; - int paddingRight = padding[1]; - int paddingTop = 0; - int paddingBottom = 0; - - if (!grid.isVerticalBarLayout()) { - paddingLeft += grid.edgeMarginPx; - paddingRight += grid.edgeMarginPx; - paddingTop = paddingBottom = grid.edgeMarginPx; - } - updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - /** - * Update the background for the reveal view and content view based on the background padding. - */ - protected void updateBackground(int paddingLeft, int paddingTop, - int paddingRight, int paddingBottom) { - mRevealView.setBackground(new InsetDrawable(mBaseDrawable, - paddingLeft, paddingTop, paddingRight, paddingBottom)); - mContent.setBackground(new InsetDrawable(mBaseDrawable, - paddingLeft, paddingTop, paddingRight, paddingBottom)); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - View touchDelegateTargetView = getTouchDelegateTargetView(); - if (touchDelegateTargetView != null) { - getRevealView().getBackground().getPadding(sBgPaddingRect); - mTouchDelegate.setBounds( - touchDelegateTargetView.getLeft() - sBgPaddingRect.left, - touchDelegateTargetView.getTop() - sBgPaddingRect.top, - touchDelegateTargetView.getRight() + sBgPaddingRect.right, - touchDelegateTargetView.getBottom() + sBgPaddingRect.bottom); - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return handleTouchEvent(ev); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent ev) { - return handleTouchEvent(ev); - } - - public void setRevealDrawableColor(int color) { - ((ColorDrawable) mBaseDrawable).setColor(color); - } - - public final View getContentView() { - return mContent; - } - - public final View getRevealView() { - return mRevealView; - } - - - /** - * Handles the touch events that shows the workspace when clicking outside the bounds of the - * touch delegate target view. - */ - private boolean handleTouchEvent(MotionEvent ev) { - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - // Check if the touch is outside touch delegate target view - View touchDelegateTargetView = getTouchDelegateTargetView(); - float leftBoundPx = touchDelegateTargetView.getLeft(); - if (ev.getX() < leftBoundPx || - ev.getX() > (touchDelegateTargetView.getWidth() + leftBoundPx)) { - mLastTouchDownPosPx.set((int) ev.getX(), (int) ev.getY()); - } - break; - case MotionEvent.ACTION_UP: - if (mLastTouchDownPosPx.x > -1) { - ViewConfiguration viewConfig = ViewConfiguration.get(getContext()); - float dx = ev.getX() - mLastTouchDownPosPx.x; - float dy = ev.getY() - mLastTouchDownPosPx.y; - float distance = PointF.length(dx, dy); - if (distance < viewConfig.getScaledTouchSlop()) { - // The background was clicked, so just go home - Launcher.getLauncher(getContext()).showWorkspace(true); - return true; - } - } - // Fall through - case MotionEvent.ACTION_CANCEL: - mLastTouchDownPosPx.set(-1, -1); - break; - } - return false; - } - - public abstract View getTouchDelegateTargetView(); -} diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 3ee6e51b8d..afb83be580 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -99,11 +100,15 @@ public abstract class BaseRecyclerView extends RecyclerView // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS } + public int getScrollBarTop() { + return getPaddingTop(); + } + /** * Returns the height of the fast scroll bar */ public int getScrollbarTrackHeight() { - return getHeight() - getPaddingTop() - getPaddingBottom(); + return getHeight() - getScrollBarTop() - getPaddingBottom(); } /** @@ -121,13 +126,6 @@ public abstract class BaseRecyclerView extends RecyclerView return availableScrollBarHeight; } - /** - * Returns the scrollbar for this recycler view. - */ - public RecyclerViewFastScroller getScrollBar() { - return mScrollbar; - } - @Override protected void dispatchDraw(Canvas canvas) { onUpdateScrollbar(0); @@ -159,6 +157,28 @@ public abstract class BaseRecyclerView extends RecyclerView mScrollbar.setThumbOffsetY(scrollBarY); } + /** + * Returns whether the view itself will handle the touch event or not. + * @param ev MotionEvent in {@param eventSource} + */ + public boolean shouldContainerScroll(MotionEvent ev, View eventSource) { + int[] point = new int[2]; + point[0] = (int) ev.getX(); + point[1] = (int) ev.getY(); + Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point); + // IF the MotionEvent is inside the thumb, container should not be pulled down. + if (mScrollbar.shouldBlockIntercept(point[0], point[1])) { + return false; + } + + // IF scroller is at the very top OR there is no scroll bar because there is probably not + // enough items to scroll, THEN it's okay for the container to be pulled down. + if (getCurrentScrollY() == 0) { + return true; + } + return false; + } + /** * @return whether fast scrolling is supported in the current state. */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1bb4807b58..5c635ca513 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -54,6 +54,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; import android.os.UserHandle; @@ -63,6 +64,7 @@ import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; +import android.util.SparseArray; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; @@ -105,8 +107,6 @@ import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.ModelWriter; -import com.android.launcher3.model.PackageItemInfo; -import com.android.launcher3.model.WidgetItem; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.popup.BaseActionPopup; @@ -136,7 +136,8 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetHostViewLoader; -import com.android.launcher3.widget.WidgetsContainerView; +import com.android.launcher3.widget.WidgetListRowEntry; +import com.android.launcher3.widget.WidgetsFullSheet; import com.android.launcher3.widget.custom.CustomWidgetParser; import java.io.FileDescriptor; @@ -198,6 +199,8 @@ public class Launcher extends BaseActivity private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args"; // Type: ActivityResultInfo private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result"; + // Type: SparseArray + private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; @@ -234,7 +237,6 @@ public class Launcher extends BaseActivity private ViewGroup mOverviewPanel; private View mAllAppsButton; - private View mWidgetsButton; private DropTargetBar mDropTargetBar; @@ -242,9 +244,6 @@ public class Launcher extends BaseActivity @Thunk AllAppsContainerView mAppsView; AllAppsTransitionController mAllAppsController; - // Main container view and the model for the widget tray screen. - @Thunk WidgetsContainerView mWidgetsView; - // We need to store the orientation Launcher was created with, due to a bug (b/64916689) // that results in widgets being inflated in the wrong orientation. private int mOrientation; @@ -1043,8 +1042,6 @@ public class Launcher extends BaseActivity ? stateValues[stateOrdinal] : State.WORKSPACE; if (state == State.APPS) { showAppsView(false /* animated */); - } else if (state == State.WIDGETS) { - showWidgetsView(false, false); } PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS); @@ -1053,6 +1050,12 @@ public class Launcher extends BaseActivity } mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT); + + SparseArray widgetsState = + savedState.getSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL); + if (widgetsState != null) { + WidgetsFullSheet.show(this, false).restoreHierarchyState(widgetsState); + } } /** @@ -1093,9 +1096,8 @@ public class Launcher extends BaseActivity // Get the search/delete/uninstall bar mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar); - // Setup Apps and Widgets - mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view); - mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view); + // Setup Apps + mAppsView = findViewById(R.id.apps_view); // Setup the drag controller (drop targets have to be added in reverse order in priority) mDragController.setMoveTarget(mWorkspace); @@ -1110,7 +1112,7 @@ public class Launcher extends BaseActivity } private void setupOverviewPanel() { - mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel); + mOverviewPanel = findViewById(R.id.overview_panel); // Bind wallpaper button actions View wallpaperButton = findViewById(R.id.wallpaper_button); @@ -1122,13 +1124,12 @@ public class Launcher extends BaseActivity }.attachTo(wallpaperButton); // Bind widget button actions - mWidgetsButton = findViewById(R.id.widget_button); new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) { @Override public void handleViewClick(View view) { onClickAddWidgetButton(view); } - }.attachTo(mWidgetsButton); + }.attachTo(findViewById(R.id.widget_button)); // Bind settings actions View settingsButton = findViewById(R.id.settings_button); @@ -1155,14 +1156,6 @@ public class Launcher extends BaseActivity mAllAppsButton = allAppsButton; } - public View getStartViewForAllAppsRevealAnimation() { - return FeatureFlags.NO_ALL_APPS_ICON ? mWorkspace.getPageIndicator() : mAllAppsButton; - } - - public View getWidgetsButton() { - return mWidgetsButton; - } - /** * Creates a view representing a shortcut. * @@ -1325,7 +1318,7 @@ public class Launcher extends BaseActivity if (Intent.ACTION_SCREEN_OFF.equals(action)) { // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop - if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) { + if (mAppsView != null && mPendingRequestArgs == null) { if (!showWorkspace(false)) { // If we are already on the workspace, then manually reset all apps mAppsView.reset(); @@ -1385,10 +1378,6 @@ public class Launcher extends BaseActivity return mAppsView; } - public WidgetsContainerView getWidgetsView() { - return mWidgetsView; - } - public Workspace getWorkspace() { return mWorkspace; } @@ -1468,11 +1457,6 @@ public class Launcher extends BaseActivity mAppsView.reset(); } - // Reset the widgets view - if (!alreadyOnHome && mWidgetsView != null) { - mWidgetsView.scrollToTop(); - } - if (mLauncherCallbacks != null) { mLauncherCallbacks.onHomeIntent(); } @@ -1517,9 +1501,19 @@ public class Launcher extends BaseActivity outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); } - super.onSaveInstanceState(outState); - outState.putInt(RUNTIME_STATE, mState.ordinal()); + + + AbstractFloatingView widgets = AbstractFloatingView + .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET); + if (widgets != null) { + SparseArray widgetsState = new SparseArray<>(); + widgets.saveHierarchyState(widgetsState); + outState.putSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL, widgetsState); + } else { + outState.remove(RUNTIME_STATE_WIDGET_PANEL); + } + // We close any open folders and shortcut containers since they will not be re-opened, // and we need to make sure this state is reflected. AbstractFloatingView.closeAllOpenViews(this, false); @@ -1531,6 +1525,8 @@ public class Launcher extends BaseActivity outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult); } + super.onSaveInstanceState(outState); + if (mLauncherCallbacks != null) { mLauncherCallbacks.onSaveInstanceState(outState); } @@ -2159,7 +2155,7 @@ public class Launcher extends BaseActivity if (mIsSafeModeEnabled) { Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); } else { - showWidgetsView(true /* animated */, true /* resetPageToZero */); + WidgetsFullSheet.show(this, true /* animated */); } } @@ -2556,24 +2552,6 @@ public class Launcher extends BaseActivity showAppsOrWidgets(State.APPS, animated); } - /** - * Shows the widgets view. - */ - void showWidgetsView(boolean animated, boolean resetPageToZero) { - if (LOGD) Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero); - if (resetPageToZero) { - mWidgetsView.scrollToTop(); - } - showAppsOrWidgets(State.WIDGETS, animated); - - mWidgetsView.post(new Runnable() { - @Override - public void run() { - mWidgetsView.requestFocus(); - } - }); - } - /** * Sets up the transition to show the apps/widgets view. * @@ -2642,7 +2620,6 @@ public class Launcher extends BaseActivity // 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 - mWidgetsView.setVisibility(View.GONE); showWorkspace(true, onCompleteRunnable); } else { exitSpringLoadedDragMode(); @@ -2661,8 +2638,6 @@ public class Launcher extends BaseActivity public void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { showAppsView(true /* animated */); - } else if (mState == State.WIDGETS_SPRING_LOADED) { - showWidgetsView(true, false); } else if (mState == State.WORKSPACE_SPRING_LOADED) { showWorkspace(true); } @@ -2676,8 +2651,6 @@ public class Launcher extends BaseActivity // Populate event with a fake title based on the current state. if (mState == State.APPS) { text.add(getString(R.string.all_apps_button_label)); - } else if (mState == State.WIDGETS) { - text.add(getString(R.string.widget_button_text)); } else if (mWorkspace != null) { text.add(mWorkspace.getCurrentPageDescription()); } else { @@ -2777,7 +2750,11 @@ public class Launcher extends BaseActivity */ public void startBinding() { TraceHelper.beginSection("startBinding"); - AbstractFloatingView.closeAllOpenViews(this); + // Floating panels (except the full widget sheet) are associated with individual icons. If + // we are starting a fresh bind, close all such panels as all the icons are about + // to go away. + AbstractFloatingView.closeOpenViews(this, true, + AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET); setWorkspaceLoading(true); @@ -3388,7 +3365,8 @@ public class Launcher extends BaseActivity } @Override - public void bindAllWidgets(final MultiHashMap allWidgets) { + public void bindAllWidgets(final ArrayList allWidgets) { + mPopupDataProvider.setAllWidgets(allWidgets); Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) { @Override public void run() { @@ -3399,25 +3377,12 @@ public class Launcher extends BaseActivity return; } - if (mWidgetsView != null && allWidgets != null) { - Executor pendingExecutor = getPendingExecutor(); - if (pendingExecutor != null && mState != State.WIDGETS) { - pendingExecutor.execute(r); - return; - } - mWidgetsView.setWidgets(allWidgets); - } - AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); if (topView != null) { topView.onWidgetsBound(); } } - public List getWidgetsForPackageUser(PackageUserKey packageUserKey) { - return mWidgetsView.getWidgetsForPackageUser(packageUserKey); - } - @Override public void notifyWidgetProvidersChanged() { if (mWorkspace.getState().shouldUpdateWidget) { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3e2236682c..618bd0f734 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -62,6 +62,7 @@ import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Provider; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.launcher3.widget.WidgetListRowEntry; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -153,7 +154,7 @@ public class LauncherModel extends BroadcastReceiver public void bindRestoreItemsChange(HashSet updates); public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher); public void bindAppInfosRemoved(ArrayList appInfos); - public void bindAllWidgets(MultiHashMap widgets); + public void bindAllWidgets(ArrayList widgets); public void onPageBoundSynchronously(int page); public void executeOnNextDraw(ViewOnDrawExecutor executor); public void bindDeepShortcutMap(MultiHashMap deepShortcutMap); diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index be0ed0cef4..5823734e47 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -19,22 +19,15 @@ 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.animation.ValueAnimator; import android.content.res.Resources; import android.util.Log; import android.view.View; -import android.view.animation.AccelerateInterpolator; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationLayerSet; -import com.android.launcher3.anim.CircleRevealOutlineProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.Thunk; -import com.android.launcher3.widget.WidgetsContainerView; /** * TODO: figure out what kind of tests we can write for this @@ -79,41 +72,8 @@ import com.android.launcher3.widget.WidgetsContainerView; */ public class LauncherStateTransitionAnimation { - /** - * animation used for the widget tray - */ - public static final int CIRCULAR_REVEAL = 0; - /** - * animation used for all apps tray - */ - public static final int PULLUP = 1; - - private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f; - - /** - * Private callbacks made during transition setup. - */ - private static class PrivateTransitionCallbacks { - private final float materialRevealViewFinalAlpha; - - PrivateTransitionCallbacks(float revealAlpha) { - materialRevealViewFinalAlpha = revealAlpha; - } - - float getMaterialRevealViewStartFinalRadius() { - return 0; - } - AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView, - View buttonView) { - return null; - } - void onTransitionComplete() {} - } - public static final String TAG = "LSTAnimation"; - public static final int SINGLE_FRAME_DELAY = 16; - @Thunk Launcher mLauncher; @Thunk AnimatorSet mCurrentAnimation; AllAppsTransitionController mAllAppsController; @@ -128,49 +88,63 @@ public class LauncherStateTransitionAnimation { */ public void startAnimationToAllApps(final boolean animated) { final AllAppsContainerView toView = mLauncher.getAppsView(); - final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation(); - PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) { + + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); + final Resources res = mLauncher.getResources(); + final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime); + + final AnimationLayerSet layerViews = new AnimationLayerSet(); + + // If for some reason our views aren't initialized, don't animate + boolean initialized = toView != null; + + // Cancel the current animation + cancelAnimation(); + + playCommonTransitionAnimations(Workspace.State.NORMAL_HIDDEN, + animated, initialized, animation, layerViews); + if (!animated || !initialized) { + mAllAppsController.finishPullUp(); + toView.setTranslationX(0.0f); + toView.setTranslationY(0.0f); + toView.setScaleX(1.0f); + toView.setScaleY(1.0f); + toView.setAlpha(1.0f); + toView.setVisibility(View.VISIBLE); + + mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); + return; + } + if (!FeatureFlags.LAUNCHER3_PHYSICS) { + // We are animating the content view alpha, so ensure we have a layer for it. + layerViews.addView(toView); + } + + animation.addListener(new AnimatorListenerAdapter() { @Override - public float getMaterialRevealViewStartFinalRadius() { - int allAppsButtonSize = mLauncher.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); - } - }; - } - @Override - void onTransitionComplete() { + public void onAnimationEnd(Animator animation) { + cleanupAnimation(); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } - }; - // Only animate the search bar if animating from spring loaded mode back to all apps - startAnimationToOverlay( - Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, PULLUP, cb); + }); + boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide); + + Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); + mCurrentAnimation = animation; + mCurrentAnimation.addListener(layerViews); + if (shouldPost) { + toView.post(startAnimRunnable); + } else { + startAnimRunnable.run(); + } } /** * Starts an animation to the widgets view. */ public void startAnimationToWidgets(final boolean animated) { - final WidgetsContainerView toView = mLauncher.getWidgetsView(); - final View buttonView = mLauncher.getWidgetsButton(); - startAnimationToOverlay( - Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL, - new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){ - @Override - void onTransitionComplete() { - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - } - }); + // TODO: Remove this + throw new RuntimeException("This cannot happen"); } /** @@ -188,10 +162,6 @@ public class LauncherStateTransitionAnimation { if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED || mAllAppsController.isTransitioning()) { startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, - animated, PULLUP, onCompleteRunnable); - } else if (fromState == Launcher.State.WIDGETS || - fromState == Launcher.State.WIDGETS_SPRING_LOADED) { - startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, animated, onCompleteRunnable); } else { startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState, @@ -199,159 +169,6 @@ public class LauncherStateTransitionAnimation { } } - /** - * Creates and starts a new animation to a particular overlay view. - */ - private void startAnimationToOverlay( - final Workspace.State toWorkspaceState, - final View buttonView, final BaseContainerView toView, - final boolean animated, int animType, final PrivateTransitionCallbacks pCb) { - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - final Resources res = mLauncher.getResources(); - final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); - final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime); - - final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger); - - final AnimationLayerSet layerViews = new AnimationLayerSet(); - - // If for some reason our views aren't initialized, don't animate - boolean initialized = buttonView != null; - - // Cancel the current animation - cancelAnimation(); - - final View contentView = toView.getContentView(); - playCommonTransitionAnimations(toWorkspaceState, - animated, initialized, animation, layerViews); - if (!animated || !initialized) { - if (toWorkspaceState == Workspace.State.NORMAL_HIDDEN) { - mAllAppsController.finishPullUp(); - } - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setScaleX(1.0f); - toView.setScaleY(1.0f); - toView.setAlpha(1.0f); - toView.setVisibility(View.VISIBLE); - - // Show the content view - contentView.setVisibility(View.VISIBLE); - pCb.onTransitionComplete(); - return; - } - if (animType == CIRCULAR_REVEAL) { - // Setup the reveal view animation - final View revealView = toView.getRevealView(); - - int width = revealView.getMeasuredWidth(); - int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.hypot(width / 2, height / 2); - revealView.setVisibility(View.VISIBLE); - revealView.setAlpha(0f); - revealView.setTranslationY(0f); - revealView.setTranslationX(0f); - - // Calculate the final animation values - int[] buttonViewToPanelDelta = - Utilities.getCenterDeltaInScreenSpace(revealView, buttonView); - final float revealViewToAlpha = pCb.materialRevealViewFinalAlpha; - final float revealViewToXDrift = buttonViewToPanelDelta[0]; - final float revealViewToYDrift = buttonViewToPanelDelta[1]; - - // Create the animators - PropertyValuesHolder panelAlpha = - PropertyValuesHolder.ofFloat(View.ALPHA, revealViewToAlpha, 1f); - PropertyValuesHolder panelDriftY = - PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, revealViewToYDrift, 0); - PropertyValuesHolder panelDriftX = - PropertyValuesHolder.ofFloat(View.TRANSLATION_X, revealViewToXDrift, 0); - ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, - panelAlpha, panelDriftY, panelDriftX); - panelAlphaAndDrift.setDuration(revealDuration); - panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); - - // Play the animation - layerViews.addView(revealView); - animation.play(panelAlphaAndDrift); - - // Setup the animation for the content view - contentView.setVisibility(View.VISIBLE); - contentView.setAlpha(0f); - contentView.setTranslationY(revealViewToYDrift); - layerViews.addView(contentView); - - // Create the individual animators - ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY", - revealViewToYDrift, 0); - pageDrift.setDuration(revealDuration); - pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); - pageDrift.setStartDelay(itemsAlphaStagger); - animation.play(pageDrift); - - ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f); - itemsAlpha.setDuration(revealDuration); - itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - itemsAlpha.setStartDelay(itemsAlphaStagger); - animation.play(itemsAlpha); - - float startRadius = pCb.getMaterialRevealViewStartFinalRadius(); - AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener( - revealView, buttonView); - Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2, - startRadius, revealRadius).createRevealAnimator(revealView); - reveal.setDuration(revealDuration); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - if (listener != null) { - reveal.addListener(listener); - } - animation.play(reveal); - - animation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Hide the reveal view - revealView.setVisibility(View.INVISIBLE); - - // This can hold unnecessary references to views. - cleanupAnimation(); - pCb.onTransitionComplete(); - } - - }); - - toView.bringToFront(); - toView.setVisibility(View.VISIBLE); - - animation.addListener(layerViews); - toView.post(new StartAnimRunnable(animation, toView)); - mCurrentAnimation = animation; - } else if (animType == PULLUP) { - if (!FeatureFlags.LAUNCHER3_PHYSICS) { - // We are animating the content view alpha, so ensure we have a layer for it. - layerViews.addView(contentView); - } - - animation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - cleanupAnimation(); - pCb.onTransitionComplete(); - } - }); - boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide); - - Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); - if (shouldPost) { - toView.post(startAnimRunnable); - } else { - startAnimRunnable.run(); - } - } - } - /** * Plays animations used by various transitions. */ @@ -376,73 +193,73 @@ public class LauncherStateTransitionAnimation { * Starts an animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final boolean animated, int type, - final Runnable onCompleteRunnable) { - // No alpha anim from all apps - PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) { - @Override - float getMaterialRevealViewStartFinalRadius() { - int allAppsButtonSize = mLauncher.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); - } - }; - } - @Override - void onTransitionComplete() { - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - } - }; - // Only animate the search bar if animating to spring loaded mode from all apps - startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState, - mLauncher.getStartViewForAllAppsRevealAnimation(), mLauncher.getAppsView(), - animated, type, onCompleteRunnable, cb); - } - - /** - * Starts an animation to the workspace from the widgets view. - */ - private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { - final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); - PrivateTransitionCallbacks cb = - new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) { - @Override - public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( - final View revealView, final View widgetsButtonView) { - return new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - // Hide the reveal view - revealView.setVisibility(View.INVISIBLE); - } - }; + + final AllAppsContainerView fromView = mLauncher.getAppsView(); + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); + final Resources res = mLauncher.getResources(); + final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime); + + final View toView = mLauncher.getWorkspace(); + + final AnimationLayerSet layerViews = new AnimationLayerSet(); + + // If for some reason our views aren't initialized, don't animate + boolean initialized = fromView != null; + + // Cancel the current animation + cancelAnimation(); + + playCommonTransitionAnimations(toWorkspaceState, + animated, initialized, animation, layerViews); + if (!animated || !initialized) { + if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) { + mAllAppsController.finishPullDown(); } + fromView.setVisibility(View.GONE); + mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + return; + } + + // We are animating the content view alpha, so ensure we have a layer for it + layerViews.addView(toView); + + animation.addListener(new AnimatorListenerAdapter() { + boolean canceled = false; @Override - void onTransitionComplete() { + public void onAnimationCancel(Animator animation) { + canceled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (canceled) return; + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + cleanupAnimation(); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } - }; - startAnimationToWorkspaceFromOverlay( - fromWorkspaceState, toWorkspaceState, - mLauncher.getWidgetsButton(), widgetsView, - animated, CIRCULAR_REVEAL, onCompleteRunnable, cb); + + }); + boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide); + + Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); + mCurrentAnimation = animation; + mCurrentAnimation.addListener(layerViews); + if (shouldPost) { + fromView.post(startAnimRunnable); + } else { + startAnimRunnable.run(); + } } /** @@ -487,199 +304,6 @@ public class LauncherStateTransitionAnimation { } } - /** - * Creates and starts a new animation to the workspace. - */ - private void startAnimationToWorkspaceFromOverlay( - final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, - final View buttonView, final BaseContainerView fromView, - final boolean animated, int animType, final Runnable onCompleteRunnable, - final PrivateTransitionCallbacks pCb) { - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - final Resources res = mLauncher.getResources(); - final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); - final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime); - final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger); - - final View toView = mLauncher.getWorkspace(); - final View revealView = fromView.getRevealView(); - final View contentView = fromView.getContentView(); - - final AnimationLayerSet layerViews = new AnimationLayerSet(); - - // If for some reason our views aren't initialized, don't animate - boolean initialized = buttonView != null; - - // Cancel the current animation - cancelAnimation(); - - playCommonTransitionAnimations(toWorkspaceState, - animated, initialized, animation, layerViews); - if (!animated || !initialized) { - if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) { - mAllAppsController.finishPullDown(); - } - fromView.setVisibility(View.GONE); - pCb.onTransitionComplete(); - - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - return; - } - if (animType == CIRCULAR_REVEAL) { - // 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.hypot(width / 2, height / 2); - revealView.setVisibility(View.VISIBLE); - revealView.setAlpha(1f); - revealView.setTranslationY(0); - layerViews.addView(revealView); - - // Calculate the final animation values - int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, buttonView); - final float revealViewToXDrift = buttonViewToPanelDelta[0]; - final float revealViewToYDrift = buttonViewToPanelDelta[1]; - - // 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 = new LogDecelerateInterpolator(100, 0); - ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY", - 0, revealViewToYDrift); - panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); - panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - panelDriftY.setInterpolator(decelerateInterpolator); - animation.play(panelDriftY); - - ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX", - 0, revealViewToXDrift); - panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); - panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - panelDriftX.setInterpolator(decelerateInterpolator); - animation.play(panelDriftX); - - // Setup animation for the reveal panel alpha - if (pCb.materialRevealViewFinalAlpha != 1f) { - ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha", - 1f, pCb.materialRevealViewFinalAlpha); - panelAlpha.setDuration(revealDuration); - panelAlpha.setInterpolator(decelerateInterpolator); - animation.play(panelAlpha); - } - - // Setup the animation for the content view - layerViews.addView(contentView); - - // Create the individual animators - ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY", - 0, revealViewToYDrift); - contentView.setTranslationY(0); - pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); - pageDrift.setInterpolator(decelerateInterpolator); - pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - animation.play(pageDrift); - - contentView.setAlpha(1f); - ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f); - itemsAlpha.setDuration(100); - itemsAlpha.setInterpolator(decelerateInterpolator); - animation.play(itemsAlpha); - - // Invalidate the scrim throughout the animation to ensure the highlight - // cutout is correct throughout. - ValueAnimator invalidateScrim = ValueAnimator.ofFloat(0f, 1f); - invalidateScrim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mLauncher.getDragLayer().invalidateScrim(); - } - }); - animation.play(invalidateScrim); - - // Animate the all apps button - float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); - AnimatorListenerAdapter listener = - pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView); - Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2, - revealRadius, finalRadius).createRevealAnimator(revealView); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - reveal.setDuration(revealDuration); - reveal.setStartDelay(itemsAlphaStagger); - if (listener != null) { - reveal.addListener(listener); - } - animation.play(reveal); - } - - animation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - fromView.setVisibility(View.GONE); - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - // Reset page transforms - if (contentView != null) { - contentView.setTranslationX(0); - contentView.setTranslationY(0); - contentView.setAlpha(1); - } - - // This can hold unnecessary references to views. - cleanupAnimation(); - pCb.onTransitionComplete(); - } - }); - - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); - fromView.post(new StartAnimRunnable(animation, null)); - } else if (animType == PULLUP) { - // We are animating the content view alpha, so ensure we have a layer for it - layerViews.addView(contentView); - - animation.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - @Override - public void onAnimationCancel(Animator animation) { - canceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) return; - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - cleanupAnimation(); - pCb.onTransitionComplete(); - } - - }); - boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide); - - Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); - if (shouldPost) { - fromView.post(startAnimRunnable); - } else { - startAnimRunnable.run(); - } - } - return; - } - /** * Cancels the current animation. */ diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index f150c89c1c..bdfeae1627 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -92,12 +92,11 @@ public class WidgetPreviewLoader { * @return a request id which can be used to cancel the request. */ public CancellationSignal getPreview(WidgetItem item, int previewWidth, - int previewHeight, WidgetCell caller, boolean animate) { + int previewHeight, WidgetCell caller) { String size = previewWidth + "x" + previewHeight; WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size); - PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller, - animate); + PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller); task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR); CancellationSignal signal = new CancellationSignal(); @@ -527,19 +526,17 @@ public class WidgetPreviewLoader { private final int mPreviewHeight; private final int mPreviewWidth; private final WidgetCell mCaller; - private final boolean mAnimatePreviewIn; private final BaseActivity mActivity; @Thunk long[] mVersions; @Thunk Bitmap mBitmapToRecycle; PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth, - int previewHeight, WidgetCell caller, boolean animate) { + int previewHeight, WidgetCell caller) { mKey = key; mInfo = info; mPreviewHeight = previewHeight; mPreviewWidth = previewWidth; mCaller = caller; - mAnimatePreviewIn = animate; mActivity = BaseActivity.fromContext(mCaller.getContext()); if (DEBUG) { Log.d(TAG, String.format("%s, %s, %d, %d", @@ -595,7 +592,7 @@ public class WidgetPreviewLoader { @Override protected void onPostExecute(final Bitmap preview) { - mCaller.applyPreview(preview, mAnimatePreviewIn); + mCaller.applyPreview(preview); // Write the generated preview to the DB in the worker thread if (mVersions != null) { diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index d63ae41899..91b196eeaa 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -16,10 +16,8 @@ package com.android.launcher3.allapps; import android.content.Context; -import android.graphics.Color; +import android.graphics.Bitmap; import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.InsetDrawable; import android.support.v7.widget.LinearLayoutManager; import android.text.Selection; import android.text.SpannableStringBuilder; @@ -28,10 +26,11 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.widget.RelativeLayout; import com.android.launcher3.AppInfo; -import com.android.launcher3.BaseContainerView; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.ClickShadowView; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; @@ -41,7 +40,6 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.PromiseAppInfo; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; @@ -49,9 +47,8 @@ import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.folder.Folder; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.ComponentKeyMapper; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.TransformingTouchDelegate; import java.util.List; import java.util.Set; @@ -59,13 +56,17 @@ import java.util.Set; /** * The all apps view container. */ -public class AllAppsContainerView extends BaseContainerView implements DragSource, - View.OnLongClickListener, Insettable { +public class AllAppsContainerView extends RelativeLayout implements DragSource, + View.OnLongClickListener, Insettable, DeviceProfile.LauncherLayoutChangeListener, + BubbleTextView.BubbleTextShadowHandler { + + protected final Rect mBasePadding = new Rect(); private final Launcher mLauncher; private final AlphabeticalAppsList mApps; private final AllAppsGridAdapter mAdapter; private final LinearLayoutManager mLayoutManager; + private final ClickShadowView mTouchFeedbackView; private AllAppsRecyclerView mAppsRecyclerView; private SearchUiManager mSearchUiManager; @@ -78,6 +79,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private SpringAnimationHandler mSpringAnimationHandler; + private TransformingTouchDelegate mTouchDelegate; + public AllAppsContainerView(Context context) { this(context, null); } @@ -98,19 +101,69 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); + + mTouchFeedbackView = new ClickShadowView(context); + // Make the feedback view large enough to hold the blur bitmap. + int size = mLauncher.getDeviceProfile().allAppsIconSizePx + + mTouchFeedbackView.getExtraSize(); + addView(mTouchFeedbackView, size, size); } @Override - protected void updateBackground( - int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - getRevealView().setBackground(new InsetDrawable(mBaseDrawable, - paddingLeft, paddingTop, paddingRight, paddingBottom)); - getContentView().setBackground( - new InsetDrawable(new ColorDrawable(Color.TRANSPARENT), - paddingLeft, paddingTop, paddingRight, paddingBottom)); - } else { - getRevealView().setBackground(mBaseDrawable); + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); + grid.addLauncherLayoutChangedListener(this); + + mTouchDelegate = new TransformingTouchDelegate(mAppsRecyclerView); + setTouchDelegate(mTouchDelegate); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); + grid.removeLauncherLayoutChangedListener(this); + } + + /** + * Calculate the background padding as it can change due to insets/content padding change. + */ + @Override + public void onLauncherLayoutChanged() { + DeviceProfile grid = mLauncher.getDeviceProfile(); + if (!grid.isVerticalBarLayout()) { + return; + } + + int[] padding = grid.getContainerPadding(); + int paddingLeft = padding[0]; + int paddingRight = padding[1]; + mBasePadding.set(paddingLeft, 0, paddingRight, 0); + setPadding(paddingLeft, 0, paddingRight, 0); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mTouchDelegate.setBounds( + mAppsRecyclerView.getLeft() - mBasePadding.left, + mAppsRecyclerView.getTop() - mBasePadding.top, + mAppsRecyclerView.getRight() + mBasePadding.right, + mAppsRecyclerView.getBottom() + mBasePadding.bottom); + } + + @Override + public void setPressedIcon(BubbleTextView icon, Bitmap background) { + if (icon == null || background == null) { + mTouchFeedbackView.setBitmap(null); + mTouchFeedbackView.animate().cancel(); + } else if (mTouchFeedbackView.setBitmap(background)) { + View rv = findViewById(R.id.apps_list_view); + mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv); + mTouchFeedbackView.animateShadow(); } } @@ -157,23 +210,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) { return true; } - - int[] point = new int[2]; - point[0] = (int) ev.getX(); - point[1] = (int) ev.getY(); - Utilities.mapCoordInSelfToDescendant( - mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point); - // IF the MotionEvent is inside the thumb, container should not be pulled down. - if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) { - return false; - } - - // IF scroller is at the very top OR there is no scroll bar because there is probably not - // enough items to scroll, THEN it's okay for the container to be pulled down. - if (mAppsRecyclerView.getCurrentScrollY() == 0) { - return true; - } - return false; + return mAppsRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer()); } /** @@ -191,7 +228,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // 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. - getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() { + setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { @@ -222,20 +259,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mAppsRecyclerView.preMeasureViews(mAdapter); mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener()); - getRevealView().setVisibility(View.VISIBLE); - getContentView().setVisibility(View.VISIBLE); - getContentView().setBackground(null); + onLauncherLayoutChanged(); } public SearchUiManager getSearchUiManager() { return mSearchUiManager; } - @Override - public View getTouchDelegateTargetView() { - return mAppsRecyclerView; - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { DeviceProfile grid = mLauncher.getDeviceProfile(); diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java deleted file mode 100644 index 517dc947e0..0000000000 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java +++ /dev/null @@ -1,73 +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.Context; -import android.graphics.Bitmap; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.RelativeLayout; - -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; -import com.android.launcher3.R; - -/** - * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that - * is launching. - */ -public class AllAppsRecyclerViewContainerView extends RelativeLayout - 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.getLauncher(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)) { - View rv = findViewById(R.id.apps_list_view); - mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv); - mTouchFeedbackView.animateShadow(); - } - } -} diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index b844ba3037..bb0822f951 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -294,7 +294,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha); updateAllAppsBg(alpha); - mAppsView.getContentView().setAlpha(alpha); + mAppsView.setAlpha(alpha); mAppsView.setTranslationY(shiftCurrent); if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) { diff --git a/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java deleted file mode 100644 index 9fb6b498bb..0000000000 --- a/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2016 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.anim; - -public class CircleRevealOutlineProvider extends RevealOutlineAnimation { - - private int mCenterX; - private int mCenterY; - private float mRadius0; - private float mRadius1; - - /** - * @param x reveal center x - * @param y reveal center y - * @param r0 initial radius - * @param r1 final radius - */ - public CircleRevealOutlineProvider(int x, int y, float r0, float r1) { - mCenterX = x; - mCenterY = y; - mRadius0 = r0; - mRadius1 = r1; - } - - @Override - public boolean shouldRemoveElevationDuringAnimation() { - return true; - } - - @Override - public void setProgress(float progress) { - mOutlineRadius = (1 - progress) * mRadius0 + progress * mRadius1; - - mOutline.left = (int) (mCenterX - mOutlineRadius); - mOutline.top = (int) (mCenterY - mOutlineRadius); - mOutline.right = (int) (mCenterX + mOutlineRadius); - mOutline.bottom = (int) (mCenterY + mOutlineRadius); - } -} diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index d5b5aa7cf3..9aa30e7cca 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -28,6 +28,8 @@ import com.android.launcher3.ShortcutInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.MultiHashMap; +import com.android.launcher3.widget.WidgetListRowEntry; +import com.android.launcher3.widget.WidgetsListAdapter; import java.util.ArrayList; import java.util.concurrent.Executor; @@ -117,8 +119,8 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { } public void bindUpdatedWidgets(BgDataModel dataModel) { - final MultiHashMap widgets - = dataModel.widgetsModel.getWidgetsMap(); + final ArrayList widgets = + dataModel.widgetsModel.getWidgetsList(mApp.getContext()); scheduleCallbackTask(new CallbackTask() { @Override public void execute(Callbacks callbacks) { diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java index b7a6b68e83..24e5b9c58c 100644 --- a/src/com/android/launcher3/model/LoaderResults.java +++ b/src/com/android/launcher3/model/LoaderResults.java @@ -36,6 +36,8 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LooperIdleLock; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.launcher3.widget.WidgetListRowEntry; +import com.android.launcher3.widget.WidgetsListAdapter; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -362,8 +364,8 @@ public class LoaderResults { } public void bindWidgets() { - final MultiHashMap widgets - = mBgDataModel.widgetsModel.getWidgetsMap(); + final ArrayList widgets = + mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext()); Runnable r = new Runnable() { public void run() { Callbacks callbacks = mCallbacks.get(); diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index ed900bf358..1ff0daca05 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -15,6 +15,7 @@ import com.android.launcher3.InvariantDeviceProfile; 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.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; @@ -22,10 +23,14 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; +import com.android.launcher3.widget.WidgetItemComparator; +import com.android.launcher3.widget.WidgetListRowEntry; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; /** * Widgets data model that is used by the adapters of the widget views and controllers. @@ -42,8 +47,26 @@ public class WidgetsModel { private AppFilter mAppFilter; - public synchronized MultiHashMap getWidgetsMap() { - return mWidgetsList.clone(); + /** + * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row + * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s + * is not sorted. This list is sorted at the UI when using + * {@link com.android.launcher3.widget.WidgetsDiffReporter} + * + * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList) + */ + public synchronized ArrayList getWidgetsList(Context context) { + ArrayList result = new ArrayList<>(); + AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context); + + WidgetItemComparator widgetComparator = new WidgetItemComparator(); + for (Map.Entry> entry : mWidgetsList.entrySet()) { + WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue()); + row.titleSectionName = indexer.computeSectionName(row.pkgItem.title); + Collections.sort(row.widgets, widgetComparator); + result.add(row); + } + return result; } /** diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index aeb7134790..070ac3971e 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -25,6 +25,8 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; import com.android.launcher3.badge.BadgeInfo; +import com.android.launcher3.model.PackageItemInfo; +import com.android.launcher3.model.WidgetItem; import com.android.launcher3.notification.NotificationInfo; import com.android.launcher3.notification.NotificationKeyData; import com.android.launcher3.notification.NotificationListener; @@ -32,6 +34,7 @@ import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.WidgetListRowEntry; import java.util.ArrayList; import java.util.Collections; @@ -62,6 +65,8 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan private MultiHashMap mDeepShortcutMap = new MultiHashMap<>(); /** Maps packages to their BadgeInfo's . */ private Map mPackageUserToBadgeInfos = new HashMap<>(); + /** Maps packages to their Widgets */ + private ArrayList mAllWidgets = new ArrayList<>(); public PopupDataProvider(Launcher launcher) { mLauncher = launcher; @@ -265,4 +270,29 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan } notificationListener.cancelNotification(notificationKey); } + + public void setAllWidgets(ArrayList allWidgets) { + mAllWidgets = allWidgets; + } + + public ArrayList getAllWidgets() { + return mAllWidgets; + } + + public List getWidgetsForPackageUser(PackageUserKey packageUserKey) { + for (WidgetListRowEntry entry : mAllWidgets) { + if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) { + ArrayList widgets = new ArrayList<>(entry.widgets); + // Remove widgets not associated with the correct user. + Iterator iterator = widgets.iterator(); + while (iterator.hasNext()) { + if (!iterator.next().user.equals(packageUserKey.mUser)) { + iterator.remove(); + } + } + return widgets.isEmpty() ? null : widgets; + } + } + return null; + } } diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 3f7bf42140..e709b93688 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -59,8 +59,9 @@ public abstract class SystemShortcut extends ItemInfo { @Override public View.OnClickListener getOnClickListener(final Launcher launcher, final ItemInfo itemInfo) { - final List widgets = launcher.getWidgetsForPackageUser(new PackageUserKey( - itemInfo.getTargetComponent().getPackageName(), itemInfo.user)); + final List widgets = + launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey( + itemInfo.getTargetComponent().getPackageName(), itemInfo.user)); if (widgets == null) { return null; } diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java index 7b5bcdbd42..8c9a44186d 100644 --- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java +++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java @@ -285,7 +285,7 @@ public class RecyclerViewFastScroller extends View { return; } int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(getWidth() / 2, mRv.getPaddingTop()); + canvas.translate(getWidth() / 2, mRv.getScrollBarTop()); // Draw the track float halfW = mWidth / 2; canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(), @@ -317,7 +317,7 @@ public class RecyclerViewFastScroller extends View { * Returns whether the specified point is inside the thumb bounds. */ private boolean isNearThumb(int x, int y) { - int offset = y - mRv.getPaddingTop() - mThumbOffsetY; + int offset = y - mRv.getScrollBarTop() - mThumbOffsetY; return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight; } @@ -348,7 +348,7 @@ public class RecyclerViewFastScroller extends View { private void updatePopupY(int lastTouchY) { int height = mPopupView.getHeight(); float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height) - + mRv.getPaddingTop(); + + mRv.getScrollBarTop(); top = Utilities.boundToRange(top, mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height); mPopupView.setTranslationY(top); diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java new file mode 100644 index 0000000000..ee5dd66bde --- /dev/null +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.util.Property; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.Toast; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.graphics.GradientView; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.util.SystemUiController; +import com.android.launcher3.util.Themes; + +/** + * Base class for various widgets popup + */ +abstract class BaseWidgetSheet extends AbstractFloatingView + implements OnClickListener, OnLongClickListener, DragSource, SwipeDetector.Listener { + + + protected static Property TRANSLATION_SHIFT = + new Property(Float.class, "translationShift") { + + @Override + public Float get(BaseWidgetSheet view) { + return view.mTranslationShift; + } + + @Override + public void set(BaseWidgetSheet view, Float value) { + view.setTranslationShift(value); + } + }; + protected static final float TRANSLATION_SHIFT_CLOSED = 1f; + protected static final float TRANSLATION_SHIFT_OPENED = 0f; + + /* Touch handling related member variables. */ + private Toast mWidgetInstructionToast; + + protected final Launcher mLauncher; + protected final SwipeDetector.ScrollInterpolator mScrollInterpolator; + protected final SwipeDetector mSwipeDetector; + protected final ObjectAnimator mOpenCloseAnimator; + + protected View mContent; + protected GradientView mGradientView; + + // range [0, 1], 0=> completely open, 1=> completely closed + protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED; + + protected boolean mNoIntercept; + + public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = Launcher.getLauncher(context); + + mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); + mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); + + mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this); + mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSwipeDetector.finishedScrolling(); + } + }); + } + + @Override + public final void onClick(View v) { + // Let the user know that they have to long press to add a widget + if (mWidgetInstructionToast != null) { + mWidgetInstructionToast.cancel(); + } + + CharSequence msg = Utilities.wrapForTts( + getContext().getText(R.string.long_press_widget_to_add), + getContext().getString(R.string.long_accessible_way_to_add)); + mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT); + mWidgetInstructionToast.show(); + } + + @Override + public final boolean onLongClick(View v) { + if (!mLauncher.isDraggingEnabled()) return false; + + if (v instanceof WidgetCell) { + return beginDraggingWidget((WidgetCell) v); + } + return true; + } + + protected void setTranslationShift(float translationShift) { + mTranslationShift = translationShift; + mGradientView.setAlpha(1 - mTranslationShift); + mContent.setTranslationY(mTranslationShift * mContent.getHeight()); + } + + private boolean beginDraggingWidget(WidgetCell v) { + // Get the widget preview as the drag representation + WidgetImageView image = v.getWidgetView(); + + // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and + // we abort the drag. + if (image.getBitmap() == null) { + return false; + } + + int[] loc = new int[2]; + mLauncher.getDragLayer().getLocationInDragLayer(image, loc); + + new PendingItemDragHelper(v).startDrag( + image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(), + new Point(loc[0], loc[1]), this, new DragOptions()); + close(true); + return true; + } + + // + // Drag related handling methods that implement {@link DragSource} interface. + // + + @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); + + if (!success) { + d.deferDragViewCleanupPostAnimation = false; + } + } + + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_UP && !mNoIntercept) { + // If we got ACTION_UP without ever returning true on intercept, + // the user never started dragging the bottom sheet. + if (!mLauncher.getDragLayer().isEventOverView(mContent, ev)) { + close(true); + return false; + } + } + + if (mNoIntercept) { + return false; + } + + int directionsToDetectScroll = mSwipeDetector.isIdleState() ? + SwipeDetector.DIRECTION_NEGATIVE : 0; + mSwipeDetector.setDetectableScrollConditions( + directionsToDetectScroll, false); + mSwipeDetector.onTouchEvent(ev); + return mSwipeDetector.isDraggingOrSettling(); + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + return mSwipeDetector.onTouchEvent(ev); + } + + /* SwipeDetector.Listener */ + + @Override + public void onDragStart(boolean start) { } + + @Override + public boolean onDrag(float displacement, float velocity) { + float range = mContent.getHeight(); + displacement = Utilities.boundToRange(displacement, 0, range); + setTranslationShift(displacement / range); + return true; + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + if ((fling && velocity > 0) || mTranslationShift > 0.5f) { + mScrollInterpolator.setVelocityAtZero(velocity); + mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration( + velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift)); + close(true); + } else { + mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat( + TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); + mOpenCloseAnimator.setDuration( + SwipeDetector.calculateDuration(velocity, mTranslationShift)) + .setInterpolator(new DecelerateInterpolator()); + mOpenCloseAnimator.start(); + } + } + + protected void handleClose(boolean animate, long defaultDuration) { + if (!mIsOpen || mOpenCloseAnimator.isRunning()) { + return; + } + if (animate) { + mOpenCloseAnimator.setValues( + PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED)); + mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onCloseComplete(); + } + }); + if (mSwipeDetector.isIdleState()) { + mOpenCloseAnimator + .setDuration(defaultDuration) + .setInterpolator(new AccelerateInterpolator()); + } else { + mOpenCloseAnimator.setInterpolator(mScrollInterpolator); + } + mOpenCloseAnimator.start(); + } else { + setTranslationShift(TRANSLATION_SHIFT_CLOSED); + onCloseComplete(); + } + } + + protected void onCloseComplete() { + mIsOpen = false; + mLauncher.getDragLayer().removeView(this); + mLauncher.getSystemUiController().updateUiState( + SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); + } + + protected void setupNavBarColor() { + boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); + mLauncher.getSystemUiController().updateUiState( + SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, + isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV); + } + + @Override + public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { + targetParent.containerType = ContainerType.WIDGETS; + } + + @Override + public final void logActionCommand(int command) { + // TODO: be more specific + mLauncher.getUserEventDispatcher().logActionCommand(command, ContainerType.WIDGETS); + } +} diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 40dbd523ca..2ba55ab977 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -75,6 +75,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { protected CancellationSignal mActiveRequest; private boolean mAnimatePreview = true; + private boolean mApplyBitmapDeferred = false; + private Bitmap mDeferredBitmap; + protected final BaseActivity mActivity; public WidgetCell(Context context) { @@ -150,15 +153,31 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return mWidgetImage; } + /** + * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but + * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are + * ready. + * This prevents invalidates while the animation is running. + */ + public void setApplyBitmapDeferred(boolean isDeferred) { + if (mApplyBitmapDeferred != isDeferred) { + mApplyBitmapDeferred = isDeferred; + if (!mApplyBitmapDeferred && mDeferredBitmap != null) { + applyPreview(mDeferredBitmap); + mDeferredBitmap = null; + } + } + } + public void setAnimatePreview(boolean shouldAnimate) { mAnimatePreview = shouldAnimate; } public void applyPreview(Bitmap bitmap) { - applyPreview(bitmap, true); - } - - public void applyPreview(Bitmap bitmap, boolean animate) { + if (mApplyBitmapDeferred) { + mDeferredBitmap = bitmap; + return; + } if (bitmap != null) { mWidgetImage.setBitmap(bitmap, DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext())); @@ -173,15 +192,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } public void ensurePreview() { - ensurePreview(true); - } - - public void ensurePreview(boolean animate) { if (mActiveRequest != null) { return; } mActiveRequest = mWidgetPreviewLoader.getPreview( - mItem, mPresetPreviewSize, mPresetPreviewSize, this, animate); + mItem, mPresetPreviewSize, mPresetPreviewSize, this); } @Override diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 7aa50a4457..201bd1c9c0 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -16,62 +16,38 @@ package com.android.launcher3.widget; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.content.Context; 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.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.TextView; -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.DropTarget; import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.PropertyListBuilder; -import com.android.launcher3.dragndrop.DragController; -import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.util.SystemUiController; -import com.android.launcher3.util.Themes; import java.util.List; /** * Bottom sheet for the "Widgets" system shortcut in the long-press popup. */ -public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, - SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener, - DragController.DragListener { +public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { - private int mTranslationYOpen; - private int mTranslationYClosed; - private float mTranslationYRange; - - private Launcher mLauncher; + private static final int DEFAULT_CLOSE_DURATION = 200; private ItemInfo mOriginalItemInfo; - private ObjectAnimator mOpenCloseAnimator; private Interpolator mFastOutSlowInInterpolator; - private SwipeDetector.ScrollInterpolator mScrollInterpolator; private Rect mInsets; - private SwipeDetector mSwipeDetector; - private GradientView mGradientBackground; public WidgetsBottomSheet(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -80,23 +56,20 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setWillNotDraw(false); - mLauncher = Launcher.getLauncher(context); - mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); - mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); mInsets = new Rect(); - mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); - mGradientBackground = (GradientView) mLauncher.getLayoutInflater().inflate( + + mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate( R.layout.gradient_bg, mLauncher.getDragLayer(), false); + mGradientView.setProgress(1, false); + mContent = this; } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - mTranslationYOpen = 0; - mTranslationYClosed = getMeasuredHeight(); - mTranslationYRange = mTranslationYClosed - mTranslationYOpen; + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setTranslationShift(mTranslationShift); } public void populateAndShow(ItemInfo itemInfo) { @@ -106,21 +79,21 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab onWidgetsBound(); - mLauncher.getDragLayer().addView(mGradientBackground); + mLauncher.getDragLayer().addView(mGradientView); mLauncher.getDragLayer().addView(this); - measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - setTranslationY(mTranslationYClosed); mIsOpen = false; open(true); } @Override protected void onWidgetsBound() { - List widgets = mLauncher.getWidgetsForPackageUser(new PackageUserKey( - mOriginalItemInfo.getTargetComponent().getPackageName(), mOriginalItemInfo.user)); + List widgets = mLauncher.getPopupDataProvider().getWidgetsForPackageUser( + new PackageUserKey( + mOriginalItemInfo.getTargetComponent().getPackageName(), + mOriginalItemInfo.user)); - ViewGroup widgetRow = (ViewGroup) findViewById(R.id.widgets); - ViewGroup widgetCells = (ViewGroup) widgetRow.findViewById(R.id.widgets_cell_list); + ViewGroup widgetRow = findViewById(R.id.widgets); + ViewGroup widgetCells = widgetRow.findViewById(R.id.widgets_cell_list); widgetCells.removeAllViews(); @@ -166,72 +139,31 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab return widget; } - @Override - public void onClick(View view) { - mLauncher.getWidgetsView().handleClick(); - } - - @Override - public boolean onLongClick(View view) { - mLauncher.getDragController().addDragListener(this); - return mLauncher.getWidgetsView().handleLongClick(view); - } - private void open(boolean animate) { if (mIsOpen || mOpenCloseAnimator.isRunning()) { return; } mIsOpen = true; - boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); - mLauncher.getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, - isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV); + setupNavBarColor(); if (animate) { - mOpenCloseAnimator.setValues(new PropertyListBuilder() - .translationY(mTranslationYOpen).build()); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeDetector.finishedScrolling(); - } - }); + mOpenCloseAnimator.setValues( + PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator); mOpenCloseAnimator.start(); } else { - setTranslationY(mTranslationYOpen); + setTranslationShift(TRANSLATION_SHIFT_OPENED); } } @Override protected void handleClose(boolean animate) { - if (!mIsOpen || mOpenCloseAnimator.isRunning()) { - return; - } - if (animate) { - mOpenCloseAnimator.setValues(new PropertyListBuilder() - .translationY(mTranslationYClosed).build()); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeDetector.finishedScrolling(); - onCloseComplete(); - } - }); - mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState() - ? mFastOutSlowInInterpolator : mScrollInterpolator); - mOpenCloseAnimator.start(); - } else { - setTranslationY(mTranslationYClosed); - onCloseComplete(); - } + handleClose(animate, DEFAULT_CLOSE_DURATION); } - private void onCloseComplete() { - mIsOpen = false; - mLauncher.getDragLayer().removeView(mGradientBackground); - mLauncher.getDragLayer().removeView(WidgetsBottomSheet.this); - mLauncher.getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); + @Override + protected void onCloseComplete() { + super.onCloseComplete(); + mLauncher.getDragLayer().removeView(mGradientView); } @Override @@ -249,83 +181,4 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab setPadding(getPaddingLeft() + leftInset, getPaddingTop(), getPaddingRight() + rightInset, getPaddingBottom() + bottomInset); } - - /* SwipeDetector.Listener */ - - @Override - public void onDragStart(boolean start) { - } - - @Override - public boolean onDrag(float displacement, float velocity) { - setTranslationY(Utilities.boundToRange(displacement, mTranslationYOpen, - mTranslationYClosed)); - return true; - } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - if (mGradientBackground == null) return; - float p = (mTranslationYClosed - translationY) / mTranslationYRange; - boolean showScrim = p <= 0; - mGradientBackground.setProgress(p, showScrim); - } - - @Override - public void onDragEnd(float velocity, boolean fling) { - if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) { - mScrollInterpolator.setVelocityAtZero(velocity); - mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity, - (mTranslationYClosed - getTranslationY()) / mTranslationYRange)); - close(true); - } else { - mIsOpen = false; - mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity, - (getTranslationY() - mTranslationYOpen) / mTranslationYRange)); - open(true); - } - } - - @Override - public void logActionCommand(int command) { - // TODO: be more specific - mLauncher.getUserEventDispatcher().logActionCommand(command, ContainerType.WIDGETS); - } - - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { - return mSwipeDetector.onTouchEvent(ev); - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_UP) { - // If we got ACTION_UP without ever returning true on intercept, - // the user never started dragging the bottom sheet. - if (!mLauncher.getDragLayer().isEventOverView(this, ev)) { - close(true); - return false; - } - } - - int directionsToDetectScroll = mSwipeDetector.isIdleState() ? - SwipeDetector.DIRECTION_NEGATIVE : 0; - mSwipeDetector.setDetectableScrollConditions( - directionsToDetectScroll, false); - mSwipeDetector.onTouchEvent(ev); - return mSwipeDetector.isDraggingOrSettling(); - } - - /* DragListener */ - - @Override - public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { - // A widget or custom shortcut was dragged. - close(true); - } - - @Override - public void onDragEnd() { - } } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java deleted file mode 100644 index 39dd0d498e..0000000000 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ /dev/null @@ -1,243 +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.pm.LauncherApps; -import android.graphics.Point; -import android.support.v7.widget.LinearLayoutManager; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.android.launcher3.BaseContainerView; -import com.android.launcher3.DeleteDropTarget; -import com.android.launcher3.DragSource; -import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.folder.Folder; -import com.android.launcher3.model.PackageItemInfo; -import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.util.MultiHashMap; -import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.util.Thunk; - -import java.util.List; - -/** - * The widgets list view container. - */ -public class WidgetsContainerView extends BaseContainerView - implements View.OnLongClickListener, View.OnClickListener, DragSource { - private static final String TAG = "WidgetsContainerView"; - private static final boolean LOGD = false; - - /* Global instances that are used inside this container. */ - @Thunk Launcher mLauncher; - - /* Recycler view related member variables */ - private WidgetsRecyclerView mRecyclerView; - private WidgetsListAdapter mAdapter; - - /* Touch handling related member variables. */ - private Toast mWidgetInstructionToast; - - 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); - mLauncher = Launcher.getLauncher(context); - LauncherAppState apps = LauncherAppState.getInstance(context); - mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context), - apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this, - new WidgetsDiffReporter(apps.getIconCache())); - mAdapter.setNotifyListener(); - if (LOGD) { - Log.d(TAG, "WidgetsContainerView constructor"); - } - } - - @Override - public View getTouchDelegateTargetView() { - return mRecyclerView; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view); - mRecyclerView.setAdapter(mAdapter); - mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - } - - // - // Returns views used for launcher transitions. - // - - public void scrollToTop() { - mRecyclerView.scrollToPosition(0); - } - - // - // 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; - - handleClick(); - } - - public void handleClick() { - // Let the user know that they have to long press to add a widget - if (mWidgetInstructionToast != null) { - mWidgetInstructionToast.cancel(); - } - - CharSequence msg = Utilities.wrapForTts( - getContext().getText(R.string.long_press_widget_to_add), - getContext().getString(R.string.long_accessible_way_to_add)); - mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT); - mWidgetInstructionToast.show(); - } - - @Override - public boolean onLongClick(View v) { - // When we have exited the widget tray, disregard long clicks - if (!mLauncher.isWidgetsViewVisible()) return false; - return handleLongClick(v); - } - - public boolean handleLongClick(View v) { - if (LOGD) { - Log.d(TAG, String.format("onLongClick [v=%s]", v)); - } - // When we are in transition, disregard long clicks - if (mLauncher.getWorkspace().isSwitchingState()) return false; - // Return if global dragging is not enabled - 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 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; - } - - private boolean beginDraggingWidget(WidgetCell v) { - // Get the widget preview as the drag representation - WidgetImageView image = (WidgetImageView) v.findViewById(R.id.widget_preview); - - // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and - // we abort the drag. - if (image.getBitmap() == null) { - return false; - } - - int[] loc = new int[2]; - mLauncher.getDragLayer().getLocationInDragLayer(image, loc); - - new PendingItemDragHelper(v).startDrag( - image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(), - new Point(loc[0], loc[1]), this, new DragOptions()); - return true; - } - - // - // Drag related handling methods that implement {@link DragSource} interface. - // - - @Override - public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, - boolean success) { - if (LOGD) { - Log.d(TAG, "onDropCompleted"); - } - 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); - - if (!success) { - d.deferDragViewCleanupPostAnimation = false; - } - } - - /** - * Initialize the widget data model. - */ - public void setWidgets(MultiHashMap model) { - mAdapter.setWidgets(model); - - View loader = getContentView().findViewById(R.id.loader); - if (loader != null) { - ((ViewGroup) getContentView()).removeView(loader); - } - } - - public boolean isEmpty() { - return mAdapter.getItemCount() == 0; - } - - public List getWidgetsForPackageUser(PackageUserKey packageUserKey) { - return mAdapter.copyWidgetsForPackageUser(packageUserKey); - } - - @Override - public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { - targetParent.containerType = ContainerType.WIDGETS; - } -} \ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java index 52deec32b0..d67f403655 100644 --- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java +++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java @@ -16,6 +16,7 @@ package com.android.launcher3.widget; +import android.support.v7.widget.RecyclerView; import android.util.Log; import com.android.launcher3.IconCache; @@ -26,26 +27,18 @@ import java.util.ArrayList; import java.util.Iterator; /** - * Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly. + * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter} + * methods accordingly. */ public class WidgetsDiffReporter { - private final boolean DEBUG = false; - private final String TAG = "WidgetsDiffReporter"; + private static final boolean DEBUG = false; + private static final String TAG = "WidgetsDiffReporter"; + private final IconCache mIconCache; - private NotifyListener mListener; + private final RecyclerView.Adapter mListener; - public interface NotifyListener { - void notifyDataSetChanged(); - void notifyItemChanged(int index); - void notifyItemInserted(int index); - void notifyItemRemoved(int index); - } - - public WidgetsDiffReporter(IconCache iconCache) { + public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) { mIconCache = iconCache; - } - - public void setListener(NotifyListener listener) { mListener = listener; } @@ -55,9 +48,17 @@ public class WidgetsDiffReporter { Log.d(TAG, "process oldEntries#=" + currentEntries.size() + " newEntries#=" + newEntries.size()); } - if (currentEntries.size() == 0 && newEntries.size() > 0) { - currentEntries.addAll(newEntries); - mListener.notifyDataSetChanged(); + // Early exit if either of the list is empty + if (currentEntries.isEmpty() || newEntries.isEmpty()) { + // Skip if both list are empty. + // On rotation, we open the widget tray with empty. Then try to fetch the list again + // when the animation completes (which still gives empty). And we get the final result + // when the bind actually completes. + if (currentEntries.size() != newEntries.size()) { + currentEntries.clear(); + currentEntries.addAll(newEntries); + mListener.notifyDataSetChanged(); + } return; } ArrayList orgEntries = diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java new file mode 100644 index 0000000000..72277a2536 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.PropertyValuesHolder; +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AnimationUtils; + +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; +import com.android.launcher3.R; + +/** + * Popup for showing the full list of available widgets + */ +public class WidgetsFullSheet extends BaseWidgetSheet + implements Insettable, ProviderChangedListener { + + private static final long DEFAULT_OPEN_DURATION = 267; + private static final long FADE_IN_DURATION = 150; + private static final float VERTICAL_START_POSITION = 0.3f; + + private static final Rect sTempRect = new Rect(); + + private final Rect mInsets = new Rect(); + + private final WidgetsListAdapter mAdapter; + + private View mNavBarScrim; + private WidgetsRecyclerView mRecyclerView; + + public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + LauncherAppState apps = LauncherAppState.getInstance(context); + mAdapter = new WidgetsListAdapter(context, + LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(), + this, this); + } + + public WidgetsFullSheet(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mContent = findViewById(R.id.container); + mNavBarScrim = findViewById(R.id.nav_bar_bg); + + mRecyclerView = findViewById(R.id.widgets_list_view); + mRecyclerView.setAdapter(mAdapter); + mAdapter.setApplyBitmapDeferred(true, mRecyclerView); + + mGradientView = findViewById(R.id.gradient_bg); + mGradientView.setProgress(1, false); + + onWidgetsBound(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mLauncher.getAppWidgetHost().addProviderChangeListener(this); + notifyWidgetProvidersChanged(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mLauncher.getAppWidgetHost().removeProviderChangeListener(this); + } + + @Override + public void setInsets(Rect insets) { + mInsets.set(insets); + + mNavBarScrim.getLayoutParams().height = insets.bottom; + mRecyclerView.setPadding( + mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(), + mRecyclerView.getPaddingRight(), insets.bottom); + if (insets.bottom > 0) { + setupNavBarColor(); + } + requestLayout(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthUsed; + if (mInsets.bottom > 0) { + // If we have bottom insets, we do not show the scrim as it would overlap + // with the navbar scrim + mGradientView.setVisibility(View.INVISIBLE); + widthUsed = 0; + } else { + mLauncher.getDeviceProfile().getWorkspacePadding(sTempRect); + widthUsed = Math.max(sTempRect.left + sTempRect.right, + 2 * (mInsets.left + mInsets.right)); + } + + int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx; + measureChildWithMargins(mContent, widthMeasureSpec, + widthUsed, heightMeasureSpec, heightUsed); + measureChild(mGradientView, widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(mGradientView.getMeasuredWidth(), mGradientView.getMeasuredHeight()); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int width = r - l; + int height = b - t; + mGradientView.layout(0, 0, width, height); + + // Content is laid out as center bottom aligned + int contentWidth = mContent.getMeasuredWidth(); + int contentLeft = (width - contentWidth) / 2; + mContent.layout(contentLeft, height - mContent.getMeasuredHeight(), + contentLeft + contentWidth, height); + + setTranslationShift(mTranslationShift); + } + + @Override + public void notifyWidgetProvidersChanged() { + mLauncher.refreshAndBindWidgetsForPackageUser(null); + } + + @Override + protected void onWidgetsBound() { + mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets()); + } + + private void open(boolean animate) { + if (mIsOpen) { + return; + } + mIsOpen = true; + if (animate) { + if (mLauncher.getDragLayer().getInsets().bottom > 0) { + mContent.setAlpha(0); + setTranslationShift(VERTICAL_START_POSITION); + } + mOpenCloseAnimator.setValues( + PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); + mOpenCloseAnimator + .setDuration(DEFAULT_OPEN_DURATION) + .setInterpolator(AnimationUtils.loadInterpolator( + getContext(), android.R.interpolator.linear_out_slow_in)); + mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRecyclerView.setLayoutFrozen(false); + mAdapter.setApplyBitmapDeferred(false, mRecyclerView); + mOpenCloseAnimator.removeListener(this); + } + }); + post(new Runnable() { + @Override + public void run() { + mRecyclerView.setLayoutFrozen(true); + mOpenCloseAnimator.start(); + mContent.animate().alpha(1).setDuration(FADE_IN_DURATION); + } + }); + } else { + setTranslationShift(TRANSLATION_SHIFT_OPENED); + mAdapter.setApplyBitmapDeferred(false, mRecyclerView); + } + } + + @Override + protected void handleClose(boolean animate) { + handleClose(animate, DEFAULT_OPEN_DURATION); + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_WIDGETS_FULL_SHEET) != 0; + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + // Disable swipe down when recycler view is scrolling + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mNoIntercept = false; + if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) { + mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer()); + } + } + return super.onControllerInterceptTouchEvent(ev); + } + + public static WidgetsFullSheet show(Launcher launcher, boolean animate) { + WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater() + .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false); + launcher.getDragLayer().addView(sheet); + sheet.open(animate); + return sheet; + } +} diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 6b1800c676..0147ea4278 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -25,13 +25,11 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import com.android.launcher3.IconCache; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; -import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.LabelComparator; -import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; @@ -39,7 +37,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import java.util.Map; /** * List view adapter for the widget tray. @@ -56,7 +53,6 @@ public class WidgetsListAdapter extends Adapter { private final WidgetPreviewLoader mWidgetPreviewLoader; private final LayoutInflater mLayoutInflater; - private final AlphabeticIndexCompat mIndexer; private final OnClickListener mIconClickListener; private final OnLongClickListener mIconLongClickListener; @@ -64,56 +60,43 @@ public class WidgetsListAdapter extends Adapter { private ArrayList mEntries = new ArrayList<>(); private final WidgetsDiffReporter mDiffReporter; + private boolean mApplyBitmapDeferred; + public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, - WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat, - OnClickListener iconClickListener, OnLongClickListener iconLongClickListener, - WidgetsDiffReporter diffReporter) { + WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache, + OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) { mLayoutInflater = layoutInflater; mWidgetPreviewLoader = widgetPreviewLoader; - mIndexer = indexCompat; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent); - mDiffReporter = diffReporter; + mDiffReporter = new WidgetsDiffReporter(iconCache, this); } - public void setNotifyListener() { - mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() { - @Override - public void notifyDataSetChanged() { - WidgetsListAdapter.this.notifyDataSetChanged(); - } + /** + * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv} + * + * @see WidgetCell#setApplyBitmapDeferred(boolean) + */ + public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) { + mApplyBitmapDeferred = isDeferred; - @Override - public void notifyItemChanged(int index) { - WidgetsListAdapter.this.notifyItemChanged(index); + for (int i = rv.getChildCount() - 1; i >= 0; i--) { + WidgetsRowViewHolder holder = (WidgetsRowViewHolder) + rv.getChildViewHolder(rv.getChildAt(i)); + for (int j = holder.cellContainer.getChildCount() - 1; j >= 0; j--) { + View v = holder.cellContainer.getChildAt(j); + if (v instanceof WidgetCell) { + ((WidgetCell) v).setApplyBitmapDeferred(mApplyBitmapDeferred); + } } - - @Override - public void notifyItemInserted(int index) { - WidgetsListAdapter.this.notifyItemInserted(index); - } - - @Override - public void notifyItemRemoved(int index) { - WidgetsListAdapter.this.notifyItemRemoved(index); - } - }); + } } /** * Update the widget list. */ - public void setWidgets(MultiHashMap widgets) { - ArrayList tempEntries = new ArrayList<>(); - - WidgetItemComparator widgetComparator = new WidgetItemComparator(); - for (Map.Entry> entry : widgets.entrySet()) { - WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue()); - row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title); - Collections.sort(row.widgets, widgetComparator); - tempEntries.add(row); - } + public void setWidgets(ArrayList tempEntries) { WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator(); Collections.sort(tempEntries, rowComparator); mDiffReporter.process(mEntries, tempEntries, rowComparator); @@ -128,26 +111,6 @@ public class WidgetsListAdapter extends Adapter { return mEntries.get(pos).titleSectionName; } - /** - * Copies and returns the widgets associated with the package and user of the ComponentKey. - */ - public List copyWidgetsForPackageUser(PackageUserKey packageUserKey) { - for (WidgetListRowEntry entry : mEntries) { - if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) { - ArrayList widgets = new ArrayList<>(entry.widgets); - // Remove widgets not associated with the correct user. - Iterator iterator = widgets.iterator(); - while (iterator.hasNext()) { - if (!iterator.next().user.equals(packageUserKey.mUser)) { - iterator.remove(); - } - } - return widgets.isEmpty() ? null : widgets; - } - } - return null; - } - @Override public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) { WidgetListRowEntry entry = mEntries.get(pos); @@ -194,6 +157,7 @@ public class WidgetsListAdapter extends Adapter { for (int i=0; i < infoList.size(); i++) { WidgetCell widget = (WidgetCell) row.getChildAt(2*i); widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader); + widget.setApplyBitmapDeferred(mApplyBitmapDeferred); widget.ensurePreview(); widget.setVisibility(View.VISIBLE); @@ -253,5 +217,4 @@ public class WidgetsListAdapter extends Adapter { return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString()); } } - } diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 9730a82aad..89c88a4e7f 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -17,12 +17,12 @@ package com.android.launcher3.widget; import android.content.Context; -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.R; /** * The widgets recycler view. @@ -32,6 +32,8 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsListAdapter mAdapter; + private final int mScrollbarTop; + public WidgetsRecyclerView(Context context) { this(context, null); } @@ -43,6 +45,7 @@ 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); + mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); } public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, @@ -130,13 +133,16 @@ public class WidgetsRecyclerView extends BaseRecyclerView { @Override protected int getAvailableScrollHeight() { View child = getChildAt(0); - int height = child.getMeasuredHeight() * mAdapter.getItemCount(); - int totalHeight = getPaddingTop() + height + getPaddingBottom(); - int availableScrollHeight = totalHeight - getScrollbarTrackHeight(); - return availableScrollHeight; + return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight() + - mScrollbarTop; } private boolean isModelNotReady() { return mAdapter.getItemCount() == 0; } + + @Override + public int getScrollBarTop() { + return mScrollbarTop; + } } \ No newline at end of file diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java index 40b65e4fb5..0185f130e5 100644 --- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java +++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java @@ -22,13 +22,13 @@ import android.graphics.Bitmap; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import com.android.launcher3.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.WidgetPreviewLoader; -import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.WidgetItem; @@ -40,8 +40,11 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; +import java.util.ArrayList; +import java.util.Map; + +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -49,15 +52,12 @@ import static org.mockito.Mockito.verify; @RunWith(AndroidJUnit4.class) public class WidgetsListAdapterTest { - private final String TAG = "WidgetsListAdapterTest"; - @Mock private LayoutInflater mMockLayoutInflater; @Mock private WidgetPreviewLoader mMockWidgetCache; - @Mock private WidgetsDiffReporter.NotifyListener mListener; + @Mock private RecyclerView.AdapterDataObserver mListener; @Mock private IconCache mIconCache; private WidgetsListAdapter mAdapter; - private AlphabeticIndexCompat mIndexCompat; private InvariantDeviceProfile mTestProfile; private Context mContext; @@ -68,41 +68,39 @@ public class WidgetsListAdapterTest { mTestProfile = new InvariantDeviceProfile(); mTestProfile.numRows = 5; mTestProfile.numColumns = 5; - mIndexCompat = new AlphabeticIndexCompat(mContext); - WidgetsDiffReporter reporter = new WidgetsDiffReporter(mIconCache); - reporter.setListener(mListener); mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache, - mIndexCompat, null, null, reporter); + mIconCache, null, null); + mAdapter.registerAdapterDataObserver(mListener); } @Test public void test_notifyDataSetChanged() throws Exception { mAdapter.setWidgets(generateSampleMap(1)); - verify(mListener, times(1)).notifyDataSetChanged(); + verify(mListener, times(1)).onChanged(); } @Test public void test_notifyItemInserted() throws Exception { mAdapter.setWidgets(generateSampleMap(1)); mAdapter.setWidgets(generateSampleMap(2)); - verify(mListener, times(1)).notifyDataSetChanged(); - verify(mListener, times(1)).notifyItemInserted(1); + verify(mListener, times(1)).onChanged(); + verify(mListener, times(1)).onItemRangeInserted(eq(1), eq(1)); } @Test public void test_notifyItemRemoved() throws Exception { mAdapter.setWidgets(generateSampleMap(2)); mAdapter.setWidgets(generateSampleMap(1)); - verify(mListener, times(1)).notifyDataSetChanged(); - verify(mListener, times(1)).notifyItemRemoved(1); + verify(mListener, times(1)).onChanged(); + verify(mListener, times(1)).onItemRangeRemoved(eq(1), eq(1)); } @Test public void testNotifyItemChanged_PackageIconDiff() throws Exception { mAdapter.setWidgets(generateSampleMap(1)); mAdapter.setWidgets(generateSampleMap(1)); - verify(mListener, times(1)).notifyDataSetChanged(); - verify(mListener, times(1)).notifyItemChanged(0); + verify(mListener, times(1)).onChanged(); + verify(mListener, times(1)).onItemRangeChanged(eq(0), eq(1), isNull()); } @Test @@ -125,10 +123,11 @@ public class WidgetsListAdapterTest { * @param num the number of WidgetItem the map should contain * @return */ - private MultiHashMap generateSampleMap(int num) { - MultiHashMap newMap = new MultiHashMap(); - if (num <= 0) return newMap; + private ArrayList generateSampleMap(int num) { + ArrayList result = new ArrayList<>(); + if (num <= 0) return result; + MultiHashMap newMap = new MultiHashMap(); PackageManager pm = mContext.getPackageManager(); AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext); for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) { @@ -144,6 +143,10 @@ public class WidgetsListAdapterTest { break; } } - return newMap; + for (Map.Entry> entry : newMap.entrySet()) { + result.add(new WidgetListRowEntry(entry.getKey(), entry.getValue())); + } + + return result; } } From 1797af41d162413dc98c33fab8ba19f96b63874b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 6 Oct 2017 13:29:57 -0700 Subject: [PATCH 050/885] Cleaning up drag state management. When the drag is started, the UI automatically goes into spring loaded mode. On a successful drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode. Bug: 34692289 Change-Id: Ic611739a43bb8d9279b587aaee3039326c143e8b --- .../android/launcher3/ButtonDropTarget.java | 2 +- src/com/android/launcher3/DragSource.java | 2 +- src/com/android/launcher3/DropTarget.java | 12 ++- src/com/android/launcher3/Launcher.java | 58 ++++++++------ .../launcher3/UninstallDropTarget.java | 10 +-- src/com/android/launcher3/Workspace.java | 77 ++++++------------- .../allapps/AllAppsContainerView.java | 19 +---- .../dragndrop/BaseItemDragListener.java | 22 +----- .../launcher3/dragndrop/DragController.java | 20 ++++- .../launcher3/dragndrop/DragLayer.java | 10 +-- .../launcher3/dragndrop/DragOptions.java | 6 -- src/com/android/launcher3/folder/Folder.java | 29 +------ .../android/launcher3/folder/FolderIcon.java | 10 +-- .../popup/PopupContainerWithArrow.java | 10 +-- .../launcher3/util/FlingAnimation.java | 2 +- .../launcher3/widget/BaseWidgetSheet.java | 16 +--- 16 files changed, 111 insertions(+), 194 deletions(-) diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index ffc2b02e2b..e1b5e2c754 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -229,7 +229,7 @@ public abstract class ButtonDropTarget extends TextView public void run() { completeDrop(d); mDropTargetBar.onDragEnd(); - mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null); + mLauncher.exitSpringLoadedDragMode(true, 0); } }; dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java index c6106c23e4..93f865c9f9 100644 --- a/src/com/android/launcher3/DragSource.java +++ b/src/com/android/launcher3/DragSource.java @@ -30,5 +30,5 @@ public interface DragSource extends LogContainerProvider { * A callback made back to the source after an item from this source has been dropped on a * DropTarget. */ - void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, boolean success); + void onDropCompleted(View target, DragObject d, boolean success); } diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 2307b89246..4d30479204 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -59,9 +59,6 @@ public interface DropTarget { /** The object is part of an accessible drag operation */ public boolean accessibleDrag; - /** Post drag animation runnable */ - public Runnable postAnimationRunnable = null; - /** Indicates that the drag operation was cancelled */ public boolean cancelled = false; @@ -105,7 +102,14 @@ public interface DropTarget { boolean isDropEnabled(); /** - * Handle an object being dropped on the DropTarget + * Handle an object being dropped on the DropTarget. + * + * This will be called only if this target previously returned true for {@link #acceptDrop}. It + * is the responsibility of this target to exit out of the spring loaded mode (either + * immediately or after any pending animations). + * + * If the drop was cancelled for some reason, onDrop will never get called, the UI will + * automatically exit out of this mode. */ void onDrop(DragObject dragObject, DragOptions options); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5c635ca513..bfcd82ace6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -582,8 +582,8 @@ public class Launcher extends BaseActivity Runnable exitSpringLoaded = new Runnable() { @Override public void run() { - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), - EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + exitSpringLoadedDragMode((resultCode != RESULT_CANCELED), + EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); } }; @@ -635,7 +635,7 @@ public class Launcher extends BaseActivity final Runnable onComplete = new Runnable() { @Override public void run() { - exitSpringLoadedDragModeDelayed(false, 0, null); + exitSpringLoadedDragMode(false, 0); } }; @@ -763,8 +763,8 @@ public class Launcher extends BaseActivity @Override public void run() { completeAddAppWidget(appWidgetId, requestArgs, layout, null); - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), - EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + exitSpringLoadedDragMode((resultCode != RESULT_CANCELED), + EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); } }; } else if (resultCode == RESULT_CANCELED) { @@ -1231,7 +1231,7 @@ public class Launcher extends BaseActivity // If appropriate, either create a folder or add to an existing folder if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0, - true, null, null)) { + true, null)) { return; } DragObject dragObject = new DragObject(); @@ -1714,8 +1714,8 @@ public class Launcher extends BaseActivity @Override public void run() { // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, - null); + exitSpringLoadedDragMode(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT + ); } }; completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this)); @@ -2598,16 +2598,36 @@ public class Launcher extends BaseActivity return; } + // Lock the orientation: + if (mRotationEnabled) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } + + // Prevent any Un/InstallShortcutReceivers from updating the db while we are + // in spring loaded mode + InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); + mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), Workspace.State.SPRING_LOADED, true /* animated */, null /* onCompleteRunnable */); setState(State.WORKSPACE_SPRING_LOADED); } - public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, + public void exitSpringLoadedDragMode(final boolean successfulDrop, int delay) { + exitSpringLoadedDragMode(successfulDrop, delay, null); + } + + public void exitSpringLoadedDragMode(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { if (!isStateSpringLoaded()) return; + // Unlock rotation lock + unlockScreenOrientation(false); + + // Re-enable any Un/InstallShortcutReceiver and now process any queued items + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_DRAG_AND_DROP, this); + if (mExitSpringLoadedModeRunnable != null) { mHandler.removeCallbacks(mExitSpringLoadedModeRunnable); } @@ -2621,8 +2641,10 @@ public class Launcher extends BaseActivity // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should // clean up our state transition functions showWorkspace(true, onCompleteRunnable); - } else { - exitSpringLoadedDragMode(); + } else if (mState == State.APPS_SPRING_LOADED) { + showAppsView(true /* animated */); + } else if (mState == State.WORKSPACE_SPRING_LOADED) { + showWorkspace(true); } mExitSpringLoadedModeRunnable = null; } @@ -2635,14 +2657,6 @@ public class Launcher extends BaseActivity || mState == State.WIDGETS_SPRING_LOADED; } - public void exitSpringLoadedDragMode() { - if (mState == State.APPS_SPRING_LOADED) { - showAppsView(true /* animated */); - } else if (mState == State.WORKSPACE_SPRING_LOADED) { - showWorkspace(true); - } - } - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); @@ -3398,12 +3412,6 @@ public class Launcher extends BaseActivity mModel.refreshAndBindWidgetsAndShortcuts(packageUser); } - public void lockScreenOrientation() { - if (mRotationEnabled) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); - } - } - public void unlockScreenOrientation(boolean immediate) { if (mRotationEnabled) { if (immediate) { diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index e8c452877e..43938194e9 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -97,9 +97,7 @@ public class UninstallDropTarget extends ButtonDropTarget { @Override public void onDrop(DragObject d, DragOptions options) { // Defer onComplete - if (options.deferCompleteForUninstall) { - d.dragSource = new DeferredOnComplete(d.dragSource, getContext()); - } + d.dragSource = new DeferredOnComplete(d.dragSource, getContext()); super.onDrop(d, options); } @@ -171,7 +169,7 @@ public class UninstallDropTarget extends ButtonDropTarget { } @Override - public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, + public void onDropCompleted(View target, DragObject d, boolean success) { mDragObject = d; } @@ -189,7 +187,7 @@ public class UninstallDropTarget extends ButtonDropTarget { .getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, mDragObject.dragInfo.user) == null) { mDragObject.dragSource = mOriginal; - mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false, true); + mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, true); } else { sendFailure(); } @@ -198,7 +196,7 @@ public class UninstallDropTarget extends ButtonDropTarget { public void sendFailure() { mDragObject.dragSource = mOriginal; mDragObject.cancelled = true; - mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false, false); + mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false); } } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 1f26965044..1525e41d21 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -237,8 +237,6 @@ public class Workspace extends PagedView final WallpaperOffsetInterpolator mWallpaperOffset; private boolean mUnlockWallpaperFromDefaultPageOnLayout; - @Thunk Runnable mDelayedResizeRunnable; - // Variables relating to the creation of user folders by hovering shortcuts over shortcuts private static final int FOLDER_CREATION_TIMEOUT = 0; public static final int REORDER_TIMEOUT = 350; @@ -401,9 +399,6 @@ public class Workspace extends PagedView } updateChildrenLayersEnabled(); - mLauncher.lockScreenOrientation(); - // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging - InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); // Do not add a new page if it is a accessible drag which was not started by the workspace. // We do not support accessibility drag from other sources and instead provide a direct @@ -453,12 +448,6 @@ public class Workspace extends PagedView } updateChildrenLayersEnabled(); - mLauncher.unlockScreenOrientation(false); - - // Re-enable any Un/InstallShortcutReceiver and now process any queued items - InstallShortcutReceiver.disableAndFlushInstallQueue( - InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext()); - mOutlineProvider = null; mDragSourceInternal = null; } @@ -1087,11 +1076,6 @@ public class Workspace extends PagedView } } - if (mDelayedResizeRunnable != null && !mIsSwitchingState) { - mDelayedResizeRunnable.run(); - mDelayedResizeRunnable = null; - } - if (mStripScreensOnPageStopMoving) { stripEmptyScreens(); mStripScreensOnPageStopMoving = false; @@ -1744,7 +1728,6 @@ public class Workspace extends PagedView } }); } - options.deferCompleteForUninstall = true; beginDragShared(child, this, options); } @@ -1960,8 +1943,7 @@ public class Workspace extends PagedView } boolean createUserFolderIfNecessary(View newView, long container, CellLayout target, - int[] targetCell, float distance, boolean external, DragView dragView, - Runnable postAnimationRunnable) { + int[] targetCell, float distance, boolean external, DragView dragView) { if (distance > mMaxDistanceForFolderCreation) return false; View v = target.getChildAt(targetCell[0], targetCell[1]); @@ -2005,8 +1987,8 @@ public class Workspace extends PagedView // folder background to the newly created icon. This preserves animation state. fi.setFolderBackground(mFolderCreateBg); mFolderCreateBg = new PreviewBackground(); - fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale, - postAnimationRunnable); + fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale + ); } else { fi.prepareCreateAnimation(v); fi.addItem(destInfo); @@ -2060,13 +2042,14 @@ public class Workspace extends PagedView int snapScreen = -1; boolean resizeOnDrop = false; - if (d.dragSource != this) { + if (d.dragSource != this || mDragInfo == null) { final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1] }; onDropExternal(touchXY, dropTargetLayout, d); - } else if (mDragInfo != null) { + } else { final View cell = mDragInfo.cell; boolean droppedOnOriginalCellDuringTransition = false; + Runnable onCompleteRunnable = null; if (dropTargetLayout != null && !d.cancelled) { // Move internally @@ -2090,12 +2073,11 @@ public class Workspace extends PagedView // If the item being dropped is a shortcut and the nearest drop // cell also contains a shortcut, then create a folder with the two shortcuts. if (createUserFolderIfNecessary(cell, container, - dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) { - return; - } - - if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, - distance, d, false)) { + dropTargetLayout, mTargetCell, distance, false, d.dragView) || + addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, + distance, d, false)) { + mLauncher.exitSpringLoadedDragMode(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); return; } @@ -2177,7 +2159,7 @@ public class Workspace extends PagedView AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo(); if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE && !d.accessibleDrag) { - mDelayedResizeRunnable = new Runnable() { + onCompleteRunnable = new Runnable() { public void run() { if (!isPageInTransition()) { AppWidgetResizeFrame.showForWidget(hostView, cellLayout); @@ -2209,9 +2191,9 @@ public class Workspace extends PagedView // Animate the item to its original position, while simultaneously exiting // spring-loaded mode so the page meets the icon where it was picked up. mLauncher.getDragController().animateDragViewToOriginalPosition( - mDelayedResizeRunnable, cell, + onCompleteRunnable, cell, mStateTransitionAnimation.mSpringLoadedTransitionTime); - mLauncher.exitSpringLoadedDragMode(); + mLauncher.exitSpringLoadedDragMode(false, 0); mLauncher.getDropTargetBar().onDragEnd(); parent.onDropChild(cell); return; @@ -2226,14 +2208,18 @@ public class Workspace extends PagedView } else { int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, - null, this); + this); } } else { d.deferDragViewCleanupPostAnimation = false; cell.setVisibility(VISIBLE); } parent.onDropChild(cell); + + mLauncher.exitSpringLoadedDragMode(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, onCompleteRunnable); } + if (d.stateAnnouncer != null && !droppedOnOriginalCell) { d.stateAnnouncer.completeAction(R.string.item_moved); } @@ -2741,14 +2727,6 @@ public class Workspace extends PagedView * to add an item to one of the workspace screens. */ private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) { - final Runnable exitSpringLoadedRunnable = new Runnable() { - @Override - public void run() { - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - } - }; - if (d.dragInfo instanceof PendingAddShortcutInfo) { ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo) .activityInfo.createShortcutInfo(); @@ -2846,6 +2824,9 @@ public class Workspace extends PagedView animationStyle, finalView, true); } else { // This is for other drag/drop cases, like dragging from All Apps + mLauncher.exitSpringLoadedDragMode(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + View view; switch (info.itemType) { @@ -2874,9 +2855,8 @@ public class Workspace extends PagedView cellLayout, mTargetCell); float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); - d.postAnimationRunnable = exitSpringLoadedRunnable; if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance, - true, d.dragView, d.postAnimationRunnable)) { + true, d.dragView)) { return; } if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d, @@ -2909,7 +2889,7 @@ public class Workspace extends PagedView // the correct final location. setFinalTransitionTransform(cellLayout); mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, - exitSpringLoadedRunnable, this); + this); resetTransitionTransform(cellLayout); } } @@ -3085,7 +3065,7 @@ public class Workspace extends PagedView * Called at the end of a drag which originated on the workspace. */ public void onDropCompleted(final View target, final DragObject d, - final boolean isFlingToDelete, final boolean success) { + final boolean success) { if (success) { if (target != this && mDragInfo != null) { @@ -3105,13 +3085,6 @@ public class Workspace extends PagedView mDragInfo.cell.setVisibility(VISIBLE); } mDragInfo = null; - - if (!isFlingToDelete) { - // Fling to delete already exits spring loaded mode after the animation finishes. - mLauncher.exitSpringLoadedDragModeDelayed(success, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, mDelayedResizeRunnable); - mDelayedResizeRunnable = null; - } } /** diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 91b196eeaa..4de31f4c27 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -31,10 +31,10 @@ import android.widget.RelativeLayout; import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; import com.android.launcher3.ClickShadowView; -import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; +import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; @@ -44,7 +44,6 @@ import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.folder.Folder; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.PackageUserKey; @@ -322,21 +321,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource, } @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); - - if (!success) { - d.deferDragViewCleanupPostAnimation = false; - } - } + public void onDropCompleted(View target, DragObject d, boolean success) { } @Override public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java index 727fb51ce2..96aaee03b3 100644 --- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java @@ -28,12 +28,10 @@ import android.util.Log; import android.view.DragEvent; import android.view.View; -import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DragSource; -import com.android.launcher3.DropTarget; +import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.folder.Folder; import com.android.launcher3.widget.PendingItemDragHelper; import java.util.UUID; @@ -141,7 +139,7 @@ public abstract class BaseItemDragListener implements } @Override - public void onPreDragStart(DropTarget.DragObject dragObject) { + public void onPreDragStart(DragObject dragObject) { // The predrag starts when the workspace is not yet loaded. In some cases we set // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the // dragView from being visible. Instead just skip the fade-in animation here. @@ -152,26 +150,14 @@ public abstract class BaseItemDragListener implements } @Override - public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { + public void onPreDragEnd(DragObject dragObject, boolean dragStarted) { if (dragStarted) { dragObject.dragView.setColor(0); } } @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); - } - - if (!success) { - d.deferDragViewCleanupPostAnimation = false; - } + public void onDropCompleted(View target, DragObject d, boolean success) { postCleanup(); } diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index db8f903afd..31255b7f06 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -120,6 +120,9 @@ public class DragController implements DragDriver.EventListener, TouchController /** * Starts a drag. + * When the drag is started, the UI automatically goes into spring loaded mode. On a successful + * drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded + * mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode. * * @param b The bitmap to display as the drag image. It will be re-scaled to the * enlarged size. @@ -248,12 +251,24 @@ public class DragController implements DragDriver.EventListener, TouchController mDragObject.cancelled = true; mDragObject.dragComplete = true; if (!mIsInPreDrag) { - mDragObject.dragSource.onDropCompleted(null, mDragObject, false, false); + dispatchDropComplete(null, false); } } endDrag(); } + private void dispatchDropComplete(View dropTarget, boolean accepted) { + if (!accepted) { + // If it was not accepted, cleanup the state. If it was accepted, it is the + // responsibility of the drop target to cleanup the state. + mLauncher.exitSpringLoadedDragMode(false, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + mDragObject.deferDragViewCleanupPostAnimation = false; + } + + mDragObject.dragSource.onDropCompleted(dropTarget, mDragObject, accepted); + } + public void onAppsRemoved(ItemInfoMatcher matcher) { // Cancel the current drag if we are removing an app that we are dragging if (mDragObject != null) { @@ -583,8 +598,7 @@ public class DragController implements DragDriver.EventListener, TouchController final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null; if (!mIsInPreDrag) { mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView); - mDragObject.dragSource.onDropCompleted( - dropTargetAsView, mDragObject, flingAnimation != null, accepted); + dispatchDropComplete(dropTargetAsView, accepted); } } diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 60ce3c36a6..95223c3f78 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -491,13 +491,12 @@ public class DragLayer extends InsettableFrameLayout { onFinishRunnable, animationEndStyle, duration, null); } - public void animateViewIntoPosition(DragView dragView, final View child, - final Runnable onFinishAnimationRunnable, View anchorView) { - animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, anchorView); + public void animateViewIntoPosition(DragView dragView, final View child, View anchorView) { + animateViewIntoPosition(dragView, child, -1, anchorView); } public void animateViewIntoPosition(DragView dragView, final View child, int duration, - final Runnable onFinishAnimationRunnable, View anchorView) { + View anchorView) { ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent(); CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); parentChildren.measureChild(child); @@ -554,9 +553,6 @@ public class DragLayer extends InsettableFrameLayout { Runnable onCompleteRunnable = new Runnable() { public void run() { child.setVisibility(VISIBLE); - if (onFinishAnimationRunnable != null) { - onFinishAnimationRunnable.run(); - } } }; animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale, diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java index 550f948309..f108f8b535 100644 --- a/src/com/android/launcher3/dragndrop/DragOptions.java +++ b/src/com/android/launcher3/dragndrop/DragOptions.java @@ -37,12 +37,6 @@ public class DragOptions { /** Scale of the icons over the workspace icon size. */ public float intrinsicIconScaleFactor = 1f; - /** - * Whether or not to defer {@link com.android.launcher3.DragSource#onDropCompleted} until - * uninstall result is available when dropped on uninstall drop target. - */ - public boolean deferCompleteForUninstall = false; - /** * Specifies a condition that must be met before DragListener#onDragStart() is called. * By default, there is no condition and onDragStart() is called immediately following diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index b930ad3ca9..4a60d4c563 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -283,7 +283,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } }); } - options.deferCompleteForUninstall = true; mLauncher.getWorkspace().beginDragShared(v, this, options); } @@ -850,7 +849,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } public void onDropCompleted(final View target, final DragObject d, - final boolean isFlingToDelete, final boolean success) { + final boolean success) { if (success) { if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) { @@ -898,12 +897,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher.getModelWriter()); } - - if (!isFlingToDelete) { - // Fling to delete already exits spring loaded mode after the animation finishes. - mLauncher.exitSpringLoadedDragModeDelayed(success, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - } } private void updateItemLocationsInDatabaseBatch() { @@ -1158,21 +1151,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } public void onDrop(DragObject d, DragOptions options) { - Runnable cleanUpRunnable = null; - - // If we are coming from All Apps space, we defer removing the extra empty screen - // until the folder closes - if (d.dragSource != mLauncher.getWorkspace() && !(d.dragSource instanceof Folder)) { - cleanUpRunnable = new Runnable() { - @Override - public void run() { - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, - 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)) { @@ -1238,8 +1216,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC float scaleY = getScaleY(); setScaleX(1.0f); setScaleY(1.0f); - mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, - cleanUpRunnable, null); + mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, null); setScaleX(scaleX); setScaleY(scaleY); } else { @@ -1264,6 +1241,8 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter()); } + mLauncher.exitSpringLoadedDragMode(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); if (d.stateAnnouncer != null) { d.stateAnnouncer.completeAction(R.string.item_moved); } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 399888899a..d469eb2542 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -254,7 +254,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { public void performCreateAnimation(final ShortcutInfo destInfo, final View destView, final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect, - float scaleRelativeToDragLayer, Runnable postAnimationRunnable) { + float scaleRelativeToDragLayer) { prepareCreateAnimation(destView); addItem(destInfo); // This will animate the first item from it's position as an icon into its @@ -263,7 +263,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { .start(); // This will animate the dragView (srcView) into the new folder - onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, + onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, false /* itemReturnedOnFailedDrop */); } @@ -279,7 +279,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect, - float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable, + float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) { item.cellX = -1; item.cellY = -1; @@ -351,7 +351,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { dragLayer.animateView(animateView, from, to, finalAlpha, 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, new DecelerateInterpolator(2), new AccelerateInterpolator(2), - postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); + null, DragLayer.ANIMATION_END_DISAPPEAR, null); mFolder.hideItem(item); @@ -381,7 +381,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { item = (ShortcutInfo) d.dragInfo; } mFolder.notifyDrop(); - onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, + onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), itemReturnedOnFailedDrop); } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 68b547d88b..4435afb8f6 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -39,6 +39,7 @@ import android.view.View; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; +import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; @@ -487,14 +488,7 @@ public class PopupContainerWithArrow extends BaseActionPopup imp } @Override - public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, - boolean success) { - if (!success) { - d.dragView.remove(); - mLauncher.showWorkspace(true); - mLauncher.getDropTargetBar().onDragEnd(); - } - } + public void onDropCompleted(View target, DragObject d, boolean success) { } @Override public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java index d475ee9dfb..15f5af6255 100644 --- a/src/com/android/launcher3/util/FlingAnimation.java +++ b/src/com/android/launcher3/util/FlingAnimation.java @@ -95,7 +95,7 @@ public class FlingAnimation implements AnimatorUpdateListener, Runnable { Runnable onAnimationEndRunnable = new Runnable() { @Override public void run() { - mLauncher.exitSpringLoadedDragMode(); + mLauncher.exitSpringLoadedDragMode(false, 0); mDropTarget.completeDrop(mDragObject); } }; diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index ee5dd66bde..b6b3089b0d 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -159,21 +159,7 @@ abstract class BaseWidgetSheet extends AbstractFloatingView // @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); - - if (!success) { - d.deferDragViewCleanupPostAnimation = false; - } - } + public void onDropCompleted(View target, DragObject d, boolean success) { } @Override From e11adb57acf19b5aaf8ef7d8ed0fa5fe54e91606 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 16 Oct 2017 14:14:39 -0700 Subject: [PATCH 051/885] Import translations. DO NOT MERGE Change-Id: I27e36b0aac568700f8278d2c5285eb7ed2c39fec Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- go/res/values-hi/strings.xml | 2 +- go/res/values-pa/strings.xml | 2 +- go/res/values-sw/strings.xml | 2 +- res/values-bn/strings.xml | 6 ++-- res/values-bs/strings.xml | 4 +-- res/values-cs/strings.xml | 2 +- res/values-da/strings.xml | 2 +- res/values-es/strings.xml | 2 +- res/values-fr-rCA/strings.xml | 2 +- res/values-gu/strings.xml | 2 +- res/values-hi/strings.xml | 58 +++++++++++++++++------------------ res/values-hy/strings.xml | 4 +-- res/values-in/strings.xml | 2 +- res/values-iw/strings.xml | 2 +- res/values-ja/strings.xml | 2 +- res/values-kn/strings.xml | 2 +- res/values-lv/strings.xml | 2 +- res/values-mk/strings.xml | 6 ++-- res/values-mr/strings.xml | 18 +++++------ res/values-pa/strings.xml | 38 +++++++++++------------ res/values-sw/strings.xml | 4 +-- res/values-ta/strings.xml | 4 +-- res/values-te/strings.xml | 10 +++--- res/values-uz/strings.xml | 4 +-- 24 files changed, 91 insertions(+), 91 deletions(-) diff --git a/go/res/values-hi/strings.xml b/go/res/values-hi/strings.xml index bc057760fb..2c1650a0dd 100644 --- a/go/res/values-hi/strings.xml +++ b/go/res/values-hi/strings.xml @@ -20,7 +20,7 @@ "शॉर्टकट चुनने के लिए छूकर रखें." - "शॉर्टकट चुनने के लिए डबल टैप करके रखें या कस्टम कार्रवाइयों का उपयोग करें." + "शॉर्टकट चुनने के लिए दो बार छूएं और कुछ देर दबाएं रखें या अपने मुताबिक कार्रवाइयों का इस्तेमाल करें." "शॉर्टकट" "%1$s शॉर्टकट" diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml index f3982ab71b..c7e4abf3a8 100644 --- a/go/res/values-pa/strings.xml +++ b/go/res/values-pa/strings.xml @@ -20,7 +20,7 @@ "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" - "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਡਬਲ-ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" "ਸ਼ਾਰਟਕੱਟ" "%1$s ਸ਼ਾਰਟਕੱਟ" diff --git a/go/res/values-sw/strings.xml b/go/res/values-sw/strings.xml index 0379ed012a..13c12e4a6c 100644 --- a/go/res/values-sw/strings.xml +++ b/go/res/values-sw/strings.xml @@ -20,7 +20,7 @@ "Gusa na ushikilie ili uchague njia ya mkato." - "Gonga mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum." + "Gusa mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum." "Njia za mkato" "Njia za mkato za %1$s" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 471602f47d..4d601e0265 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -35,10 +35,10 @@ "%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ" "নিজে যোগ করতে টাচ করে ধরে রাখুন" "স্বয়ংক্রিয়ভাবে যোগ করুন" - "অ্যাপ অনুসন্ধান করুন" + "অ্যাপ খুঁজুন" "অ্যাপ লোড হচ্ছে…" "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি" - "আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন" + "আরও অ্যাপ্লিকেশানের জন্য খুঁজুন" "বিজ্ঞপ্তি" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" "পছন্দসই ট্রে-তে আর কোনো জায়গা নেই" @@ -46,7 +46,7 @@ "হোম" "সরান" "আনইনস্টল করুন" - "অ্যাপ্লিকেশানের তথ্য" + "অ্যাপের তথ্য" "শর্টকাটগুলি ইনস্টল করে" "একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷" "হোম সেটিংস এবং শর্টকাটগুলি পড়ে" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 49c862a8cd..a3da3651ad 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -64,7 +64,7 @@ "Nova stranica početnog ekrana" "Folder je otvoren, (š) %1$d (v) %2$d" "Dodirnite da zatvorite folder" - "Dodirnite da sačuvate promjenu imena" + "Dodirnite da sačuvate promjenu naziva" "Folder je zatvoren" "Ime foldera je promijenjeno u %1$s" "Folder: %1$s" @@ -82,7 +82,7 @@ "Potreban je pristup obavještenjima" "Za prikaz tačaka obavještenja, uključite obavještenja za aplikacije za aplikaciju %1$s" "Promijeni postavke" - "Dodajte ikonu na početni ekran" + "Dodaj ikonu na početni ekran" "Za nove aplikacije" "Promjena oblika ikona" "Koristite sistemski zadano" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index e6dee3c3b3..5a3b4c000e 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -46,7 +46,7 @@ "Plocha" "Odstranit" "Odinstalovat" - "Informace o aplikaci" + "O aplikaci" "instalace zástupce" "Umožňuje aplikaci přidat zástupce bez zásahu uživatele." "čtení nastavení a odkazů plochy" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 653105e058..849be0d4ee 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -46,7 +46,7 @@ "Startskærm" "Fjern" "Afinstaller" - "Oplysninger om appen" + "Appinfo" "installere genveje" "Tillader, at en app tilføjer genveje uden brugerens indgriben." "læs indstillinger og genveje for startskærmen" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 3ab41caf69..8e637ffa6e 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -83,7 +83,7 @@ "Para mostrar burbujas de notificación, activa las notificaciones de %1$s" "Cambiar ajustes" "Añadir icono a la pantalla de inicio" - "Para nuevas aplicaciones" + "Para aplicaciones nuevas" "Cambiar forma de los iconos" "Usar opción predeterminada del sistema" "Cuadrado" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index e57433d0c3..cd4181e27b 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -24,7 +24,7 @@ "Travail" "L\'application n\'est pas installée." "Application indisponible" - "L\'application téléchargée est désactivée en mode sécurisé." + "L\'application téléchargée est désactivée en mode sans échec." "Widgets désactivés en mode sans échec" "Le raccourci n\'est pas disponible" "Écran d\'accueil" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 7096a3ee0a..462f0a05f5 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -39,7 +39,7 @@ "ઍપ્લિકેશનો લોડ કરી રહ્યું છે…" "\"%1$s\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી" "વધુ ઍપ્લિકેશનો શોધો" - "સૂચનાઓ" + "નોટિફિકેશનો" "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." "મનપસંદ ટ્રે પર વધુ જગ્યા નથી" "ઍપ્લિકેશનોની સૂચિ" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index f1ee0f2366..61f736450f 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -28,31 +28,31 @@ "विजेट सुरक्षित मोड में अक्षम हैं" "शॉर्टकट उपलब्ध नहीं है" "होम स्क्रीन" - "कस्टम कार्रवाई" - "विजेट को चुनने के लिए स्‍पर्श करके रखें." - "कोई विजेट चुनने के लिए डबल टैप करके रखें या कस्‍टम कार्रवाइयां चुनें." + "कस्टम कार्रवाइयां" + "विजेट को चुनने के लिए दबाकर रखें" + "कोई विजेट चुनने के लिए दो बार छूएं और दबाये रखें या अपने मुताबिक कार्रवाइयां चुनें." "%1$d × %2$d" "%1$d चौड़ाई गुणा %2$d ऊंचाई" - "मैन्युअल रूप से जोड़ने के लिए स्पर्श करके रखें" + "खुद जोड़ने के लिए दबाकर रखें" "अपने आप जोड़ें" - "ऐप्लिकेशन खोजें" + "ऐप सर्च करें" "ऐप्लिकेशन लोड हो रहे हैं…" "\"%1$s\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला" - "अधिक ऐप्लिकेशन खोजें" - "नोटिफ़िकेशन" - "इस होम स्‍क्रीन पर स्थान शेष नहीं है." - "पसंदीदा ट्रे में और स्थान नहीं है" + "और ऐप सर्च करें" + "सूचनाएं" + "इस होम स्‍क्रीन पर जगह नहीं बची है" + "पसंदीदा ट्रे में और जगह नहीं है" "ऐप्लिकेशन सूची" - "होम" + "होम पेज" "निकालें" "अनइंस्टॉल करें" "ऐप की जानकारी" "शॉर्टकट इंस्‍टॉल करें" - "ऐप्लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है." - "होम सेटिंग और शॉर्टकट पढ़ें" - "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट पढ़ने देती है." - "होम सेटिंग और शॉर्टकट लिखें" - "ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट बदलने देती है." + "ऐप को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है." + "होम पेज की सेटिंग और शॉर्टकट पढ़ें" + "ऐप्लिकेशन को होम पेज में सेटिंग और शॉर्टकट पढ़ने देती है." + "होम पेज की सेटिंग और शॉर्टकट लिखें" + "ऐप्लिकेशन को होम पेज में सेटिंग और शॉर्टकट बदलने देती है." "%1$s को फ़ोन कॉल करने की अनुमति नहीं है" "विजेट लोड करने में समस्‍या" "सेटअप" @@ -61,7 +61,7 @@ "%1$s अक्षम है" "पेज %2$d में से %1$d" "होम स्क्रीन %2$d में से %1$d" - "नया होम स्‍क्रीन पृष्‍ठ" + "नया होम स्‍क्रीन पेज" "फ़ोल्डर खोला गया, %1$d गुणा %2$d" "फ़ोल्डर बंद करने के लिए टैप करें" "नाम बदलना सहेजने के लिए टैप करें" @@ -70,32 +70,32 @@ "फ़ोल्डर: %1$s" "शॉर्टकट" "वॉलपेपर" - "होम सेटिंग" + "होम पेज की सेटिंग" "आपके व्यवस्थापक द्वारा अक्षम" "खास जानकारी" "होमस्क्रीन घुमाने की अनुमति दें" "फ़ोन घुुमाए जाने पर" - "वर्तमान प्रदर्शन सेटिंग घुमाने की अनुमति नहीं देती" - "नोटिफ़िकेशन बिंदु" + "इस डिसप्ले सेटिंग में रोटेशन (स्क्रीन पर सामग्री को घुमाकर देखने) की सुविधा काम नहीं करती" + "सूचना बिंदु" "चालू" "बंद" - "नोटिफ़िकेशन एक्‍सेस ज़रूरी है" - "नोटिफ़िकेशन बिंदु दिखाने के लिए, %1$s के ऐप्लिकेशन नोटिफ़िकेशन चालू करें" + "सूचना के एक्सेस की ज़रूरत है" + "सूचना बिंदु दिखाने के लिए, %1$s के ऐप्लिकेशन सूचना चालू करें" "सेटिंग बदलें" - "होम स्क्रीन में आइकन जोड़ें" - "नए ऐप्लिकेशन के लिए" - "आइकन का आकार बदलें" + "होम स्क्रीन में आइकॉन जोड़ें" + "नए ऐप के लिए" + "आइकॉन का आकार बदलें" "सिस्टम डिफ़ॉल्ट का उपयोग करें" "वर्ग" "गोल कोनों वाला वर्ग" "मंडली" "आंसू की बूंद" - "आइकन के आकार में बदलाव लागू किए जा रहे हैं" + "आइकॉन के आकार में बदलाव किए जा रहे हैं" "अज्ञात" "निकालें" - "खोजें" + "सर्च करें" "यह ऐप्स इंस्टॉल नहीं है" - "इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." + "इस आइकॉन का ऐप इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप को खोज कर उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं." "%1$s डाउनलोड हो रहा है, %2$s पूर्ण" "%1$s के इंस्टॉल होने की प्रतीक्षा की जा रही है" "%1$s विजेट" @@ -125,7 +125,7 @@ "विजेट का आकार बदलकर उसकी चौड़ाई %1$s और ऊंचाई %2$s कर दी गई" "शॉर्टकट" "%2$s के लिए %1$d शॉर्टकट" - "%3$s के लिए %1$d शॉर्टकट और %2$d नोटिफ़िकेशन हैं" + "%3$s के लिए %1$d शॉर्टकट और %2$d सूचनाएं हैं" "खारिज करें" - "नोटिफ़िकेशन को खारिज किया गया" + "सूचना को खारिज किया गया" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 2b55e966a4..ffb14b0623 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -76,11 +76,11 @@ "Թույլ տալ հիմնական էկրանի պտտումը" "Հեռախոսը պտտելու դեպքում" "Ցուցադրման ընթացիկ կարգավորումներն արգելում են պտտումը" - "Ծանուցման կետեր" + "Ծանուցումների կետիկներ" "Միացված է" "Անջատված է" "Անհրաժեշտ է ծանուցման թույլտվություն" - "Ծանուցման կետերը ցուցադրելու համար միացրեք հավելվածի ծանուցումները %1$s-ի համար" + "Ծանուցումների կետիկները ցուցադրելու համար միացրեք ծանուցումները %1$s-ի համար" "Փոխել կարգավորումները" "Ավելացնել պատկերակը Հիմնական էկրանին" "Նոր հավելվածների համար" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 379a960d05..b503c88e16 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -82,7 +82,7 @@ "Perlu akses notifikasi" "Guna menampilkan Titik Notifikasi, aktifkan notifikasi aplikasi untuk %1$s" "Ubah setelan" - "Tambahkan ikon ke layar Utama" + "Tambahkan ikon ke Layar utama" "Untuk aplikasi baru" "Ubah bentuk ikon" "Gunakan default sistem" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 650a90db81..8ffa1052f0 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -93,7 +93,7 @@ "משנה את הצורה של הסמלים" "לא ידוע" "הסר" - "חפש" + "חיפוש" "אפליקציה זו אינה מותקנת" "האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית." "מוריד את %1$s, %2$s הושלמו" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 4071a6cc99..12815cddbb 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -83,7 +83,7 @@ "通知ドットを表示するには、「%1$s」のアプリ通知を ON にしてください" "設定を変更" "ホーム画面にアイコンを追加" - "新しいアプリをダウンロードしたときに" + "新しいアプリをダウンロードしたとき" "アイコンの形の変更" "システムのデフォルトを使用" "スクエア" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 9a31649be3..5c8b5acf2b 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -45,7 +45,7 @@ "ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ" "ಮುಖಪುಟ" "ತೆಗೆದುಹಾಕಿ" - "ಅಸ್ಥಾಪಿಸು" + "ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್" "ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ" "ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ" "ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ." diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 2d8bfd83a3..563a063b22 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -46,7 +46,7 @@ "Sākums" "Noņemt" "Atinstalēt" - "Lietotnes informācija" + "Par lietotni" "instalēt saīsnes" "Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam." "lasīt sākuma ekrāna iestatījumus un saīsnes" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 7d95a23a6c..458f0fe555 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -68,8 +68,8 @@ "Папката е затворена" "Папката е преименувана во %1$s" "Папка: %1$s" - "Додатоци" - "Позадини" + "Виџети" + "Тапети" "Поставки за Home" "Оневозможено од администраторот" "Краток преглед" @@ -82,7 +82,7 @@ "Потребен е пристап до известувањата" "За да се прикажуваат „Точки за известување“, вклучете ги известувањата за апликацијата %1$s" "Промени ги поставките" - "Додајте икона на почетниот екран" + "Додај икона на почетниот екран" "За нови апликации" "Променете ја формата на иконата" "Користи ја стандардната поставка на системот" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 751ad227ab..ecc5e3927d 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -22,7 +22,7 @@ "Launcher3" "कार्य" - "अॅप स्थापित केलेला नाही." + "अॅप इंस्टॉल केलेला नाही." "अॅप उपलब्ध नाही" "डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला" "विजेट सुरक्षित मोडमध्ये अक्षम झाले" @@ -45,18 +45,18 @@ "अॅप्स सूची" "होम" "काढा" - "विस्थापित करा" + "अनइंस्टॉल करा" "अॅप माहिती" "शॉर्टकट स्‍थापित करा" "वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते." - "होम सेटिग्ज आणि शॉर्टकट वाचा" + "होम सेटिंग्ज आणि शॉर्टकट वाचा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते." - "होम स्क्रीन सेटिंग्ज आणि शॉर्टकट लिहा" + "होम सेटिंग्ज आणि शॉर्टकट लिहा" "मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते." "%1$s ला फोन कॉल करण्याची अनुमती नाही" "विजेट लोड करण्यात समस्या" "सेटअप" - "हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही." + "हा सिस्टम अॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही." "अनामित फोल्डर" "%1$s अक्षम केला आहे" "%2$d पैकी %1$d पृष्ठ" @@ -75,7 +75,7 @@ "अवलोकन" "मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या" "फोन फिरविला जातो तेव्हा" - "वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही" + "वर्तमान डिस्प्ले सेटिंग रोटेशनला परवानगी देत नाही" "सूचना बिंदू" "चालू" "बंद" @@ -94,10 +94,10 @@ "अज्ञात" "काढा" "शोधा" - "हा अॅप स्थापित केलेला नाही" - "या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता." + "हा अॅप इंस्टॉल केलेला नाही" + "या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता." "%1$s डाउनलोड होत आहे , %2$s पूर्ण झाले" - "%1$s स्थापित करण्याची प्रतिक्षा करीत आहे" + "%1$s इंस्टॉल करण्याची प्रतिक्षा करीत आहे" "%1$s विजेट" "होम स्क्रीनवर जोडा" "आयटम येथे हलवा" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index cf5351c7a7..5deb2a9821 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -27,10 +27,10 @@ "ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ" - "ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ" + "ਹੋਮ ਸਕ੍ਰੀਨ" + "ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ" "ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT & ਹੋਲਡ ਕਰੋ।" - "ਡਬਲ-ਟੈਪ & ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।" + "ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤਣ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" "%1$d × %2$d" "%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ" "ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ" @@ -45,15 +45,15 @@ "ਐਪ ਸੂਚੀ" "ਹੋਮ" "ਹਟਾਓ" - "ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ" + "ਅਣਸਥਾਪਤ ਕਰੋ" "ਐਪ ਜਾਣਕਾਰੀ" - "ਸ਼ਾਰਟਕੱਟ ਇੰਸਟੌਲ ਕਰੋ" - "ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" + "ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ" + "ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" "ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ" "ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" "ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਲਿਖੋ" "ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" - "%1$s ਨੂੰ ਫੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ" + "%1$s ਨੂੰ ਫ਼ੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ" "ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ" "ਸਥਾਪਤ ਕਰੋ" "ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।" @@ -73,47 +73,47 @@ "ਹੋਮ ਸੈਟਿੰਗਾਂ" "ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ" "ਰੂਪ-ਰੇਖਾ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ" + "ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ" "ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ" "ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ" "ਸੂਚਨਾ ਬਿੰਦੂ" "ਚਾਲੂ" "ਬੰਦ" "ਸੂਚਨਾ ਪਹੁੰਚ ਲੋੜੀਂਦੀ ਹੈ" - "ਸੂਚਨਾ ਬਿੰਦੀਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" + "ਸੂਚਨਾ ਬਿੰਦੂਆਂ ਦਿਖਾਉਣ ਲਈ, %1$s ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ" "ਸੈਟਿੰਗਾਂ ਬਦਲੋ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ" "ਨਵੀਆਂ ਐਪਾਂ ਲਈ" - "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" + "ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ" "ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ" "ਵਰਗ" "ਵਰਗਾਕਾਰ-ਚੱਕਰ" "ਚੱਕਰ" "ਹੰਝੂ ਦੀ ਬੂੰਦ" - "ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + "ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" "ਅਗਿਆਤ" "ਹਟਾਓ" "ਖੋਜੋ" "ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।" - "ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਇੰਸਟੌਲ ਕਰ ਸਕਦੇ ਹੋ।" + "ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।" "%1$s ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, %2$s ਸੰਪੂਰਣ" - "%1$s ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" + "%1$s ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ" "%1$s ਵਿਜੇਟ" - "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ" - "ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" + "ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" "ਅਈਟਮ ਹਟਾਈ ਗਈ" "ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ" "ਕਤਾਰ %1$s ਕਾਲਮ %2$s ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਸਥਿਤੀ %1$s ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਮਨਪਸੰਦ ਸਥਿਤੀ %1$s ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਆਈਟਮ ਮੂਵ ਕੀਤੀ ਗਈ" - "ਇਸ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜੋ: %1$s" - "%1$s ਦੇ ਨਾਲ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜੋ" + "ਇਸ ਫੋਲਡਰ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ: %1$s" + "%1$s ਦੇ ਨਾਲ ਫੋਲਡਰ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ" "ਆਈਟਮ ਨੂੰ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" "ਇਸਦੇ ਨਾਲ ਫੋਲਡਰ ਬਣਾਓ: %1$s" "ਫੋਲਡਰ ਬਣਾਇਆ ਗਿਆ" - "ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ" + "ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ" "ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਮੂਵ ਕਰੋ" "ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਮੂਵ ਕਰੋ" "ਸਕ੍ਰੀਨ ਨੂੰ ਮੂਵ ਕੀਤਾ ਗਿਆ" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 3de28e011d..fd9e4e308b 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -30,7 +30,7 @@ "Skrini ya kwanza" "Vitendo maalum" "Gusa na ushikilie ili kuteua wijeti." - "Gonga mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum." + "Gusa mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum." "%1$d × %2$d" "Upana wa %1$d na kimo cha %2$d" "Gusa na ushikilie ili uweke mwenyewe" @@ -97,7 +97,7 @@ "Ondoa" "Tafuta" "Programu hii haijasakinishwa" - "Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." + "Programu ya aikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." "%1$s inapakuliwa, %2$s imekamilika" "%1$s inasubiri kusakinisha" "Wijeti za %1$s" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 1d92581a05..738abaf404 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -46,7 +46,7 @@ "முகப்பு" "அகற்று" "நிறுவல் நீக்கு" - "பயன்பாட்டுத் தகவல்" + "ஆப்ஸ் தகவல்" "குறுக்குவழிகளை நிறுவுதல்" "பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது." "முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்" @@ -77,7 +77,7 @@ "மொபைலைச் சுழற்றும் போது" "தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை" "அறிவிப்புப் புள்ளிகள்" - "இயக்கப்பட்டுள்ளது" + "ஆன்" "முடக்கப்பட்டுள்ளது" "அறிவிப்பிற்கான அணுகல் தேவை" "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 6c6669938b..3155e3d6c1 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -26,7 +26,7 @@ "యాప్ అందుబాటులో లేదు" "డౌన్‌లోడ్ చేసిన యాప్ సురక్షిత మోడ్‌లో నిలిపివేయబడింది" "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" - "సత్వరమార్గం అందుబాటులో లేదు" + "షార్ట్‌కట్ అందుబాటులో లేదు" "హోమ్ స్క్రీన్" "అనుకూల చర్యలు" "విడ్జెట్‌ను ఎంచుకోవడానికి తాకి & నొక్కి పెట్టండి." @@ -38,7 +38,7 @@ "అప్లికేషన్‌లను శోధించండి" "అప్లికేషన్‌లను లోడ్ చేస్తోంది…" "\"%1$s\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు" - "మరిన్ని అనువర్తనాల కోసం శోధించు" + "మరిన్ని యాప్‌ల కోసం వెతుకు" "నోటిఫికేషన్‌లు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." "ఇష్టమైనవి ట్రేలో ఖాళీ లేదు" @@ -46,7 +46,7 @@ "హోమ్" "తీసివేయి" "అన్ఇన్‌స్టాల్ చేయి" - "అనువర్తన సమాచారం" + "యాప్ సమాచారం" "సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం" "వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం" @@ -83,7 +83,7 @@ "నోటిఫికేషన్ డాట్‌లను చూపించడానికి %1$sకు యాప్ నోటిఫికేషన్‌లను ఆన్ చేయండి" "సెట్టింగ్‌లను మార్చు" "హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు" - "కొత్త అనువర్తనాల కోసం" + "కొత్త యాప్‌ల కోసం" "చిహ్న ఆకారాన్ని మార్చు" "సిస్టమ్ డిఫాల్ట్‌ను ఉపయోగించండి" "చతురస్రం" @@ -93,7 +93,7 @@ "చిహ్న ఆకార మార్పులను వర్తింపజేస్తోంది" "తెలియదు" "తీసివేయి" - "శోధించు" + "వెతుకు" "ఈ యాప్ ఇన్‌స్టాల్ చేయబడలేదు" "ఈ చిహ్నం యొక్క యాప్ ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ యాప్ కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు." "%1$s డౌన్‌లోడ్ అవుతోంది, %2$s పూర్తయింది" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 7e31889138..ed2d342c46 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -76,11 +76,11 @@ "Asosiy ekranni aylantirishga ruxsat berish" "Telefon burilganda" "Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi" - "Bildirishnoma nuqtalari" + "Bildirishnoma belgilari" "Yoniq" "O‘chiq" "Bildirishnomalarga ruxsat berilmagan" - "Bildirishnoma nuqtalarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" + "Bildirishnoma belgilarini ko‘rsatish uchun %1$s ilovasida bildirishnomalarni yoqing" "Sozlamalarni o‘zgartirish" "Bosh ekranga ikonka qo‘shish" "Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish" From a4ec5b45d33630c5fa8f1179552209704ccdbb62 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 16 Oct 2017 14:26:10 -0700 Subject: [PATCH 052/885] Import translations. DO NOT MERGE Change-Id: Id64734c3e855af2dc1fe94de1cf6635bd2741e29 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- go/res/values-hi/strings.xml | 2 +- go/res/values-pa/strings.xml | 2 +- go/res/values-sw/strings.xml | 2 +- res/values-sw/strings.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go/res/values-hi/strings.xml b/go/res/values-hi/strings.xml index bc057760fb..2c1650a0dd 100644 --- a/go/res/values-hi/strings.xml +++ b/go/res/values-hi/strings.xml @@ -20,7 +20,7 @@ "शॉर्टकट चुनने के लिए छूकर रखें." - "शॉर्टकट चुनने के लिए डबल टैप करके रखें या कस्टम कार्रवाइयों का उपयोग करें." + "शॉर्टकट चुनने के लिए दो बार छूएं और कुछ देर दबाएं रखें या अपने मुताबिक कार्रवाइयों का इस्तेमाल करें." "शॉर्टकट" "%1$s शॉर्टकट" diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml index f3982ab71b..c7e4abf3a8 100644 --- a/go/res/values-pa/strings.xml +++ b/go/res/values-pa/strings.xml @@ -20,7 +20,7 @@ "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।" - "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਡਬਲ-ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" + "ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।" "ਸ਼ਾਰਟਕੱਟ" "%1$s ਸ਼ਾਰਟਕੱਟ" diff --git a/go/res/values-sw/strings.xml b/go/res/values-sw/strings.xml index 0379ed012a..13c12e4a6c 100644 --- a/go/res/values-sw/strings.xml +++ b/go/res/values-sw/strings.xml @@ -20,7 +20,7 @@ "Gusa na ushikilie ili uchague njia ya mkato." - "Gonga mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum." + "Gusa mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum." "Njia za mkato" "Njia za mkato za %1$s" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 2f0bbf755b..402f3a6d4c 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -30,7 +30,7 @@ "Skrini ya kwanza" "Vitendo maalum" "Gusa na ushikilie ili kuteua wijeti." - "Gonga mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum." + "Gusa mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum." "%1$d × %2$d" "Upana wa %1$d na kimo cha %2$d" "Gusa na ushikilie ili uweke mwenyewe" @@ -98,7 +98,7 @@ "Ondoa" "Tafuta" "Programu hii haijasakinishwa" - "Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." + "Programu ya aikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe." "%1$s inapakuliwa, %2$s imekamilika" "%1$s inasubiri kusakinisha" "Wijeti za %1$s" From aeb1643ec61af93453c862e84b158d3b0ebcb1c7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 16 Oct 2017 11:46:41 -0700 Subject: [PATCH 053/885] Launcher state management cleanup > Removing Widgets and related states > Fixing different durations being used when opening/closing all-apps > Removing some unnecessary object allocations when changing state without animation > Differentiating widget bootm sheel and full sheet in logs Bug: 67678570 Change-Id: Ic169528736d04ee0b38564b4f96595ba066eabda --- res/values/config.xml | 9 +- .../android/launcher3/ButtonDropTarget.java | 4 +- src/com/android/launcher3/CellLayout.java | 17 +- src/com/android/launcher3/Launcher.java | 94 ++-- .../android/launcher3/LauncherAnimUtils.java | 24 + src/com/android/launcher3/LauncherModel.java | 2 +- .../LauncherStateTransitionAnimation.java | 159 +++---- .../launcher3/PinchAnimationManager.java | 19 +- src/com/android/launcher3/Workspace.java | 148 +++--- .../WorkspaceStateTransitionAnimation.java | 424 ++++++------------ .../allapps/AllAppsTransitionController.java | 26 +- .../launcher3/dragndrop/DragController.java | 5 +- .../launcher3/dragndrop/DragLayer.java | 55 +-- .../dragndrop/PageCutOutScrimDrawable.java | 90 ++++ .../PinShortcutRequestActivityInfo.java | 6 +- src/com/android/launcher3/folder/Folder.java | 5 +- .../folder/FolderAnimationManager.java | 18 +- .../android/launcher3/folder/FolderIcon.java | 4 +- .../logging/UserEventDispatcher.java | 8 +- .../launcher3/model/PackageUpdatedTask.java | 12 +- .../launcher3/util/FlingAnimation.java | 4 +- .../launcher3/widget/BaseWidgetSheet.java | 13 +- .../launcher3/widget/WidgetsBottomSheet.java | 5 + .../launcher3/widget/WidgetsFullSheet.java | 5 + 24 files changed, 520 insertions(+), 636 deletions(-) create mode 100644 src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java diff --git a/res/values/config.xml b/res/values/config.xml index 27d4655b9b..c0bf360385 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -46,20 +46,13 @@ - 30 - 100 - 250 + 76 90 70 - - 320 - 300 - 17 diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index e1b5e2c754..1a1c3198f6 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; + import android.animation.AnimatorSet; import android.animation.FloatArrayEvaluator; import android.animation.ObjectAnimator; @@ -229,7 +231,7 @@ public abstract class ButtonDropTarget extends TextView public void run() { completeDrop(d); mDropTargetBar.onDragEnd(); - mLauncher.exitSpringLoadedDragMode(true, 0); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); } }; dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index d6c8575d63..3643971a42 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -104,8 +104,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private final ArrayList mFolderBackgrounds = new ArrayList<>(); final PreviewBackground mFolderLeaveBehind = new PreviewBackground(); - private float mBackgroundAlpha; - private static final int[] BACKGROUND_STATE_ACTIVE = new int[] { android.R.attr.state_active }; private static final int[] BACKGROUND_STATE_DEFAULT = new int[0]; private final Drawable mBackground; @@ -221,7 +219,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mBackground = res.getDrawable(R.drawable.bg_celllayout); mBackground.setCallback(this); - mBackground.setAlpha((int) (mBackgroundAlpha * 255)); + mBackground.setAlpha(0); mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx); @@ -440,7 +438,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // 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 (mBackgroundAlpha > 0.0f) { + if (mBackground.getAlpha() > 0) { mBackground.draw(canvas); } @@ -847,15 +845,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); } - public float getBackgroundAlpha() { - return mBackgroundAlpha; - } - - public void setBackgroundAlpha(float alpha) { - if (mBackgroundAlpha != alpha) { - mBackgroundAlpha = alpha; - mBackground.setAlpha((int) (mBackgroundAlpha * 255)); - } + public Drawable getScrimBackground() { + return mBackground; } @Override diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index bfcd82ace6..a80e62b08f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; +import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; @@ -117,6 +120,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.ActivityResultInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ItemInfoMatcher; @@ -205,15 +209,13 @@ public class Launcher extends BaseActivity static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; /** The different states that Launcher can be in. */ - enum State { WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED, - WIDGETS, WIDGETS_SPRING_LOADED } + enum State { WORKSPACE, WORKSPACE_SPRING_LOADED, APPS} @Thunk State mState = State.WORKSPACE; @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; - public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500; private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; // How long to wait before the new-shortcut animation automatically pans the workspace @@ -366,9 +368,6 @@ public class Launcher extends BaseActivity mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); mAppWidgetHost = new LauncherAppWidgetHost(this); - if (Utilities.ATLEAST_MARSHMALLOW) { - mAppWidgetHost.addProviderChangeListener(this); - } mAppWidgetHost.startListening(); // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here, @@ -582,8 +581,7 @@ public class Launcher extends BaseActivity Runnable exitSpringLoaded = new Runnable() { @Override public void run() { - exitSpringLoadedDragMode((resultCode != RESULT_CANCELED), - EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); } }; @@ -635,7 +633,7 @@ public class Launcher extends BaseActivity final Runnable onComplete = new Runnable() { @Override public void run() { - exitSpringLoadedDragMode(false, 0); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); } }; @@ -763,8 +761,7 @@ public class Launcher extends BaseActivity @Override public void run() { completeAddAppWidget(appWidgetId, requestArgs, layout, null); - exitSpringLoadedDragMode((resultCode != RESULT_CANCELED), - EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); } }; } else if (resultCode == RESULT_CANCELED) { @@ -1439,8 +1436,9 @@ public class Launcher extends BaseActivity if (topOpenView != null) { topOpenView.logActionCommand(Action.Command.HOME_INTENT); } else if (alreadyOnHome) { - ued.logActionCommand(Action.Command.HOME_INTENT, - mWorkspace.getState().containerType, mWorkspace.getCurrentPage()); + Target target = newContainerTarget(mWorkspace.getState().containerType); + target.pageIndex = mWorkspace.getCurrentPage(); + ued.logActionCommand(Action.Command.HOME_INTENT, target); } // In all these cases, only animate if we're already on home @@ -1714,8 +1712,7 @@ public class Launcher extends BaseActivity @Override public void run() { // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragMode(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT - ); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); } }; completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this)); @@ -1899,9 +1896,6 @@ public class Launcher extends BaseActivity } else if (isAppsViewVisible()) { ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS); showWorkspace(true); - } else if (isWidgetsViewVisible()) { - ued.logActionCommand(Action.Command.BACK, ContainerType.WIDGETS); - showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW); showWorkspace(true); @@ -2444,10 +2438,6 @@ public class Launcher extends BaseActivity return mState == State.APPS; } - public boolean isWidgetsViewVisible() { - return mState == State.WIDGETS; - } - @Override public void onTrimMemory(int level) { super.onTrimMemory(level); @@ -2546,29 +2536,18 @@ public class Launcher extends BaseActivity /** * Shows the apps view. - */ - public void showAppsView(boolean animated) { - markAppsViewShown(); - showAppsOrWidgets(State.APPS, animated); - } - - /** - * Sets up the transition to show the apps/widgets view. * * @return whether the current from and to state allowed this operation */ // TODO: calling method should use the return value so that when {@code false} is returned // the workspace transition doesn't fall into invalid state. - private boolean showAppsOrWidgets(State toState, boolean animated) { + public boolean showAppsView(boolean animated) { + markAppsViewShown(); + if (!(mState == State.WORKSPACE || - mState == State.APPS_SPRING_LOADED || - mState == State.WIDGETS_SPRING_LOADED || (mState == State.APPS && mAllAppsController.isTransitioning()))) { return false; } - if (toState != State.APPS && toState != State.WIDGETS) { - return false; - } // This is a safe and supported transition to bypass spring_loaded mode. if (mExitSpringLoadedModeRunnable != null) { @@ -2576,14 +2555,10 @@ public class Launcher extends BaseActivity mExitSpringLoadedModeRunnable = null; } - if (toState == State.APPS) { - mStateTransitionAnimation.startAnimationToAllApps(animated); - } else { - mStateTransitionAnimation.startAnimationToWidgets(animated); - } + mStateTransitionAnimation.startAnimationToAllApps(animated); // Change the state *after* we've called all the transition code - setState(toState); + setState(State.APPS); AbstractFloatingView.closeAllOpenViews(this); // Send an accessibility event to announce the context change @@ -2613,12 +2588,11 @@ public class Launcher extends BaseActivity setState(State.WORKSPACE_SPRING_LOADED); } - public void exitSpringLoadedDragMode(final boolean successfulDrop, int delay) { - exitSpringLoadedDragMode(successfulDrop, delay, null); + public void exitSpringLoadedDragMode(int delay) { + exitSpringLoadedDragMode(delay, null); } - public void exitSpringLoadedDragMode(final boolean successfulDrop, int delay, - final Runnable onCompleteRunnable) { + public void exitSpringLoadedDragMode(int delay, final Runnable onCompleteRunnable) { if (!isStateSpringLoaded()) return; // Unlock rotation lock @@ -2634,18 +2608,12 @@ public class Launcher extends BaseActivity mExitSpringLoadedModeRunnable = new Runnable() { @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 - showWorkspace(true, onCompleteRunnable); - } else if (mState == State.APPS_SPRING_LOADED) { - showAppsView(true /* animated */); - } else if (mState == State.WORKSPACE_SPRING_LOADED) { - showWorkspace(true); - } + // 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 + showWorkspace(true, onCompleteRunnable); mExitSpringLoadedModeRunnable = null; } }; @@ -2653,8 +2621,7 @@ public class Launcher extends BaseActivity } boolean isStateSpringLoaded() { - return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED - || mState == State.WIDGETS_SPRING_LOADED; + return mState == State.WORKSPACE_SPRING_LOADED; } @Override @@ -3397,13 +3364,6 @@ public class Launcher extends BaseActivity } } - @Override - public void notifyWidgetProvidersChanged() { - if (mWorkspace.getState().shouldUpdateWidget) { - refreshAndBindWidgetsForPackageUser(null); - } - } - /** * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only * refreshes the widgets and shortcuts associated with the given package/user diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index cfb9b570b0..02f05c30e7 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -30,6 +30,16 @@ import java.util.HashSet; import java.util.WeakHashMap; public class LauncherAnimUtils { + /** + * Durations for various state animations. These are not defined in resources to allow + * easier access from static classes and enums + */ + public static final int ALL_APPS_TRANSITION_MS = 320; + public static final int OVERVIEW_TRANSITION_MS = 250; + public static final int SPRING_LOADED_TRANSITION_MS = 150; + public static final int SPRING_LOADED_EXIT_SHORT_TIMEOUT = 500; + public static final int SPRING_LOADED_EXIT_NEXT_FRAME = 0; + static WeakHashMap sAnimators = new WeakHashMap(); static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() { public void onAnimationStart(Animator animation) { @@ -141,4 +151,18 @@ public class LauncherAnimUtils { drawable.setAlpha(alpha); } }; + + public static final Property SCALE_PROPERTY = + new Property(Float.class, "scale") { + @Override + public Float get(View view) { + return view.getScaleX(); + } + + @Override + public void set(View view, Float scale) { + view.setScaleX(scale); + view.setScaleY(scale); + } + }; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 618bd0f734..469c5f1c09 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -134,7 +134,7 @@ public class LauncherModel extends BroadcastReceiver } }; - public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener { + public interface Callbacks { public boolean setLoadOnResume(); public int getCurrentWorkspaceScreen(); public void clearPendingBinds(); diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 5823734e47..9c83e3c016 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -19,7 +19,6 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.content.res.Resources; import android.util.Log; import android.view.View; @@ -74,6 +73,7 @@ public class LauncherStateTransitionAnimation { public static final String TAG = "LSTAnimation"; + private final AnimationConfig mConfig = new AnimationConfig(); @Thunk Launcher mLauncher; @Thunk AnimatorSet mCurrentAnimation; AllAppsTransitionController mAllAppsController; @@ -86,24 +86,22 @@ public class LauncherStateTransitionAnimation { /** * Starts an animation to the apps view. */ - public void startAnimationToAllApps(final boolean animated) { + public void startAnimationToAllApps(boolean animated) { final AllAppsContainerView toView = mLauncher.getAppsView(); + // If for some reason our views aren't initialized, don't animate + animated = animated && (toView != null); + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - final Resources res = mLauncher.getResources(); - final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime); final AnimationLayerSet layerViews = new AnimationLayerSet(); - // If for some reason our views aren't initialized, don't animate - boolean initialized = toView != null; - // Cancel the current animation cancelAnimation(); - playCommonTransitionAnimations(Workspace.State.NORMAL_HIDDEN, - animated, initialized, animation, layerViews); - if (!animated || !initialized) { + if (!animated) { + mLauncher.getWorkspace().setState(Workspace.State.NORMAL_HIDDEN); + mAllAppsController.finishPullUp(); toView.setTranslationX(0.0f); toView.setTranslationY(0.0f); @@ -115,6 +113,7 @@ public class LauncherStateTransitionAnimation { mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); return; } + if (!FeatureFlags.LAUNCHER3_PHYSICS) { // We are animating the content view alpha, so ensure we have a layer for it. layerViews.addView(toView); @@ -127,26 +126,22 @@ public class LauncherStateTransitionAnimation { mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } }); - boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide); + + mConfig.reset(); + mAllAppsController.animateToAllApps(animation, mConfig); + mLauncher.getWorkspace().setStateWithAnimation(Workspace.State.NORMAL_HIDDEN, + layerViews, animation, mConfig); Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); mCurrentAnimation = animation; mCurrentAnimation.addListener(layerViews); - if (shouldPost) { + if (mConfig.shouldPost) { toView.post(startAnimRunnable); } else { startAnimRunnable.run(); } } - /** - * Starts an animation to the widgets view. - */ - public void startAnimationToWidgets(final boolean animated) { - // TODO: Remove this - throw new RuntimeException("This cannot happen"); - } - /** * Starts an animation to the workspace from the current overlay view. */ @@ -159,8 +154,7 @@ public class LauncherStateTransitionAnimation { Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); } - if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED - || mAllAppsController.isTransitioning()) { + if (fromState == Launcher.State.APPS || mAllAppsController.isTransitioning()) { startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, animated, onCompleteRunnable); } else { @@ -169,55 +163,28 @@ public class LauncherStateTransitionAnimation { } } - /** - * Plays animations used by various transitions. - */ - private void playCommonTransitionAnimations( - Workspace.State toWorkspaceState, - boolean animated, boolean initialized, AnimatorSet animation, - AnimationLayerSet layerViews) { - // Create the workspace animation. - // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.getWorkspace(). - setStateWithAnimation(toWorkspaceState, animated, layerViews); - - if (animated && initialized) { - // Play the workspace animation - if (workspaceAnim != null) { - animation.play(workspaceAnim); - } - } - } - /** * Starts an animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final boolean animated, + final Workspace.State toWorkspaceState, boolean animated, final Runnable onCompleteRunnable) { - final AllAppsContainerView fromView = mLauncher.getAppsView(); - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - final Resources res = mLauncher.getResources(); - final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime); - - final View toView = mLauncher.getWorkspace(); - - final AnimationLayerSet layerViews = new AnimationLayerSet(); - // If for some reason our views aren't initialized, don't animate - boolean initialized = fromView != null; + animated = animated & (fromView != null); + + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); + final AnimationLayerSet layerViews = new AnimationLayerSet(); // Cancel the current animation cancelAnimation(); - playCommonTransitionAnimations(toWorkspaceState, - animated, initialized, animation, layerViews); - if (!animated || !initialized) { + if (!animated) { if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) { mAllAppsController.finishPullDown(); } fromView.setVisibility(View.GONE); + mLauncher.getWorkspace().setState(toWorkspaceState); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); // Run any queued runnables @@ -227,9 +194,6 @@ public class LauncherStateTransitionAnimation { return; } - // We are animating the content view alpha, so ensure we have a layer for it - layerViews.addView(toView); - animation.addListener(new AnimatorListenerAdapter() { boolean canceled = false; @Override @@ -250,12 +214,16 @@ public class LauncherStateTransitionAnimation { } }); - boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide); - Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); + mConfig.reset(); + mAllAppsController.animateToWorkspace(animation, mConfig); + mLauncher.getWorkspace().setStateWithAnimation(toWorkspaceState, layerViews, animation, + mConfig); + + Runnable startAnimRunnable = new StartAnimRunnable(animation, mLauncher.getWorkspace()); mCurrentAnimation = animation; mCurrentAnimation.addListener(layerViews); - if (shouldPost) { + if (mConfig.shouldPost) { fromView.post(startAnimRunnable); } else { startAnimRunnable.run(); @@ -269,39 +237,41 @@ public class LauncherStateTransitionAnimation { final Workspace.State toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { final View fromWorkspace = mLauncher.getWorkspace(); - final AnimationLayerSet layerViews = new AnimationLayerSet(); - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - // Cancel the current animation cancelAnimation(); - playCommonTransitionAnimations(toWorkspaceState, animated, animated, animation, layerViews); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - if (animated) { - animation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - // This can hold unnecessary references to views. - cleanupAnimation(); - } - }); - animation.addListener(layerViews); - fromWorkspace.post(new StartAnimRunnable(animation, null)); - mCurrentAnimation = animation; - } else /* if (!animated) */ { + if (!animated) { + mLauncher.getWorkspace().setState(toWorkspaceState); // Run any queued runnables if (onCompleteRunnable != null) { onCompleteRunnable.run(); } - - mCurrentAnimation = null; + return; } + + final AnimationLayerSet layerViews = new AnimationLayerSet(); + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); + mConfig.reset(); + mLauncher.getWorkspace().setStateWithAnimation(toWorkspaceState, + layerViews, animation, mConfig); + + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + // This can hold unnecessary references to views. + cleanupAnimation(); + } + }); + animation.addListener(layerViews); + fromWorkspace.post(new StartAnimRunnable(animation, null)); + mCurrentAnimation = animation; } /** @@ -340,4 +310,23 @@ public class LauncherStateTransitionAnimation { mAnim.start(); } } + + public static class AnimationConfig { + public boolean shouldPost; + + private long mOverriddenDuration = -1; + + public void reset() { + shouldPost = false; + mOverriddenDuration = -1; + } + + public void overrideDuration(long duration) { + mOverriddenDuration = duration; + } + + public long getDuration(long defaultDuration) { + return mOverriddenDuration >= 0 ? mOverriddenDuration : defaultDuration; + } + } } diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java index c3d3bb3dfa..10f35bde3e 100644 --- a/src/com/android/launcher3/PinchAnimationManager.java +++ b/src/com/android/launcher3/PinchAnimationManager.java @@ -75,8 +75,7 @@ public class PinchAnimationManager { mOverviewScale = mWorkspace.getOverviewModeShrinkFactor(); mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY(); - mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation() - .mOverviewTransitionTime; + mNormalOverviewTransitionDuration = LauncherAnimUtils.OVERVIEW_TRANSITION_MS; } public int getNormalOverviewTransitionDuration() { @@ -131,7 +130,8 @@ public class PinchAnimationManager { mWorkspace.setScaleX(interpolatedScale); mWorkspace.setScaleY(interpolatedScale); mWorkspace.setTranslationY(interpolatedTranslationY); - setOverviewPanelsAlpha(1f - interpolatedProgress, 0); + int alpha = (int) ((1f - interpolatedProgress) * 255); + setOverviewPanelsAlpha(alpha, 0); } /** @@ -180,14 +180,15 @@ public class PinchAnimationManager { } } - private void setOverviewPanelsAlpha(float alpha, int duration) { + private void setOverviewPanelsAlpha(int alpha, int duration) { int childCount = mWorkspace.getChildCount(); for (int i = 0; i < childCount; i++) { final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); if (duration == 0) { - cl.setBackgroundAlpha(alpha); + cl.getScrimBackground().setAlpha(alpha); } else { - ObjectAnimator.ofFloat(cl, "backgroundAlpha", alpha).setDuration(duration).start(); + ObjectAnimator.ofInt(cl.getScrimBackground(), + LauncherAnimUtils.DRAWABLE_ALPHA, alpha).setDuration(duration).start(); } } } @@ -202,9 +203,9 @@ public class PinchAnimationManager { } private void animateScrim(boolean show) { - float endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0; - startAnimator(INDEX_SCRIM, - ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", endValue), + int endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0; + startAnimator(INDEX_SCRIM, ObjectAnimator.ofInt( + mLauncher.getDragLayer().getScrim(), LauncherAnimUtils.DRAWABLE_ALPHA, endValue), mNormalOverviewTransitionDuration); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 1525e41d21..b57ce63757 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -16,6 +16,12 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; +import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -49,12 +55,14 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.Toast; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; +import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.OverviewAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; @@ -181,20 +189,26 @@ public class Workspace extends PagedView // in all apps or customize mode) public enum State { - NORMAL (false, false, ContainerType.WORKSPACE), - NORMAL_HIDDEN (false, false, ContainerType.ALLAPPS), - SPRING_LOADED (false, true, ContainerType.WORKSPACE), - OVERVIEW (true, true, ContainerType.OVERVIEW), - OVERVIEW_HIDDEN (true, false, ContainerType.WIDGETS); + NORMAL (false, ContainerType.WORKSPACE, false, 1, 0), + NORMAL_HIDDEN (false, ContainerType.ALLAPPS, false, 1, ALL_APPS_TRANSITION_MS), + SPRING_LOADED (true, ContainerType.WORKSPACE, true, 1, SPRING_LOADED_TRANSITION_MS), + OVERVIEW (true, ContainerType.OVERVIEW, true, 0, OVERVIEW_TRANSITION_MS); - public final boolean shouldUpdateWidget; public final boolean hasMultipleVisiblePages; public final int containerType; - State(boolean shouldUpdateWidget, boolean hasMultipleVisiblePages, int containerType) { - this.shouldUpdateWidget = shouldUpdateWidget; + // Properties related to state transition animation. + public final boolean hasScrim; + public final float hotseatAlpha; + public final int transitionDuration; + + State(boolean hasMultipleVisiblePages, int containerType, + boolean hasScrim, float hotseatAlpha, int transitionDuration) { this.hasMultipleVisiblePages = hasMultipleVisiblePages; this.containerType = containerType; + this.hasScrim = hasScrim; + this.hotseatAlpha = hotseatAlpha; + this.transitionDuration = transitionDuration; } } @@ -1296,9 +1310,7 @@ public class Workspace extends PagedView } }); - AccessibilityManager am = (AccessibilityManager) - mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); - final boolean accessibilityEnabled = am.isEnabled(); + final boolean accessibilityEnabled = isAccessibilityEnabled(); animator.addUpdateListener( new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled)); animator.addUpdateListener( @@ -1307,6 +1319,12 @@ public class Workspace extends PagedView } } + protected boolean isAccessibilityEnabled() { + AccessibilityManager am = (AccessibilityManager) + mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); + return am.isEnabled(); + } + @Override protected void notifyPageSwitchListener(int prevPage) { super.notifyPageSwitchListener(prevPage); @@ -1562,7 +1580,7 @@ public class Workspace extends PagedView } public void snapToPageFromOverView(int whichPage) { - mStateTransitionAnimation.snapToPageFromOverView(whichPage); + snapToPage(whichPage, OVERVIEW_TRANSITION_MS, new ZoomInInterpolator()); } int getOverviewModeTranslationY() { @@ -1606,45 +1624,42 @@ public class Workspace extends PagedView return mOverviewModeShrinkFactor; } + /** - * Sets the current workspace {@link State}, returning an animation transitioning the workspace - * to that new state. + * Sets the current workspace {@link State} and updates the UI without any animations */ - public Animator setStateWithAnimation(State toState, boolean animated, - AnimationLayerSet layerViews) { + public void setState(State toState) { + // Update the current state + mState = toState; + mStateTransitionAnimation.setState(mState); + + updateAccessibilityFlags(); + onPrepareStateTransition(mState.hasMultipleVisiblePages); + onStartStateTransition(); + onEndStateTransition(); + } + + /** + * Sets the current workspace {@link State}, while animating the UI + */ + public void setStateWithAnimation(State toState, AnimationLayerSet layerViews, AnimatorSet anim, + AnimationConfig config) { final State fromState = mState; // Update the current state mState = toState; - - // Create the animation to the new state - AnimatorSet workspaceAnim = mStateTransitionAnimation.getAnimationToState(fromState, - toState, animated, layerViews); - - boolean shouldNotifyWidgetChange = !fromState.shouldUpdateWidget - && toState.shouldUpdateWidget; + mStateTransitionAnimation.setStateWithAnimation( + fromState, toState, anim, layerViews, config); updateAccessibilityFlags(); - - if (shouldNotifyWidgetChange) { - mLauncher.notifyWidgetProvidersChanged(); - } - onPrepareStateTransition(mState.hasMultipleVisiblePages); StateTransitionListener listener = new StateTransitionListener(); - if (animated) { - ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1); - stepAnimator.addUpdateListener(listener); + ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1); + stepAnimator.addUpdateListener(listener); - workspaceAnim.play(stepAnimator); - workspaceAnim.addListener(listener); - } else { - listener.onAnimationStart(null); - listener.onAnimationEnd(null); - } - - return workspaceAnim; + anim.play(stepAnimator); + anim.addListener(listener); } public State getState() { @@ -1701,11 +1716,31 @@ public class Workspace extends PagedView updateChildrenLayersEnabled(); } + private void onStartStateTransition() { + if (mState == State.SPRING_LOADED) { + // Show the page indicator at the same time as the rest of the transition. + showPageIndicatorAtCurrentScroll(); + } + getPageIndicator().setShouldAutoHide(mState != State.SPRING_LOADED); + } + public void onEndStateTransition() { mIsSwitchingState = false; updateChildrenLayersEnabled(); mForceDrawAdjacentPages = false; mTransitionProgress = 1; + + if (mState == State.OVERVIEW) { + enableFreeScroll(); + } else { + disableFreeScroll(); + } + + ViewGroup overviewPanel = mLauncher.getOverviewPanel(); + if (isAccessibilityEnabled() && overviewPanel.getVisibility() == View.VISIBLE) { + overviewPanel.getChildAt(0).performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } } public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) { @@ -2076,8 +2111,7 @@ public class Workspace extends PagedView dropTargetLayout, mTargetCell, distance, false, d.dragView) || addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, d, false)) { - mLauncher.exitSpringLoadedDragMode(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); return; } @@ -2191,9 +2225,8 @@ public class Workspace extends PagedView // Animate the item to its original position, while simultaneously exiting // spring-loaded mode so the page meets the icon where it was picked up. mLauncher.getDragController().animateDragViewToOriginalPosition( - onCompleteRunnable, cell, - mStateTransitionAnimation.mSpringLoadedTransitionTime); - mLauncher.exitSpringLoadedDragMode(false, 0); + onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); mLauncher.getDropTargetBar().onDragEnd(); parent.onDropChild(cell); return; @@ -2216,8 +2249,8 @@ public class Workspace extends PagedView } parent.onDropChild(cell); - mLauncher.exitSpringLoadedDragMode(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, onCompleteRunnable); + mLauncher.exitSpringLoadedDragMode( + SPRING_LOADED_EXIT_SHORT_TIMEOUT, onCompleteRunnable); } if (d.stateAnnouncer != null && !droppedOnOriginalCell) { @@ -2824,8 +2857,7 @@ public class Workspace extends PagedView animationStyle, finalView, true); } else { // This is for other drag/drop cases, like dragging from All Apps - mLauncher.exitSpringLoadedDragMode(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); View view; @@ -2887,10 +2919,9 @@ public class Workspace extends PagedView // We wrap the animation call in the temporary set and reset of the current // cellLayout to its final transform -- this means we animate the drag view to // the correct final location. - setFinalTransitionTransform(cellLayout); - mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, - this); - resetTransitionTransform(cellLayout); + setFinalTransitionTransform(); + mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this); + resetTransitionTransform(); } } } @@ -2926,10 +2957,10 @@ public class Workspace extends PagedView loc[0] = r.left; loc[1] = r.top; - setFinalTransitionTransform(layout); + setFinalTransitionTransform(); float cellLayoutScale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true); - resetTransitionTransform(layout); + resetTransitionTransform(); if (scale) { float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth(); @@ -3013,14 +3044,14 @@ public class Workspace extends PagedView } } - public void setFinalTransitionTransform(CellLayout layout) { + public void setFinalTransitionTransform() { if (isSwitchingState()) { mCurrentScale = getScaleX(); setScaleX(mStateTransitionAnimation.getFinalScale()); setScaleY(mStateTransitionAnimation.getFinalScale()); } } - public void resetTransitionTransform(CellLayout layout) { + public void resetTransitionTransform() { if (isSwitchingState()) { setScaleX(mCurrentScale); setScaleY(mCurrentScale); @@ -3641,11 +3672,8 @@ public class Workspace extends PagedView @Override public void onAnimationStart(Animator animation) { - if (mState == State.SPRING_LOADED) { - // Show the page indicator at the same time as the rest of the transition. - showPageIndicatorAtCurrentScroll(); - } mTransitionProgress = 0; + onStartStateTransition(); } @Override diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index e84d3b4e6f..9d9e9fb1a9 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -24,17 +27,14 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; +import android.util.Property; 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.LauncherStateTransitionAnimation.AnimationConfig; +import com.android.launcher3.Workspace.State; import com.android.launcher3.anim.AnimationLayerSet; -import com.android.launcher3.anim.PropertyListBuilder; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.util.Thunk; /** * A convenience class to update a view's visibility state after an alpha animation. @@ -130,79 +130,25 @@ 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 = (oldStateIsNormalHidden && stateIsNormal); - } -} - /** * Manages the animations between each of the workspace states. */ public class WorkspaceStateTransitionAnimation { - public static final String TAG = "WorkspaceStateTransitionAnimation"; + private static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); - @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350; + private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); - final @Thunk Launcher mLauncher; - final @Thunk Workspace mWorkspace; + public final int mWorkspaceScrimAlpha; - @Thunk AnimatorSet mStateAnimator; + private final Launcher mLauncher; + private final Workspace mWorkspace; - @Thunk float mCurrentScale; - @Thunk float mNewScale; + private final float mSpringLoadedShrinkFactor; + private final float mOverviewModeShrinkFactor; + private final boolean mWorkspaceFadeInAdjacentScreens; - @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); - - @Thunk float mSpringLoadedShrinkFactor; - @Thunk float mOverviewModeShrinkFactor; - @Thunk float mWorkspaceScrimAlpha; - @Thunk int mAllAppsTransitionTime; - @Thunk int mOverviewTransitionTime; - @Thunk int mOverlayTransitionTime; - @Thunk int mSpringLoadedTransitionTime; - @Thunk boolean mWorkspaceFadeInAdjacentScreens; + private float mNewScale; public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) { mLauncher = launcher; @@ -210,275 +156,163 @@ public class WorkspaceStateTransitionAnimation { DeviceProfile grid = mLauncher.getDeviceProfile(); Resources res = launcher.getResources(); - mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime); - mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime); - mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime); - mSpringLoadedTransitionTime = mOverlayTransitionTime / 2; mSpringLoadedShrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor; mOverviewModeShrinkFactor = res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f; - mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; + mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha); mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); } - public void snapToPageFromOverView(int whichPage) { - mWorkspace.snapToPage(whichPage, mOverviewTransitionTime, mZoomInInterpolator); + public void setState(Workspace.State toState) { + setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER); } - public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, - boolean animated, AnimationLayerSet layerViews) { - AccessibilityManager am = (AccessibilityManager) - mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); - final boolean accessibilityEnabled = am.isEnabled(); - TransitionStates states = new TransitionStates(fromState, toState); - int workspaceDuration = getAnimationDuration(states); - animateWorkspace(states, animated, workspaceDuration, layerViews, - accessibilityEnabled); - animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION); - return mStateAnimator; + public void setStateWithAnimation(Workspace.State fromState, Workspace.State toState, + AnimatorSet anim, AnimationLayerSet layerViews, AnimationConfig config) { + long duration = config.getDuration(toState == State.NORMAL + ? fromState.transitionDuration : toState.transitionDuration); + AnimatedPropertySetter proertSetter = + new AnimatedPropertySetter(duration, layerViews, anim); + setWorkspaceProperty(toState, proertSetter); } public float getFinalScale() { return mNewScale; } - /** - * 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 if (mLauncher.mState == Launcher.State.WORKSPACE_SPRING_LOADED - || states.oldStateIsNormal && states.stateIsSpringLoaded) { - return mSpringLoadedTransitionTime; - } else { - return mOverlayTransitionTime; - } - } - /** * Starts a transition animation for the workspace. */ - private void animateWorkspace(final TransitionStates states, final boolean animated, - final int duration, AnimationLayerSet layerViews, final boolean accessibilityEnabled) { - // Cancel existing workspace animations and create a new animator set if requested - cancelAnimation(); - if (animated) { - mStateAnimator = LauncherAnimUtils.createAnimatorSet(); - } - + private void setWorkspaceProperty(Workspace.State state, PropertySetter propertySetter) { // Update the workspace state - float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ? - 1.0f : 0f; - float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded || - states.stateIsNormalHidden) ? 1f : 0f; + int finalBackgroundAlpha = state.hasScrim ? 255 : 0; - float finalWorkspaceTranslationY = 0; - if (states.stateIsOverview || states.stateIsOverviewHidden) { - finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY(); - } else if (states.stateIsSpringLoaded) { - finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY(); - } - - final int childCount = mWorkspace.getChildCount(); - - mNewScale = 1.0f; - - if (states.oldStateIsOverview) { - mWorkspace.disableFreeScroll(); - } else if (states.stateIsOverview) { - mWorkspace.enableFreeScroll(); - } - - if (!states.stateIsNormal) { - if (states.stateIsSpringLoaded) { - mNewScale = mSpringLoadedShrinkFactor; - } else if (states.stateIsOverview || states.stateIsOverviewHidden) { + final float finalWorkspaceTranslationY; + switch (state) { + case OVERVIEW: mNewScale = mOverviewModeShrinkFactor; - } + finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY(); + break; + case SPRING_LOADED: + mNewScale = mSpringLoadedShrinkFactor; + finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY(); + break; + default: + mNewScale = 1f; + finalWorkspaceTranslationY = 0; } int toPage = mWorkspace.getPageNearestToCenterOfScreen(); - // TODO: Animate the celllayout alpha instead of the pages. + final int childCount = mWorkspace.getChildCount(); for (int i = 0; i < childCount; i++) { final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); - float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); - float finalAlpha; - if (states.stateIsOverviewHidden) { - finalAlpha = 0f; - } else if(states.stateIsNormalHidden) { - finalAlpha = (i == mWorkspace.getNextPage()) ? 1 : 0; - } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) { - finalAlpha = (i == toPage) ? 1f : 0f; - } else { - finalAlpha = 1f; - } + propertySetter.setInt(cl.getScrimBackground(), + DRAWABLE_ALPHA, finalBackgroundAlpha, mZoomInInterpolator); - // If we are animating to/from the small state, then hide the side pages and fade the - // current page in - if (!FeatureFlags.NO_ALL_APPS_ICON && !mWorkspace.isSwitchingState()) { - if (states.workspaceToAllApps || states.allAppsToWorkspace) { - boolean isCurrentPage = (i == toPage); - if (states.allAppsToWorkspace && isCurrentPage) { - initialAlpha = 0f; - } else if (!isCurrentPage) { - initialAlpha = finalAlpha = 0f; - } - cl.setShortcutAndWidgetAlpha(initialAlpha); - } - } - - if (animated) { - float oldBackgroundAlpha = cl.getBackgroundAlpha(); - if (initialAlpha != finalAlpha) { - Animator alphaAnim = ObjectAnimator.ofFloat( - cl.getShortcutsAndWidgets(), View.ALPHA, finalAlpha); - alphaAnim.setDuration(duration) - .setInterpolator(mZoomInInterpolator); - mStateAnimator.play(alphaAnim); - } - if (oldBackgroundAlpha != 0 || finalBackgroundAlpha != 0) { - ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha", - oldBackgroundAlpha, finalBackgroundAlpha); - bgAnim.setInterpolator(mZoomInInterpolator); - bgAnim.setDuration(duration); - mStateAnimator.play(bgAnim); - } - } else { - cl.setBackgroundAlpha(finalBackgroundAlpha); - cl.setShortcutAndWidgetAlpha(finalAlpha); + // Only animate the page alpha when we actually fade pages + if (mWorkspaceFadeInAdjacentScreens) { + float finalAlpha = state == State.NORMAL && i != toPage ? 0 : 1f; + propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, + finalAlpha, mZoomInInterpolator); } } - final ViewGroup overviewPanel = mLauncher.getOverviewPanel(); + float finalHotseatAlpha = state.hotseatAlpha; - float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f; - if (animated) { - // This is true when transitioning between: - // - Overview <-> Workspace - // - Overview <-> Widget Tray - if (finalOverviewPanelAlpha != overviewPanel.getAlpha()) { - Animator overviewPanelAlpha = ObjectAnimator.ofFloat( - overviewPanel, View.ALPHA, finalOverviewPanelAlpha); - overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel, - accessibilityEnabled)); - layerViews.addView(overviewPanel); + // This is true when transitioning between: + // - Overview <-> Workspace + propertySetter.setViewAlpha(null, mLauncher.getOverviewPanel(), 1 - finalHotseatAlpha); + propertySetter.setViewAlpha(mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha), + mLauncher.getHotseat(), finalHotseatAlpha); - if (states.overviewToWorkspace) { - overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); - } else if (states.workspaceToOverview) { - overviewPanelAlpha.setInterpolator(null); - } + propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, mZoomInInterpolator); + propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y, + finalWorkspaceTranslationY, mZoomInInterpolator); - overviewPanelAlpha.setDuration(duration); - mStateAnimator.play(overviewPanelAlpha); + // Set scrim + propertySetter.setInt(mLauncher.getDragLayer().getScrim(), DRAWABLE_ALPHA, + state.hasScrim ? mWorkspaceScrimAlpha : 0, new DecelerateInterpolator(1.5f)); + } + + private static class PropertySetter { + + public void setViewAlpha(Animator anim, View view, float alpha) { + if (anim != null) { + anim.end(); + return; } + view.setAlpha(alpha); + AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view)); + } - Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace, - new PropertyListBuilder().scale(mNewScale) - .translationY(finalWorkspaceTranslationY).build()) - .setDuration(duration); - scale.setInterpolator(mZoomInInterpolator); - mStateAnimator.play(scale); + public void setFloat(T target, Property property, float value, + TimeInterpolator interpolator) { + property.set(target, value); + } - // For animation optimization, we may need to provide the Launcher transition - // with a set of views on which to force build and manage layers in certain scenarios. - layerViews.addView(mLauncher.getHotseat()); - layerViews.addView(mWorkspace.getPageIndicator()); + public void setInt(T target, Property property, int value, + TimeInterpolator interpolator) { + property.set(target, value); + } - Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha); - if (states.workspaceToOverview) { - hotseatAlpha.setInterpolator(new DecelerateInterpolator(2)); - } else if (states.overviewToWorkspace) { - hotseatAlpha.setInterpolator(null); - } - hotseatAlpha.setDuration(duration); - mStateAnimator.play(hotseatAlpha); - mStateAnimator.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - @Override - public void onAnimationCancel(Animator animation) { - canceled = true; - } - - @Override - public void onAnimationStart(Animator animation) { - mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded); - } - - @Override - public void onAnimationEnd(Animator animation) { - mStateAnimator = null; - if (canceled) return; - if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { - overviewPanel.getChildAt(0).performAccessibilityAction( - AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } - } - }); - } else { - overviewPanel.setAlpha(finalOverviewPanelAlpha); - AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled); - mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded); - - mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end(); - mWorkspace.setScaleX(mNewScale); - mWorkspace.setScaleY(mNewScale); - mWorkspace.setTranslationY(finalWorkspaceTranslationY); - - if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) { - overviewPanel.getChildAt(0).performAccessibilityAction( - AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } + protected boolean isAccessibilityEnabled(View v) { + AccessibilityManager am = (AccessibilityManager) + v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + return am.isEnabled(); } } - /** - * Animates the background scrim. Add to the state animator to prevent jankiness. - * - * @param states the current and final workspace states - * @param animated whether or not to set the background alpha immediately - * @duration duration of the animation - */ - private void animateBackgroundGradient(TransitionStates states, - boolean animated, int duration) { + private static class AnimatedPropertySetter extends PropertySetter { - final DragLayer dragLayer = mLauncher.getDragLayer(); - final float startAlpha = dragLayer.getBackgroundAlpha(); - float finalAlpha = states.stateIsNormal || states.stateIsNormalHidden ? - 0 : mWorkspaceScrimAlpha; + private final long mDuration; + private final AnimationLayerSet mLayerViews; + private final AnimatorSet mStateAnimator; - if (finalAlpha != startAlpha) { - if (animated) { - // These properties refer to the background protection gradient used for AllApps - // and Widget tray. - ValueAnimator bgFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha); - bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - dragLayer.setBackgroundAlpha( - ((Float)animation.getAnimatedValue()).floatValue()); - } - }); - bgFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); - bgFadeOutAnimation.setDuration(duration); - mStateAnimator.play(bgFadeOutAnimation); - } else { - dragLayer.setBackgroundAlpha(finalAlpha); + AnimatedPropertySetter(long duration, AnimationLayerSet layerView, AnimatorSet anim) { + mDuration = duration; + mLayerViews = layerView; + mStateAnimator = anim; + } + + @Override + public void setViewAlpha(Animator anim, View view, float alpha) { + if (anim == null) { + if (view.getAlpha() == alpha) { + return; + } + anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha); + anim.addListener(new AlphaUpdateListener(view, isAccessibilityEnabled(view))); } - } - } - /** - * Cancels the current animation. - */ - private void cancelAnimation() { - if (mStateAnimator != null) { - mStateAnimator.setDuration(0); - mStateAnimator.cancel(); + anim.setDuration(mDuration).setInterpolator(getFadeInterpolator(alpha)); + mLayerViews.addView(view); + mStateAnimator.play(anim); + } + + @Override + public void setFloat(T target, Property property, float value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return; + } + Animator anim = ObjectAnimator.ofFloat(target, property, value); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + + @Override + public void setInt(T target, Property property, int value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return; + } + Animator anim = ObjectAnimator.ofInt(target, property, value); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + + private TimeInterpolator getFadeInterpolator(float finalAlpha) { + return finalAlpha == 0 ? new DecelerateInterpolator(2) : null; } - mStateAnimator = null; } } \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index bb0822f951..5642b4c011 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -11,12 +11,14 @@ import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; @@ -329,15 +331,15 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange); } - public boolean animateToAllApps(AnimatorSet animationOut, long duration) { - boolean shouldPost = true; + public void animateToAllApps(AnimatorSet animationOut, AnimationConfig outConfig) { + outConfig.shouldPost = true; if (animationOut == null) { - return shouldPost; + return; } Interpolator interpolator; if (mDetector.isIdleState()) { preparePull(true); - mAnimationDuration = duration; + mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; mShiftStart = mAppsView.getTranslationY(); interpolator = mFastOutSlowInInterpolator; } else { @@ -347,9 +349,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (nextFrameProgress >= 0f) { mProgress = nextFrameProgress; } - shouldPost = false; + outConfig.shouldPost = false; } + outConfig.overrideDuration(mAnimationDuration); ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", mProgress, 0f); driftAndAlpha.setDuration(mAnimationDuration); @@ -396,7 +399,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } }); mCurrentAnimation = animationOut; - return shouldPost; } public void showDiscoveryBounce() { @@ -432,15 +434,15 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect }); } - public boolean animateToWorkspace(AnimatorSet animationOut, long duration) { - boolean shouldPost = true; + public void animateToWorkspace(AnimatorSet animationOut, AnimationConfig outconfig) { + outconfig.shouldPost = true; if (animationOut == null) { - return shouldPost; + return; } Interpolator interpolator; if (mDetector.isIdleState()) { preparePull(true); - mAnimationDuration = duration; + mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; mShiftStart = mAppsView.getTranslationY(); interpolator = mFastOutSlowInInterpolator; } else { @@ -450,9 +452,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (nextFrameProgress <= 1f) { mProgress = nextFrameProgress; } - shouldPost = false; + outconfig.shouldPost = false; } + outconfig.overrideDuration(mAnimationDuration); ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", mProgress, 1f); driftAndAlpha.setDuration(mAnimationDuration); @@ -479,7 +482,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } }); mCurrentAnimation = animationOut; - return shouldPost; } public void finishPullUp() { diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 31255b7f06..8386f8ffac 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -16,6 +16,8 @@ package com.android.launcher3.dragndrop; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; + import android.content.ComponentName; import android.content.res.Resources; import android.graphics.Bitmap; @@ -261,8 +263,7 @@ public class DragController implements DragDriver.EventListener, TouchController if (!accepted) { // If it was not accepted, cleanup the state. If it was accepted, it is the // responsibility of the drop target to cleanup the state. - mLauncher.exitSpringLoadedDragMode(false, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); mDragObject.deferDragViewCleanupPostAnimation = false; } diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 95223c3f78..bc5aafc0a9 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -25,8 +25,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.Region; -import android.support.v4.graphics.ColorUtils; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; @@ -50,7 +49,6 @@ import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dynamicui.WallpaperColorInfo; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.keyboard.ViewGroupFocusHelper; @@ -83,19 +81,15 @@ public class DragLayer extends InsettableFrameLayout { private boolean mHoverPointClosesFolder = false; private final Rect mHitRect = new Rect(); - private final Rect mHighlightRect = new Rect(); private TouchCompleteListener mTouchCompleteListener; private int mTopViewIndex; private int mChildCountOnLastUpdate = -1; - // Darkening scrim - private float mBackgroundAlpha = 0; - // Related to adjacent page hints private final ViewGroupFocusHelper mFocusIndicatorHelper; - private final WallpaperColorInfo mWallpaperColorInfo; + private final PageCutOutScrimDrawable mPageCutOutScrim; // Related to pinch-to-go-to-overview gesture. private PinchToOverviewListener mPinchListener = null; @@ -118,7 +112,8 @@ public class DragLayer extends InsettableFrameLayout { setChildrenDrawingOrderEnabled(true); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); - mWallpaperColorInfo = WallpaperColorInfo.getInstance(getContext()); + mPageCutOutScrim = new PageCutOutScrimDrawable(this); + mPageCutOutScrim.setCallback(this); } public void setup(Launcher launcher, DragController dragController, @@ -141,6 +136,11 @@ public class DragLayer extends InsettableFrameLayout { return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); } + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || who == mPageCutOutScrim; + } + public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) { mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled ? null : new PinchToOverviewListener(mLauncher); @@ -772,48 +772,23 @@ public class DragLayer extends InsettableFrameLayout { } public void invalidateScrim() { - if (mBackgroundAlpha > 0.0f) { + if (mPageCutOutScrim.getAlpha() > 0) { invalidate(); } } + public Drawable getScrim() { + return mPageCutOutScrim; + } + @Override protected void dispatchDraw(Canvas canvas) { // Draw the background below children. - if (mBackgroundAlpha > 0.0f) { - // Update the scroll position first to ensure scrim cutout is in the right place. - mLauncher.getWorkspace().computeScrollWithoutInvalidation(); - - int alpha = (int) (mBackgroundAlpha * 255); - CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout(); - canvas.save(); - if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) { - // Cut a hole in the darkening scrim on the page that should be highlighted, if any. - getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect); - canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE); - } - // for super light wallpaper it needs to be darken for contrast to workspace - // for dark wallpapers the text is white so darkening works as well - int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor()); - canvas.drawColor(ColorUtils.setAlphaComponent(color, alpha)); - canvas.restore(); - } - + mPageCutOutScrim.draw(canvas); mFocusIndicatorHelper.draw(canvas); super.dispatchDraw(canvas); } - public void setBackgroundAlpha(float alpha) { - if (alpha != mBackgroundAlpha) { - mBackgroundAlpha = alpha; - invalidate(); - } - } - - public float getBackgroundAlpha() { - return mBackgroundAlpha; - } - @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { View topView = AbstractFloatingView.getTopOpenView(mLauncher); diff --git a/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java b/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java new file mode 100644 index 0000000000..367124b5dd --- /dev/null +++ b/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.dragndrop; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.drawable.Drawable; +import android.support.v4.graphics.ColorUtils; + +import com.android.launcher3.CellLayout; +import com.android.launcher3.Launcher; +import com.android.launcher3.dynamicui.WallpaperColorInfo; + +/** + * Scrim drawable which draws a hole for the current drop target page. + */ +public class PageCutOutScrimDrawable extends Drawable { + + private final Rect mHighlightRect = new Rect(); + private final DragLayer mDragLayer; + private final Launcher mLauncher; + private final WallpaperColorInfo mWallpaperColorInfo; + + private int mAlpha = 0; + + public PageCutOutScrimDrawable(DragLayer dragLayer) { + mDragLayer = dragLayer; + mLauncher = Launcher.getLauncher(mDragLayer.getContext()); + mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher); + } + + @Override + public void draw(Canvas canvas) { + // Draw the background below children. + if (mAlpha > 0) { + // Update the scroll position first to ensure scrim cutout is in the right place. + mLauncher.getWorkspace().computeScrollWithoutInvalidation(); + CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout(); + canvas.save(); + if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) { + // Cut a hole in the darkening scrim on the page that should be highlighted, if any. + mDragLayer.getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect); + canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE); + } + // for super light wallpaper it needs to be darken for contrast to workspace + // for dark wallpapers the text is white so darkening works as well + int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor()); + canvas.drawColor(ColorUtils.setAlphaComponent(color, mAlpha)); + canvas.restore(); + } + } + + @Override + public void setAlpha(int alpha) { + if (mAlpha != alpha) { + mAlpha = alpha; + invalidateSelf(); + } + } + + @Override + public int getAlpha() { + return mAlpha; + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index a70a9bb1fe..1da78349d6 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -29,7 +29,7 @@ import android.os.Process; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.IconCache; -import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; @@ -83,8 +83,8 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo { public com.android.launcher3.ShortcutInfo createShortcutInfo() { // Total duration for the drop animation to complete. long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) + - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT + - mContext.getResources().getInteger(R.integer.config_overlayTransitionTime) / 2; + LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT + + LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; // Delay the actual accept() call until the drop animation is complete. return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest( mContext, mRequest, duration); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 4a60d4c563..fae420eef1 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -16,6 +16,8 @@ package com.android.launcher3.folder; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -1241,8 +1243,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter()); } - mLauncher.exitSpringLoadedDragMode(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); if (d.stateAnnouncer != null) { d.stateAnnouncer.completeAction(R.string.item_moved); } diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index cdb0ce3670..ec448e9978 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -16,6 +16,9 @@ package com.android.launcher3.folder; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -45,8 +48,6 @@ import com.android.launcher3.util.Themes; import java.util.List; -import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; - /** * Manages the opening and closing animations for a {@link Folder}. * @@ -77,19 +78,6 @@ public class FolderAnimationManager { private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); - private static final Property SCALE_PROPERTY = - new Property(Float.class, "scale") { - @Override - public Float get(View view) { - return view.getScaleX(); - } - - @Override - public void set(View view, Float scale) { - view.setScaleX(scale); - view.setScaleY(scale); - } - }; public FolderAnimationManager(Folder folder, boolean isOpening) { mFolder = folder; diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index d469eb2542..900781cbb3 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -296,7 +296,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { to = new Rect(); Workspace workspace = mLauncher.getWorkspace(); // Set cellLayout and this to it's final state to compute final animation locations - workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); + workspace.setFinalTransitionTransform(); float scaleX = getScaleX(); float scaleY = getScaleY(); setScaleX(1.0f); @@ -305,7 +305,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { // Finished computing final animation locations, restore current state setScaleX(scaleX); setScaleY(scaleY); - workspace.resetTransitionTransform((CellLayout) getParent().getParent()); + workspace.resetTransitionTransform(); } int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1); diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java index d5c6515c0b..1a6dade313 100644 --- a/src/com/android/launcher3/logging/UserEventDispatcher.java +++ b/src/com/android/launcher3/logging/UserEventDispatcher.java @@ -177,13 +177,11 @@ public class UserEventDispatcher { } public void logActionCommand(int command, int containerType) { - logActionCommand(command, containerType, 0); + logActionCommand(command, newContainerTarget(containerType)); } - public void logActionCommand(int command, int containerType, int pageIndex) { - LauncherEvent event = newLauncherEvent( - newCommandAction(command), newContainerTarget(containerType)); - event.srcTarget[0].pageIndex = pageIndex; + public void logActionCommand(int command, Target target) { + LauncherEvent event = newLauncherEvent(newCommandAction(command), target); dispatchUserEvent(event, null); } diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 78ecbc6218..53a9862580 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -336,17 +336,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { }); } - // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to - // get widget update signals. - if (!Utilities.ATLEAST_MARSHMALLOW && - (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) { - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.notifyWidgetProvidersChanged(); - } - }); - } else if (Utilities.ATLEAST_OREO && mOp == OP_ADD) { + if (Utilities.ATLEAST_OREO && mOp == OP_ADD) { // Load widgets for the new package. Changes due to app updates are handled through // AppWidgetHost events, this is just to initialize the long-press options. for (int i = 0; i < N; i++) { diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java index 15f5af6255..ec62764781 100644 --- a/src/com/android/launcher3/util/FlingAnimation.java +++ b/src/com/android/launcher3/util/FlingAnimation.java @@ -1,5 +1,7 @@ package com.android.launcher3.util; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; + import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -95,7 +97,7 @@ public class FlingAnimation implements AnimatorUpdateListener, Runnable { Runnable onAnimationEndRunnable = new Runnable() { @Override public void run() { - mLauncher.exitSpringLoadedDragMode(false, 0); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); mDropTarget.completeDrop(mDragObject); } }; diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index b6b3089b0d..61a733338f 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.widget; +import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -32,7 +34,6 @@ import android.view.animation.DecelerateInterpolator; import android.widget.Toast; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.ItemInfo; @@ -41,7 +42,6 @@ import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.folder.Folder; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -264,11 +264,16 @@ abstract class BaseWidgetSheet extends AbstractFloatingView @Override public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { targetParent.containerType = ContainerType.WIDGETS; + targetParent.cardinality = getElementsRowCount(); } @Override public final void logActionCommand(int command) { - // TODO: be more specific - mLauncher.getUserEventDispatcher().logActionCommand(command, ContainerType.WIDGETS); + Target target = newContainerTarget(ContainerType.WIDGETS); + target.cardinality = getElementsRowCount(); + mLauncher.getUserEventDispatcher().logActionCommand(command, target); } + + protected abstract int getElementsRowCount(); + } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 201bd1c9c0..3bb3fcb981 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -181,4 +181,9 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { setPadding(getPaddingLeft() + leftInset, getPaddingTop(), getPaddingRight() + rightInset, getPaddingBottom() + bottomInset); } + + @Override + protected int getElementsRowCount() { + return 1; + } } diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java index 72277a2536..a40ea1b644 100644 --- a/src/com/android/launcher3/widget/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -219,4 +219,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet sheet.open(animate); return sheet; } + + @Override + protected int getElementsRowCount() { + return mAdapter.getItemCount(); + } } From 4c7f215651a282ce75dec5e570a6e593be476cc5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 17 Oct 2017 17:17:16 -0700 Subject: [PATCH 054/885] Moving Workspace states into a separate file Bug: 67678570 Change-Id: I5c63b4df29ca0f58a0223fb1919abc132576a1b6 --- src/com/android/launcher3/Launcher.java | 21 ++--- .../android/launcher3/LauncherAnimUtils.java | 2 +- src/com/android/launcher3/LauncherState.java | 71 ++++++++++++++++ .../LauncherStateTransitionAnimation.java | 22 ++--- .../launcher3/PinchAnimationManager.java | 12 +-- .../launcher3/PinchThresholdManager.java | 8 +- src/com/android/launcher3/Workspace.java | 84 ++++++------------- .../WorkspaceStateTransitionAnimation.java | 17 ++-- .../launcher3/dragndrop/DragController.java | 4 +- .../PinShortcutRequestActivityInfo.java | 2 +- src/com/android/launcher3/folder/Folder.java | 4 +- 11 files changed, 140 insertions(+), 107 deletions(-) create mode 100644 src/com/android/launcher3/LauncherState.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a80e62b08f..24e708315d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -17,7 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; @@ -581,7 +581,7 @@ public class Launcher extends BaseActivity Runnable exitSpringLoaded = new Runnable() { @Override public void run() { - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); } }; @@ -761,7 +761,7 @@ public class Launcher extends BaseActivity @Override public void run() { completeAddAppWidget(appWidgetId, requestArgs, layout, null); - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); } }; } else if (resultCode == RESULT_CANCELED) { @@ -1712,7 +1712,7 @@ public class Launcher extends BaseActivity @Override public void run() { // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); } }; completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this)); @@ -2460,11 +2460,11 @@ public class Launcher extends BaseActivity public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) { boolean changed = mState != State.WORKSPACE || - mWorkspace.getState() != Workspace.State.NORMAL; + mWorkspace.getState() != LauncherState.NORMAL; if (changed || mAllAppsController.isTransitioning()) { mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), - Workspace.State.NORMAL, animated, onCompleteRunnable); + LauncherState.NORMAL, animated, onCompleteRunnable); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { @@ -2509,7 +2509,7 @@ public class Launcher extends BaseActivity } mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), - Workspace.State.OVERVIEW, animated, postAnimRunnable); + LauncherState.OVERVIEW, animated, postAnimRunnable); setState(State.WORKSPACE); // If animated from long press, then don't allow any of the controller in the drag @@ -2583,7 +2583,7 @@ public class Launcher extends BaseActivity InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), - Workspace.State.SPRING_LOADED, true /* animated */, + LauncherState.SPRING_LOADED, true /* animated */, null /* onCompleteRunnable */); setState(State.WORKSPACE_SPRING_LOADED); } @@ -2608,11 +2608,6 @@ public class Launcher extends BaseActivity mExitSpringLoadedModeRunnable = new Runnable() { @Override public void run() { - // 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 showWorkspace(true, onCompleteRunnable); mExitSpringLoadedModeRunnable = null; } diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 02f05c30e7..358511046c 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -37,7 +37,7 @@ public class LauncherAnimUtils { public static final int ALL_APPS_TRANSITION_MS = 320; public static final int OVERVIEW_TRANSITION_MS = 250; public static final int SPRING_LOADED_TRANSITION_MS = 150; - public static final int SPRING_LOADED_EXIT_SHORT_TIMEOUT = 500; + public static final int SPRING_LOADED_EXIT_DELAY = 500; public static final int SPRING_LOADED_EXIT_NEXT_FRAME = 0; static WeakHashMap sAnimators = new WeakHashMap(); diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java new file mode 100644 index 0000000000..8ddc491227 --- /dev/null +++ b/src/com/android/launcher3/LauncherState.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3; + +import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; +import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; + +import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; +import static com.android.launcher3.StateFlags.FLAG_DISABLE_ACCESSIBILITY; +import static com.android.launcher3.StateFlags.FLAG_HIDE_HOTSEAT; +import static com.android.launcher3.StateFlags.FLAG_MULTI_PAGE; +import static com.android.launcher3.StateFlags.FLAG_SHOW_SCRIM; + +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +interface StateFlags { + int FLAG_SHOW_SCRIM = 1 << 0; + int FLAG_MULTI_PAGE = 1 << 1; + int FLAG_HIDE_HOTSEAT = 1 << 2; + int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; +} + +/** + * Various states for launcher + */ +public enum LauncherState { + + NORMAL (ContainerType.WORKSPACE, 0, 0), + ALL_APPS (ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY), + SPRING_LOADED (ContainerType.WORKSPACE, SPRING_LOADED_TRANSITION_MS, + FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY), + OVERVIEW (ContainerType.OVERVIEW, OVERVIEW_TRANSITION_MS, + FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT); + + public final int containerType; + + public final boolean hasMultipleVisiblePages; + public final int workspaceAccessibilityFlag; + + // Properties related to state transition animation. + public final boolean hasScrim; + public final boolean hideHotseat; + public final int transitionDuration; + + LauncherState(int containerType, int transitionDuration, int flags) { + this.containerType = containerType; + this.transitionDuration = transitionDuration; + + this.hasScrim = (flags & FLAG_SHOW_SCRIM) != 0; + this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0; + this.hideHotseat = (flags & FLAG_HIDE_HOTSEAT) != 0; + this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0 + ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : IMPORTANT_FOR_ACCESSIBILITY_AUTO; + } +} diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 9c83e3c016..f07f1bf605 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -100,7 +100,7 @@ public class LauncherStateTransitionAnimation { cancelAnimation(); if (!animated) { - mLauncher.getWorkspace().setState(Workspace.State.NORMAL_HIDDEN); + mLauncher.getWorkspace().setState(LauncherState.ALL_APPS); mAllAppsController.finishPullUp(); toView.setTranslationX(0.0f); @@ -129,7 +129,7 @@ public class LauncherStateTransitionAnimation { mConfig.reset(); mAllAppsController.animateToAllApps(animation, mConfig); - mLauncher.getWorkspace().setStateWithAnimation(Workspace.State.NORMAL_HIDDEN, + mLauncher.getWorkspace().setStateWithAnimation(LauncherState.ALL_APPS, layerViews, animation, mConfig); Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); @@ -146,11 +146,11 @@ public class LauncherStateTransitionAnimation { * Starts an animation to the workspace from the current overlay view. */ public void startAnimationToWorkspace(final Launcher.State fromState, - final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, + final LauncherState fromWorkspaceState, final LauncherState toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { - if (toWorkspaceState != Workspace.State.NORMAL && - toWorkspaceState != Workspace.State.SPRING_LOADED && - toWorkspaceState != Workspace.State.OVERVIEW) { + if (toWorkspaceState != LauncherState.NORMAL && + toWorkspaceState != LauncherState.SPRING_LOADED && + toWorkspaceState != LauncherState.OVERVIEW) { Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); } @@ -166,8 +166,8 @@ public class LauncherStateTransitionAnimation { /** * Starts an animation to the workspace from the apps view. */ - private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, boolean animated, + private void startAnimationToWorkspaceFromAllApps(final LauncherState fromWorkspaceState, + final LauncherState toWorkspaceState, boolean animated, final Runnable onCompleteRunnable) { final AllAppsContainerView fromView = mLauncher.getAppsView(); // If for some reason our views aren't initialized, don't animate @@ -180,7 +180,7 @@ public class LauncherStateTransitionAnimation { cancelAnimation(); if (!animated) { - if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) { + if (fromWorkspaceState == LauncherState.ALL_APPS) { mAllAppsController.finishPullDown(); } fromView.setVisibility(View.GONE); @@ -233,8 +233,8 @@ public class LauncherStateTransitionAnimation { /** * Starts an animation to the workspace from another workspace state, e.g. normal to overview. */ - private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final boolean animated, + private void startAnimationToNewWorkspaceState(final LauncherState fromWorkspaceState, + final LauncherState toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { final View fromWorkspace = mLauncher.getWorkspace(); // Cancel the current animation diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java index 10f35bde3e..bbe8e89225 100644 --- a/src/com/android/launcher3/PinchAnimationManager.java +++ b/src/com/android/launcher3/PinchAnimationManager.java @@ -28,8 +28,8 @@ import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import static com.android.launcher3.Workspace.State.NORMAL; -import static com.android.launcher3.Workspace.State.OVERVIEW; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; /** * Manages the animations that play as the user pinches to/from overview mode. @@ -141,12 +141,12 @@ public class PinchAnimationManager { * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE}, * {@link PinchThresholdManager#THRESHOLD_TWO}, or * {@link PinchThresholdManager#THRESHOLD_THREE} - * @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}. - * @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}. + * @param startState {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}. + * @param goingTowards {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}. * Note that this doesn't have to be the opposite of startState; */ - public void animateThreshold(float threshold, Workspace.State startState, - Workspace.State goingTowards) { + public void animateThreshold(float threshold, LauncherState startState, + LauncherState goingTowards) { if (threshold == PinchThresholdManager.THRESHOLD_ONE) { if (startState == OVERVIEW) { animateOverviewPanelButtons(goingTowards == OVERVIEW); diff --git a/src/com/android/launcher3/PinchThresholdManager.java b/src/com/android/launcher3/PinchThresholdManager.java index 52aac17352..8cbc33d304 100644 --- a/src/com/android/launcher3/PinchThresholdManager.java +++ b/src/com/android/launcher3/PinchThresholdManager.java @@ -68,10 +68,10 @@ public class PinchThresholdManager { } if (mPassedThreshold != previousPassedThreshold) { - Workspace.State fromState = mWorkspace.isInOverviewMode() ? Workspace.State.OVERVIEW - : Workspace.State.NORMAL; - Workspace.State toState = mWorkspace.isInOverviewMode() ? Workspace.State.NORMAL - : Workspace.State.OVERVIEW; + LauncherState fromState = mWorkspace.isInOverviewMode() ? LauncherState.OVERVIEW + : LauncherState.NORMAL; + LauncherState toState = mWorkspace.isInOverviewMode() ? LauncherState.NORMAL + : LauncherState.OVERVIEW; float thresholdToAnimate = mPassedThreshold; if (mPassedThreshold < previousPassedThreshold) { // User reversed pinch, so heading back to the state that they started from. diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b57ce63757..213eaadb01 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -17,8 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; -import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; @@ -185,33 +184,6 @@ public class Workspace extends PagedView private SpringLoadedDragController mSpringLoadedDragController; private final float mOverviewModeShrinkFactor; - // State variable that indicates whether the pages are small (ie when you're - // in all apps or customize mode) - - public enum State { - NORMAL (false, ContainerType.WORKSPACE, false, 1, 0), - NORMAL_HIDDEN (false, ContainerType.ALLAPPS, false, 1, ALL_APPS_TRANSITION_MS), - SPRING_LOADED (true, ContainerType.WORKSPACE, true, 1, SPRING_LOADED_TRANSITION_MS), - OVERVIEW (true, ContainerType.OVERVIEW, true, 0, OVERVIEW_TRANSITION_MS); - - public final boolean hasMultipleVisiblePages; - public final int containerType; - - // Properties related to state transition animation. - public final boolean hasScrim; - public final float hotseatAlpha; - public final int transitionDuration; - - State(boolean hasMultipleVisiblePages, int containerType, - boolean hasScrim, float hotseatAlpha, int transitionDuration) { - this.hasMultipleVisiblePages = hasMultipleVisiblePages; - this.containerType = containerType; - this.hasScrim = hasScrim; - this.hotseatAlpha = hotseatAlpha; - this.transitionDuration = transitionDuration; - } - } - // Direction used for moving the workspace and hotseat UI public enum Direction { X (TRANSLATION_X), @@ -238,7 +210,7 @@ public class Workspace extends PagedView private final float[] mHotseatAlpha = new float[] {1, 1, 1}; @ViewDebug.ExportedProperty(category = "launcher") - private State mState = State.NORMAL; + private LauncherState mState = LauncherState.NORMAL; private boolean mIsSwitchingState = false; boolean mChildrenLayersEnabled = true; @@ -1447,16 +1419,16 @@ public class Workspace extends PagedView } public boolean workspaceInModalState() { - return mState != State.NORMAL; + return mState != LauncherState.NORMAL; } /** Returns whether a drag should be allowed to be started from the current workspace state. */ public boolean workspaceIconsCanBeDragged() { - return mState == State.NORMAL || mState == State.SPRING_LOADED; + return mState == LauncherState.NORMAL || mState == LauncherState.SPRING_LOADED; } private void updateChildrenLayersEnabled() { - boolean small = mState == State.OVERVIEW || mIsSwitchingState; + boolean small = mState == LauncherState.OVERVIEW || mIsSwitchingState; boolean enableChildrenLayers = small || isPageInTransition(); if (enableChildrenLayers != mChildrenLayersEnabled) { @@ -1576,7 +1548,7 @@ public class Workspace extends PagedView } public boolean isInOverviewMode() { - return mState == State.OVERVIEW; + return mState == LauncherState.OVERVIEW; } public void snapToPageFromOverView(int whichPage) { @@ -1626,9 +1598,9 @@ public class Workspace extends PagedView /** - * Sets the current workspace {@link State} and updates the UI without any animations + * Sets the current workspace {@link LauncherState} and updates the UI without any animations */ - public void setState(State toState) { + public void setState(LauncherState toState) { // Update the current state mState = toState; mStateTransitionAnimation.setState(mState); @@ -1640,11 +1612,11 @@ public class Workspace extends PagedView } /** - * Sets the current workspace {@link State}, while animating the UI + * Sets the current workspace {@link LauncherState}, then animates the UI */ - public void setStateWithAnimation(State toState, AnimationLayerSet layerViews, AnimatorSet anim, - AnimationConfig config) { - final State fromState = mState; + public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, + AnimatorSet anim, AnimationConfig config) { + final LauncherState fromState = mState; // Update the current state mState = toState; @@ -1662,7 +1634,7 @@ public class Workspace extends PagedView anim.addListener(listener); } - public State getState() { + public LauncherState getState() { return mState; } @@ -1673,14 +1645,12 @@ public class Workspace extends PagedView for (int i = 0; i < total; i++) { updateAccessibilityFlags((CellLayout) getPageAt(i), i); } - setImportantForAccessibility((mState == State.NORMAL || mState == State.OVERVIEW) - ? IMPORTANT_FOR_ACCESSIBILITY_AUTO - : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + setImportantForAccessibility(mState.workspaceAccessibilityFlag); } } private void updateAccessibilityFlags(CellLayout page, int pageNo) { - if (mState == State.OVERVIEW) { + if (mState == LauncherState.OVERVIEW) { page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); page.getShortcutsAndWidgets().setImportantForAccessibility( IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); @@ -1694,11 +1664,9 @@ public class Workspace extends PagedView page.setAccessibilityDelegate(mPagesAccessibilityDelegate); } } else { - int accessible = mState == State.NORMAL ? - IMPORTANT_FOR_ACCESSIBILITY_AUTO : - IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - page.getShortcutsAndWidgets().setImportantForAccessibility(accessible); + page.getShortcutsAndWidgets() + .setImportantForAccessibility(mState.workspaceAccessibilityFlag); page.setContentDescription(null); page.setAccessibilityDelegate(null); } @@ -1717,11 +1685,11 @@ public class Workspace extends PagedView } private void onStartStateTransition() { - if (mState == State.SPRING_LOADED) { + if (mState == LauncherState.SPRING_LOADED) { // Show the page indicator at the same time as the rest of the transition. showPageIndicatorAtCurrentScroll(); } - getPageIndicator().setShouldAutoHide(mState != State.SPRING_LOADED); + getPageIndicator().setShouldAutoHide(mState != LauncherState.SPRING_LOADED); } public void onEndStateTransition() { @@ -1730,7 +1698,7 @@ public class Workspace extends PagedView mForceDrawAdjacentPages = false; mTransitionProgress = 1; - if (mState == State.OVERVIEW) { + if (mState == LauncherState.OVERVIEW) { enableFreeScroll(); } else { disableFreeScroll(); @@ -1840,7 +1808,7 @@ public class Workspace extends PagedView private boolean transitionStateShouldAllowDrop() { return ((!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) && - (mState == State.NORMAL || mState == State.SPRING_LOADED)); + (mState == LauncherState.NORMAL || mState == LauncherState.SPRING_LOADED)); } /** @@ -2111,7 +2079,7 @@ public class Workspace extends PagedView dropTargetLayout, mTargetCell, distance, false, d.dragView) || addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, d, false)) { - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); return; } @@ -2250,7 +2218,7 @@ public class Workspace extends PagedView parent.onDropChild(cell); mLauncher.exitSpringLoadedDragMode( - SPRING_LOADED_EXIT_SHORT_TIMEOUT, onCompleteRunnable); + SPRING_LOADED_EXIT_DELAY, onCompleteRunnable); } if (d.stateAnnouncer != null && !droppedOnOriginalCell) { @@ -2782,7 +2750,7 @@ public class Workspace extends PagedView final long screenId = getIdForScreen(cellLayout); if (!mLauncher.isHotseatLayout(cellLayout) && screenId != getScreenIdForPageIndex(mCurrentPage) - && mState != State.SPRING_LOADED) { + && mState != LauncherState.SPRING_LOADED) { snapToPage(getPageIndexForScreenId(screenId)); } @@ -2857,7 +2825,7 @@ public class Workspace extends PagedView animationStyle, finalView, true); } else { // This is for other drag/drop cases, like dragging from All Apps - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); View view; @@ -3601,7 +3569,7 @@ public class Workspace extends PagedView @Override public boolean enableFreeScroll() { - if (getState() == State.OVERVIEW) { + if (getState() == LauncherState.OVERVIEW) { return super.enableFreeScroll(); } else { Log.w(TAG, "enableFreeScroll called but not in overview: state=" + getState()); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 9d9e9fb1a9..8e215b0336 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -33,7 +33,6 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; -import com.android.launcher3.Workspace.State; import com.android.launcher3.anim.AnimationLayerSet; /** @@ -163,17 +162,17 @@ public class WorkspaceStateTransitionAnimation { mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); } - public void setState(Workspace.State toState) { + public void setState(LauncherState toState) { setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER); } - public void setStateWithAnimation(Workspace.State fromState, Workspace.State toState, + public void setStateWithAnimation(LauncherState fromState, LauncherState toState, AnimatorSet anim, AnimationLayerSet layerViews, AnimationConfig config) { - long duration = config.getDuration(toState == State.NORMAL + long duration = config.getDuration(toState == LauncherState.NORMAL ? fromState.transitionDuration : toState.transitionDuration); - AnimatedPropertySetter proertSetter = + AnimatedPropertySetter propertySetter = new AnimatedPropertySetter(duration, layerViews, anim); - setWorkspaceProperty(toState, proertSetter); + setWorkspaceProperty(toState, propertySetter); } public float getFinalScale() { @@ -183,7 +182,7 @@ public class WorkspaceStateTransitionAnimation { /** * Starts a transition animation for the workspace. */ - private void setWorkspaceProperty(Workspace.State state, PropertySetter propertySetter) { + private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter) { // Update the workspace state int finalBackgroundAlpha = state.hasScrim ? 255 : 0; @@ -211,13 +210,13 @@ public class WorkspaceStateTransitionAnimation { // Only animate the page alpha when we actually fade pages if (mWorkspaceFadeInAdjacentScreens) { - float finalAlpha = state == State.NORMAL && i != toPage ? 0 : 1f; + float finalAlpha = state == LauncherState.NORMAL && i != toPage ? 0 : 1f; propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, finalAlpha, mZoomInInterpolator); } } - float finalHotseatAlpha = state.hotseatAlpha; + float finalHotseatAlpha = state.hideHotseat ? 0f : 1f; // This is true when transitioning between: // - Overview <-> Workspace diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 8386f8ffac..10bd67de8e 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -16,7 +16,7 @@ package com.android.launcher3.dragndrop; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import android.content.ComponentName; import android.content.res.Resources; @@ -263,7 +263,7 @@ public class DragController implements DragDriver.EventListener, TouchController if (!accepted) { // If it was not accepted, cleanup the state. If it was accepted, it is the // responsibility of the drop target to cleanup the state. - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); mDragObject.deferDragViewCleanupPostAnimation = false; } diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index 1da78349d6..52167bb8bf 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -83,7 +83,7 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo { public com.android.launcher3.ShortcutInfo createShortcutInfo() { // Total duration for the drop animation to complete. long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) + - LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT + + LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY + LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; // Delay the actual accept() call until the drop animation is complete. return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest( diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index fae420eef1..fcc4f0efc3 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -16,7 +16,7 @@ package com.android.launcher3.folder; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_SHORT_TIMEOUT; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -1243,7 +1243,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter()); } - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_SHORT_TIMEOUT); + mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); if (d.stateAnnouncer != null) { d.stateAnnouncer.completeAction(R.string.item_moved); } From f9403d92fa872a9f9f487540dc989df7b80db8b8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Oct 2017 10:55:56 -0700 Subject: [PATCH 055/885] Removing state definition from Launcher State is only maintained is workspace and is used for all the UI Bug: 67678570 Change-Id: Ie3cce5a1f4ada3913d0480f7918c27460dacbd34 --- src/com/android/launcher3/CellLayout.java | 3 +- src/com/android/launcher3/Launcher.java | 126 ++++++------------ src/com/android/launcher3/LauncherState.java | 25 +++- .../LauncherStateTransitionAnimation.java | 14 +- .../launcher3/PinchThresholdManager.java | 13 +- .../launcher3/PinchToOverviewListener.java | 22 +-- src/com/android/launcher3/Workspace.java | 6 +- .../allapps/AllAppsContainerView.java | 3 +- .../allapps/AllAppsTransitionController.java | 12 +- .../android/launcher3/config/BaseFlags.java | 2 - 10 files changed, 98 insertions(+), 128 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 3643971a42..2ab08b2eb4 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -52,7 +52,6 @@ import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.util.CellAndSpan; @@ -342,7 +341,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // 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() + if (mLauncher.isInState(LauncherState.OVERVIEW) && mStylusEventHelper.onMotionEvent(ev)) { return true; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 24e708315d..4efb911b77 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -79,7 +79,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.OvershootInterpolator; @@ -186,11 +185,6 @@ public class Launcher extends BaseActivity */ protected static final int REQUEST_LAST = 100; - private static final int SOFT_INPUT_MODE_DEFAULT = - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; - private static final int SOFT_INPUT_MODE_ALL_APPS = - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - // The Intent extra that defines whether to ignore the launch animation static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; @@ -208,10 +202,6 @@ public class Launcher extends BaseActivity static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; - /** The different states that Launcher can be in. */ - enum State { WORKSPACE, WORKSPACE_SPRING_LOADED, APPS} - - @Thunk State mState = State.WORKSPACE; @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -601,7 +591,7 @@ public class Launcher extends BaseActivity } return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { - if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) { + if (resultCode == RESULT_OK && isInState(LauncherState.OVERVIEW)) { // User could have free-scrolled between pages before picking a wallpaper; make sure // we move to the closest one now. mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen()); @@ -1001,8 +991,7 @@ public class Launcher extends BaseActivity AbstractFloatingView.closeAllOpenViews(this); // Show the overview mode if we are on the workspace - if (mState == State.WORKSPACE && !mWorkspace.isInOverviewMode() && - !mWorkspace.isSwitchingState()) { + if (isInState(LauncherState.NORMAL) && !mWorkspace.isSwitchingState()) { mOverviewPanel.requestFocus(); showOverviewMode(true, true /* requestButtonFocus */); } @@ -1023,6 +1012,10 @@ public class Launcher extends BaseActivity Selection.setSelection(mDefaultKeySsb, 0); } + public boolean isInState(LauncherState state) { + return mWorkspace.getState() == state; + } + /** * Restores the previous state, if it exists. * @@ -1033,12 +1026,15 @@ public class Launcher extends BaseActivity return; } - int stateOrdinal = savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()); - State[] stateValues = State.values(); - State state = (stateOrdinal >= 0 && stateOrdinal < stateValues.length) - ? stateValues[stateOrdinal] : State.WORKSPACE; - if (state == State.APPS) { - showAppsView(false /* animated */); + int stateOrdinal = savedState.getInt(RUNTIME_STATE, LauncherState.NORMAL.ordinal()); + LauncherState[] stateValues = LauncherState.values(); + LauncherState state = stateValues[stateOrdinal]; + if (!state.doNotRestore) { + if (state == LauncherState.ALL_APPS) { + showAppsView(false /* animated */); + } else { + // TODO: Add logic for other states + } } PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS); @@ -1419,8 +1415,8 @@ public class Launcher extends BaseActivity != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); // Check this condition before handling isActionMain, as this will get reset. - boolean shouldMoveToDefaultScreen = alreadyOnHome && - mState == State.WORKSPACE && AbstractFloatingView.getTopOpenView(this) == null; + boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(LauncherState.NORMAL) + && AbstractFloatingView.getTopOpenView(this) == null; boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); if (isActionMain) { @@ -1499,7 +1495,7 @@ public class Launcher extends BaseActivity outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); } - outState.putInt(RUNTIME_STATE, mState.ordinal()); + outState.putInt(RUNTIME_STATE, mWorkspace.getState().ordinal()); AbstractFloatingView widgets = AbstractFloatingView @@ -1893,10 +1889,10 @@ public class Launcher extends BaseActivity AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); if (topView != null) { topView.onBackPressed(); - } else if (isAppsViewVisible()) { + } else if (isInState(LauncherState.ALL_APPS)) { ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS); showWorkspace(true); - } else if (mWorkspace.isInOverviewMode()) { + } else if (isInState(LauncherState.OVERVIEW)) { ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW); showWorkspace(true); } else { @@ -1922,7 +1918,7 @@ public class Launcher extends BaseActivity } if (v instanceof Workspace) { - if (mWorkspace.isInOverviewMode()) { + if (isInState(LauncherState.OVERVIEW)) { getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH, LauncherLogProto.Action.Direction.NONE, LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage()); @@ -1932,7 +1928,7 @@ public class Launcher extends BaseActivity } if (v instanceof CellLayout) { - if (mWorkspace.isInOverviewMode()) { + if (isInState(LauncherState.OVERVIEW)) { int page = mWorkspace.indexOfChild(v); getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH, LauncherLogProto.Action.Direction.NONE, @@ -2010,7 +2006,7 @@ public class Launcher extends BaseActivity */ protected void onClickAllAppsButton(View v) { if (LOGD) Log.d(TAG, "onClickAllAppsButton"); - if (!isAppsViewVisible()) { + if (!isInState(LauncherState.ALL_APPS)) { getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, ControlType.ALL_APPS_BUTTON); showAppsView(true /* animated */); @@ -2340,13 +2336,13 @@ public class Launcher extends BaseActivity public boolean onLongClick(View v) { if (!isDraggingEnabled()) return false; if (isWorkspaceLocked()) return false; - if (mState != State.WORKSPACE) return false; + if (!isInState(LauncherState.NORMAL)) return false; boolean ignoreLongPressToOverview = mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX); if (v instanceof Workspace) { - if (!mWorkspace.isInOverviewMode()) { + if (!isInState(LauncherState.OVERVIEW)) { if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) { getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS, Action.Direction.NONE, ContainerType.WORKSPACE, @@ -2377,7 +2373,7 @@ public class Launcher extends BaseActivity if (!mDragController.isDragging()) { if (itemUnderLongClick == null) { // User long pressed on empty space - if (mWorkspace.isInOverviewMode()) { + if (isInState(LauncherState.OVERVIEW)) { mWorkspace.startReordering(v); getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS, Action.Direction.NONE, ContainerType.OVERVIEW); @@ -2427,17 +2423,6 @@ public class Launcher extends BaseActivity } } - /** - * For overridden classes. - */ - public boolean isAllAppsVisible() { - return isAppsViewVisible(); - } - - public boolean isAppsViewVisible() { - return mState == State.APPS; - } - @Override public void onTrimMemory(int level) { super.onTrimMemory(level); @@ -2459,11 +2444,10 @@ public class Launcher extends BaseActivity } public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) { - boolean changed = mState != State.WORKSPACE || - mWorkspace.getState() != LauncherState.NORMAL; + boolean changed = !isInState(LauncherState.NORMAL); if (changed || mAllAppsController.isTransitioning()) { mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), + mStateTransitionAnimation.startAnimationToWorkspace( LauncherState.NORMAL, animated, onCompleteRunnable); // Set focus to the AppsCustomize button @@ -2472,9 +2456,6 @@ public class Launcher extends BaseActivity } } - // Change the state *after* we've called all the transition code - setState(State.WORKSPACE); - if (changed) { // Send an accessibility event to announce the context change getWindow().getDecorView() @@ -2508,32 +2489,14 @@ public class Launcher extends BaseActivity }; } mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), + mStateTransitionAnimation.startAnimationToWorkspace( LauncherState.OVERVIEW, animated, postAnimRunnable); - setState(State.WORKSPACE); // If animated from long press, then don't allow any of the controller in the drag // layer to intercept any remaining touch. mWorkspace.requestDisallowInterceptTouchEvent(animated); } - private void setState(State state) { - this.mState = state; - updateSoftInputMode(); - } - - private void updateSoftInputMode() { - if (FeatureFlags.LAUNCHER3_UPDATE_SOFT_INPUT_MODE) { - final int mode; - if (isAppsViewVisible()) { - mode = SOFT_INPUT_MODE_ALL_APPS; - } else { - mode = SOFT_INPUT_MODE_DEFAULT; - } - getWindow().setSoftInputMode(mode); - } - } - /** * Shows the apps view. * @@ -2544,8 +2507,8 @@ public class Launcher extends BaseActivity public boolean showAppsView(boolean animated) { markAppsViewShown(); - if (!(mState == State.WORKSPACE || - (mState == State.APPS && mAllAppsController.isTransitioning()))) { + if (!(isInState(LauncherState.NORMAL) || + (isInState(LauncherState.ALL_APPS) && mAllAppsController.isTransitioning()))) { return false; } @@ -2558,7 +2521,6 @@ public class Launcher extends BaseActivity mStateTransitionAnimation.startAnimationToAllApps(animated); // Change the state *after* we've called all the transition code - setState(State.APPS); AbstractFloatingView.closeAllOpenViews(this); // Send an accessibility event to announce the context change @@ -2568,8 +2530,9 @@ public class Launcher extends BaseActivity } public void enterSpringLoadedDragMode() { - if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name())); - if (isStateSpringLoaded()) { + if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", + mWorkspace.getState().name())); + if (isInState(LauncherState.SPRING_LOADED)) { return; } @@ -2582,10 +2545,9 @@ public class Launcher extends BaseActivity // in spring loaded mode InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); - mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), + mStateTransitionAnimation.startAnimationToWorkspace( LauncherState.SPRING_LOADED, true /* animated */, null /* onCompleteRunnable */); - setState(State.WORKSPACE_SPRING_LOADED); } public void exitSpringLoadedDragMode(int delay) { @@ -2593,7 +2555,7 @@ public class Launcher extends BaseActivity } public void exitSpringLoadedDragMode(int delay, final Runnable onCompleteRunnable) { - if (!isStateSpringLoaded()) return; + if (!isInState(LauncherState.SPRING_LOADED)) return; // Unlock rotation lock unlockScreenOrientation(false); @@ -2615,17 +2577,13 @@ public class Launcher extends BaseActivity mHandler.postDelayed(mExitSpringLoadedModeRunnable, delay); } - boolean isStateSpringLoaded() { - return mState == State.WORKSPACE_SPRING_LOADED; - } - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); final List text = event.getText(); text.clear(); // Populate event with a fake title based on the current state. - if (mState == State.APPS) { + if (isInState(LauncherState.ALL_APPS)) { text.add(getString(R.string.all_apps_button_label)); } else if (mWorkspace != null) { text.add(mWorkspace.getCurrentPageDescription()); @@ -3182,7 +3140,7 @@ public class Launcher extends BaseActivity if (mAppsView != null) { Executor pendingExecutor = getPendingExecutor(); - if (pendingExecutor != null && mState != State.APPS) { + if (pendingExecutor != null && !isInState(LauncherState.ALL_APPS)) { // Wait until the fade in animation has finished before setting all apps list. pendingExecutor.execute(r); return; @@ -3389,8 +3347,8 @@ public class Launcher extends BaseActivity } private boolean shouldShowDiscoveryBounce() { - UserManagerCompat um = UserManagerCompat.getInstance(this); - return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) && !um.isDemoUser(); + return isInState(LauncherState.NORMAL) && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) + && !UserManagerCompat.getInstance(this).isDemoUser(); } /** @@ -3448,7 +3406,7 @@ public class Launcher extends BaseActivity List data, Menu menu, int deviceId) { ArrayList shortcutInfos = new ArrayList<>(); - if (mState == State.WORKSPACE) { + if (isInState(LauncherState.NORMAL)) { shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label), KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); } @@ -3474,7 +3432,7 @@ public class Launcher extends BaseActivity if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { switch (keyCode) { case KeyEvent.KEYCODE_A: - if (mState == State.WORKSPACE) { + if (isInState(LauncherState.NORMAL)) { showAppsView(true); return true; } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 8ddc491227..c51b920812 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; import static com.android.launcher3.StateFlags.FLAG_DISABLE_ACCESSIBILITY; +import static com.android.launcher3.StateFlags.FLAG_DO_NOT_RESTORE; import static com.android.launcher3.StateFlags.FLAG_HIDE_HOTSEAT; import static com.android.launcher3.StateFlags.FLAG_MULTI_PAGE; import static com.android.launcher3.StateFlags.FLAG_SHOW_SCRIM; @@ -33,6 +34,7 @@ interface StateFlags { int FLAG_MULTI_PAGE = 1 << 1; int FLAG_HIDE_HOTSEAT = 1 << 2; int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; + int FLAG_DO_NOT_RESTORE = 1 << 4; } /** @@ -40,16 +42,32 @@ interface StateFlags { */ public enum LauncherState { - NORMAL (ContainerType.WORKSPACE, 0, 0), + NORMAL (ContainerType.WORKSPACE, 0, FLAG_DO_NOT_RESTORE), ALL_APPS (ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY), SPRING_LOADED (ContainerType.WORKSPACE, SPRING_LOADED_TRANSITION_MS, - FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY), + FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE), OVERVIEW (ContainerType.OVERVIEW, OVERVIEW_TRANSITION_MS, - FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT); + FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT | FLAG_DO_NOT_RESTORE); + /** + * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher} + */ public final int containerType; + /** + * True if the state can be persisted across activity restarts. + */ + public final boolean doNotRestore; + + /** + * True if workspace has multiple pages visible. + */ public final boolean hasMultipleVisiblePages; + + /** + * Accessibility flag for workspace and its pages. + * @see android.view.View#setImportantForAccessibility(int) + */ public final int workspaceAccessibilityFlag; // Properties related to state transition animation. @@ -67,5 +85,6 @@ public enum LauncherState { this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : IMPORTANT_FOR_ACCESSIBILITY_AUTO; + this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0; } } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index f07f1bf605..40d5495657 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -145,8 +145,7 @@ public class LauncherStateTransitionAnimation { /** * Starts an animation to the workspace from the current overlay view. */ - public void startAnimationToWorkspace(final Launcher.State fromState, - final LauncherState fromWorkspaceState, final LauncherState toWorkspaceState, + public void startAnimationToWorkspace(final LauncherState toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { if (toWorkspaceState != LauncherState.NORMAL && toWorkspaceState != LauncherState.SPRING_LOADED && @@ -154,12 +153,11 @@ public class LauncherStateTransitionAnimation { Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); } - if (fromState == Launcher.State.APPS || mAllAppsController.isTransitioning()) { - startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, - animated, onCompleteRunnable); + if (mLauncher.isInState(LauncherState.ALL_APPS) || mAllAppsController.isTransitioning()) { + startAnimationToWorkspaceFromAllApps(mLauncher.getWorkspace().getState(), + toWorkspaceState, animated, onCompleteRunnable); } else { - startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState, - animated, onCompleteRunnable); + startAnimationToNewWorkspaceState(toWorkspaceState, animated, onCompleteRunnable); } } @@ -233,7 +231,7 @@ public class LauncherStateTransitionAnimation { /** * Starts an animation to the workspace from another workspace state, e.g. normal to overview. */ - private void startAnimationToNewWorkspaceState(final LauncherState fromWorkspaceState, + private void startAnimationToNewWorkspaceState( final LauncherState toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { final View fromWorkspace = mLauncher.getWorkspace(); diff --git a/src/com/android/launcher3/PinchThresholdManager.java b/src/com/android/launcher3/PinchThresholdManager.java index 8cbc33d304..4937633320 100644 --- a/src/com/android/launcher3/PinchThresholdManager.java +++ b/src/com/android/launcher3/PinchThresholdManager.java @@ -29,12 +29,14 @@ public class PinchThresholdManager { public static final float THRESHOLD_TWO = 0.70f; public static final float THRESHOLD_THREE = 0.95f; - private Workspace mWorkspace; + private final Workspace mWorkspace; + private final Launcher mLauncher; private float mPassedThreshold = THRESHOLD_ZERO; public PinchThresholdManager(Workspace workspace) { mWorkspace = workspace; + mLauncher = mWorkspace.mLauncher; } /** @@ -50,7 +52,7 @@ public class PinchThresholdManager { */ public float updateAndAnimatePassedThreshold(float progress, PinchAnimationManager animationManager) { - if (!mWorkspace.isInOverviewMode()) { + if (!mLauncher.isInState(LauncherState.OVERVIEW)) { // Invert the progress, because going from workspace to overview is 1 to 0. progress = 1f - progress; } @@ -68,10 +70,9 @@ public class PinchThresholdManager { } if (mPassedThreshold != previousPassedThreshold) { - LauncherState fromState = mWorkspace.isInOverviewMode() ? LauncherState.OVERVIEW - : LauncherState.NORMAL; - LauncherState toState = mWorkspace.isInOverviewMode() ? LauncherState.NORMAL - : LauncherState.OVERVIEW; + LauncherState fromState = mLauncher.getWorkspace().getState(); + LauncherState toState = mLauncher.isInState(LauncherState.OVERVIEW) + ? LauncherState.NORMAL : LauncherState.OVERVIEW; float thresholdToAnimate = mPassedThreshold; if (mPassedThreshold < previousPassedThreshold) { // User reversed pinch, so heading back to the state that they started from. diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index 2a5899c31e..a1288b48ee 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -80,7 +80,7 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG @Override public boolean onScaleBegin(ScaleGestureDetector detector) { - if (mLauncher.mState != Launcher.State.WORKSPACE) { + if (!mLauncher.isInState(LauncherState.NORMAL)) { // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. return false; } @@ -107,9 +107,9 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG return false; } - mPreviousProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS; + mPreviousProgress = mLauncher.isInState(LauncherState.OVERVIEW) ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS; mPreviousTimeMillis = System.currentTimeMillis(); - mInterpolator = mWorkspace.isInOverviewMode() ? new LogDecelerateInterpolator(100, 0) + mInterpolator = mLauncher.isInState(LauncherState.OVERVIEW) ? new LogDecelerateInterpolator(100, 0) : new LogAccelerateInterpolator(100, 0); mPinchStarted = true; mWorkspace.onPrepareStateTransition(true); @@ -122,21 +122,21 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG float progressVelocity = mProgressDelta / mTimeDelta; float passedThreshold = mThresholdManager.getPassedThreshold(); - boolean isFling = mWorkspace.isInOverviewMode() && progressVelocity >= FLING_VELOCITY - || !mWorkspace.isInOverviewMode() && progressVelocity <= -FLING_VELOCITY; + boolean isFling = mLauncher.isInState(LauncherState.OVERVIEW) && progressVelocity >= FLING_VELOCITY + || !mLauncher.isInState(LauncherState.OVERVIEW) && progressVelocity <= -FLING_VELOCITY; boolean shouldCancelPinch = !isFling && passedThreshold < PinchThresholdManager.THRESHOLD_ONE; // If we are going towards overview, mPreviousProgress is how much further we need to // go, since it is going from 1 to 0. If we are going to workspace, we want // 1 - mPreviousProgress. float remainingProgress = mPreviousProgress; - if (mWorkspace.isInOverviewMode() || shouldCancelPinch) { + if (mLauncher.isInState(LauncherState.OVERVIEW) || shouldCancelPinch) { remainingProgress = 1f - mPreviousProgress; } int duration = computeDuration(remainingProgress, progressVelocity); if (shouldCancelPinch) { cancelPinch(mPreviousProgress, duration); } else if (passedThreshold < PinchThresholdManager.THRESHOLD_THREE) { - float toProgress = mWorkspace.isInOverviewMode() ? + float toProgress = mLauncher.isInState(LauncherState.OVERVIEW) ? WORKSPACE_PROGRESS : OVERVIEW_PROGRESS; mAnimationManager.animateToProgress(mPreviousProgress, toProgress, duration, mThresholdManager); @@ -165,7 +165,7 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG private void cancelPinch(float currentProgress, int duration) { if (mPinchCanceled) return; mPinchCanceled = true; - float toProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS; + float toProgress = mLauncher.isInState(LauncherState.OVERVIEW) ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS; mAnimationManager.animateToProgress(currentProgress, toProgress, duration, mThresholdManager); mPinchStarted = false; @@ -182,15 +182,15 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG } float pinchDist = detector.getCurrentSpan() - detector.getPreviousSpan(); - if (pinchDist < 0 && mWorkspace.isInOverviewMode() || - pinchDist > 0 && !mWorkspace.isInOverviewMode()) { + if (pinchDist < 0 && mLauncher.isInState(LauncherState.OVERVIEW) || + pinchDist > 0 && !mLauncher.isInState(LauncherState.OVERVIEW)) { // Pinching the wrong way, so ignore. return false; } // Pinch distance must equal the workspace width before switching states. int pinchDistanceToCompleteTransition = mWorkspace.getWidth(); float overviewScale = mWorkspace.getOverviewModeShrinkFactor(); - float initialWorkspaceScale = mWorkspace.isInOverviewMode() ? overviewScale : 1f; + float initialWorkspaceScale = mLauncher.isInState(LauncherState.OVERVIEW) ? overviewScale : 1f; float pinchScale = initialWorkspaceScale + pinchDist / pinchDistanceToCompleteTransition; // Bound the scale between the overview scale and the normal workspace scale (1f). pinchScale = Math.max(overviewScale, Math.min(pinchScale, 1f)); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 213eaadb01..671ba07720 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1351,7 +1351,7 @@ public class Workspace extends PagedView @Override public void announceForAccessibility(CharSequence text) { // Don't announce if apps is on top of us. - if (!mLauncher.isAppsViewVisible()) { + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { super.announceForAccessibility(text); } } @@ -1547,10 +1547,6 @@ public class Workspace extends PagedView enableLayoutTransitions(); } - public boolean isInOverviewMode() { - return mState == LauncherState.OVERVIEW; - } - public void snapToPageFromOverView(int whichPage) { snapToPage(whichPage, OVERVIEW_TRANSITION_MS, new ZoomInInterpolator()); } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 4de31f4c27..a1f37ba222 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -38,6 +38,7 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; import com.android.launcher3.PromiseAppInfo; import com.android.launcher3.R; import com.android.launcher3.anim.SpringAnimationHandler; @@ -292,7 +293,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource, @Override public boolean onLongClick(final View v) { // When we have exited all apps or are in transition, disregard long clicks - if (!mLauncher.isAppsViewVisible() || + if (!mLauncher.isInState(LauncherState.ALL_APPS) || mLauncher.getWorkspace().isSwitchingState()) return false; // Return if global dragging is not enabled or we are already dragging if (!mLauncher.isDraggingEnabled()) return false; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 5642b4c011..2243a9cd87 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -11,13 +11,13 @@ import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; -import android.view.animation.Animation; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -113,9 +113,9 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = false; mTouchEventStartedOnHotseat = mLauncher.getDragLayer().isEventOverHotseat(ev); - if (!mLauncher.isAllAppsVisible() && mLauncher.getWorkspace().workspaceInModalState()) { + if (!mLauncher.isInState(LauncherState.ALL_APPS) && mLauncher.getWorkspace().workspaceInModalState()) { mNoIntercept = true; - } else if (mLauncher.isAllAppsVisible() && + } else if (mLauncher.isInState(LauncherState.ALL_APPS) && !mAppsView.shouldContainerScroll(ev)) { mNoIntercept = true; } else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { @@ -127,7 +127,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect boolean ignoreSlopWhenSettling = false; if (mDetector.isIdleState()) { - if (mLauncher.isAllAppsVisible()) { + if (mLauncher.isInState(LauncherState.ALL_APPS)) { directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE; } else { directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE; @@ -212,7 +212,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (velocity < 0) { calculateDuration(velocity, mAppsView.getTranslationY()); - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { mLauncher.getUserEventDispatcher().logActionOnContainer( Action.Touch.FLING, Action.Direction.UP, @@ -235,7 +235,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mLauncher.showWorkspace(true); } else { calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { mLauncher.getUserEventDispatcher().logActionOnContainer( Action.Touch.SWIPE, Action.Direction.UP, diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 8a1bc6388e..f01923f7d2 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -35,8 +35,6 @@ abstract class BaseFlags { public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false; // When enabled allows to use any point on the fast scrollbar to start dragging. public static final boolean LAUNCHER3_DIRECT_SCROLL = true; - // When enabled while all-apps open, the soft input will be set to adjust resize . - public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false; // When enabled the promise icon is visible in all apps while installation an app. public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false; // When enabled allows use of physics based motions in the Launcher. From cd7c0aad5fd7245981fe615103ea62c85239a2bb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 19 Oct 2017 12:36:27 -0700 Subject: [PATCH 056/885] Changing LauncherState to a class to allow adding custom functionality Bug: 67678570 Change-Id: I777e335e9fdf7014b041addff6b8e54fb94167bb --- src/com/android/launcher3/Launcher.java | 8 +-- src/com/android/launcher3/LauncherState.java | 52 ++++++++++++------- .../WorkspaceStateTransitionAnimation.java | 23 ++++---- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4efb911b77..c2d391684f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1026,7 +1026,7 @@ public class Launcher extends BaseActivity return; } - int stateOrdinal = savedState.getInt(RUNTIME_STATE, LauncherState.NORMAL.ordinal()); + int stateOrdinal = savedState.getInt(RUNTIME_STATE, LauncherState.NORMAL.ordinal); LauncherState[] stateValues = LauncherState.values(); LauncherState state = stateValues[stateOrdinal]; if (!state.doNotRestore) { @@ -1495,7 +1495,7 @@ public class Launcher extends BaseActivity outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); } - outState.putInt(RUNTIME_STATE, mWorkspace.getState().ordinal()); + outState.putInt(RUNTIME_STATE, mWorkspace.getState().ordinal); AbstractFloatingView widgets = AbstractFloatingView @@ -2336,7 +2336,7 @@ public class Launcher extends BaseActivity public boolean onLongClick(View v) { if (!isDraggingEnabled()) return false; if (isWorkspaceLocked()) return false; - if (!isInState(LauncherState.NORMAL)) return false; + if (!isInState(LauncherState.NORMAL) && !isInState(LauncherState.OVERVIEW)) return false; boolean ignoreLongPressToOverview = mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX); @@ -2531,7 +2531,7 @@ public class Launcher extends BaseActivity public void enterSpringLoadedDragMode() { if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", - mWorkspace.getState().name())); + mWorkspace.getState().ordinal)); if (isInState(LauncherState.SPRING_LOADED)) { return; } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index c51b920812..4619f4e58e 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -21,34 +21,41 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; -import static com.android.launcher3.StateFlags.FLAG_DISABLE_ACCESSIBILITY; -import static com.android.launcher3.StateFlags.FLAG_DO_NOT_RESTORE; -import static com.android.launcher3.StateFlags.FLAG_HIDE_HOTSEAT; -import static com.android.launcher3.StateFlags.FLAG_MULTI_PAGE; -import static com.android.launcher3.StateFlags.FLAG_SHOW_SCRIM; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -interface StateFlags { - int FLAG_SHOW_SCRIM = 1 << 0; - int FLAG_MULTI_PAGE = 1 << 1; - int FLAG_HIDE_HOTSEAT = 1 << 2; - int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; - int FLAG_DO_NOT_RESTORE = 1 << 4; -} +import java.util.Arrays; + /** * Various states for launcher */ -public enum LauncherState { +public class LauncherState { - NORMAL (ContainerType.WORKSPACE, 0, FLAG_DO_NOT_RESTORE), - ALL_APPS (ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY), - SPRING_LOADED (ContainerType.WORKSPACE, SPRING_LOADED_TRANSITION_MS, - FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE), - OVERVIEW (ContainerType.OVERVIEW, OVERVIEW_TRANSITION_MS, + protected static final int FLAG_SHOW_SCRIM = 1 << 0; + protected static final int FLAG_MULTI_PAGE = 1 << 1; + protected static final int FLAG_HIDE_HOTSEAT = 1 << 2; + protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; + protected static final int FLAG_DO_NOT_RESTORE = 1 << 4; + + private static final LauncherState[] sAllStates = new LauncherState[4]; + + public static LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, + 0, FLAG_DO_NOT_RESTORE); + + public static LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS, + ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY); + + public static LauncherState SPRING_LOADED = new LauncherState(2, ContainerType.WORKSPACE, + SPRING_LOADED_TRANSITION_MS, + FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE); + + public static LauncherState OVERVIEW = new LauncherState(3, ContainerType.OVERVIEW, + OVERVIEW_TRANSITION_MS, FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT | FLAG_DO_NOT_RESTORE); + public final int ordinal; + /** * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher} */ @@ -75,7 +82,7 @@ public enum LauncherState { public final boolean hideHotseat; public final int transitionDuration; - LauncherState(int containerType, int transitionDuration, int flags) { + public LauncherState(int id, int containerType, int transitionDuration, int flags) { this.containerType = containerType; this.transitionDuration = transitionDuration; @@ -86,5 +93,12 @@ public enum LauncherState { ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : IMPORTANT_FOR_ACCESSIBILITY_AUTO; this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0; + + this.ordinal = id; + sAllStates[id] = this; + } + + public static LauncherState[] values() { + return Arrays.copyOf(sAllStates, sAllStates.length); } } diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 8e215b0336..af56fd77c6 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -18,6 +18,8 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherState.SPRING_LOADED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -187,18 +189,15 @@ public class WorkspaceStateTransitionAnimation { int finalBackgroundAlpha = state.hasScrim ? 255 : 0; final float finalWorkspaceTranslationY; - switch (state) { - case OVERVIEW: - mNewScale = mOverviewModeShrinkFactor; - finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY(); - break; - case SPRING_LOADED: - mNewScale = mSpringLoadedShrinkFactor; - finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY(); - break; - default: - mNewScale = 1f; - finalWorkspaceTranslationY = 0; + if (state == OVERVIEW) { + mNewScale = mOverviewModeShrinkFactor; + finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY(); + } else if (state == SPRING_LOADED) { + mNewScale = mSpringLoadedShrinkFactor; + finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY(); + } else { + mNewScale = 1f; + finalWorkspaceTranslationY = 0; } int toPage = mWorkspace.getPageNearestToCenterOfScreen(); From d79dd20e8833e95c5a6b6c8b26cc2732cf6528c5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 19 Oct 2017 11:22:07 -0700 Subject: [PATCH 057/885] Deteriorating PinchToZoom interaction Instead of using a custom animation, simply using the LauncherStateTransition animation to control the transition. This keeps the state transitions sane. Bug: 65598794 Bug: 67678570 Change-Id: Id1be0ca98b6a0ecf9ab7feadb23408cbef526d0e --- .../LauncherStateTransitionAnimation.java | 18 +- .../launcher3/PinchAnimationManager.java | 245 ------------------ .../launcher3/PinchThresholdManager.java | 94 ------- .../launcher3/PinchToOverviewListener.java | 240 ++++++++--------- .../launcher3/dragndrop/DragLayer.java | 2 +- 5 files changed, 119 insertions(+), 480 deletions(-) delete mode 100644 src/com/android/launcher3/PinchAnimationManager.java delete mode 100644 src/com/android/launcher3/PinchThresholdManager.java diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 40d5495657..0e6cead0c4 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -234,10 +234,8 @@ public class LauncherStateTransitionAnimation { private void startAnimationToNewWorkspaceState( final LauncherState toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { - final View fromWorkspace = mLauncher.getWorkspace(); // Cancel the current animation cancelAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); if (!animated) { @@ -249,11 +247,20 @@ public class LauncherStateTransitionAnimation { return; } + final AnimatorSet animation = + createAnimationToNewWorkspace(toWorkspaceState, onCompleteRunnable); + mLauncher.getWorkspace().post(new StartAnimRunnable(animation, null)); + mCurrentAnimation = animation; + } + + protected AnimatorSet createAnimationToNewWorkspace(LauncherState state, + final Runnable onCompleteRunnable) { + cancelAnimation(); + final AnimationLayerSet layerViews = new AnimationLayerSet(); final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); mConfig.reset(); - mLauncher.getWorkspace().setStateWithAnimation(toWorkspaceState, - layerViews, animation, mConfig); + mLauncher.getWorkspace().setStateWithAnimation(state, layerViews, animation, mConfig); animation.addListener(new AnimatorListenerAdapter() { @Override @@ -268,8 +275,7 @@ public class LauncherStateTransitionAnimation { } }); animation.addListener(layerViews); - fromWorkspace.post(new StartAnimRunnable(animation, null)); - mCurrentAnimation = animation; + return animation; } /** diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java deleted file mode 100644 index bbe8e89225..0000000000 --- a/src/com/android/launcher3/PinchAnimationManager.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2016 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.ObjectAnimator; -import android.animation.ValueAnimator; -import android.util.Log; -import android.view.View; -import android.view.animation.LinearInterpolator; - -import com.android.launcher3.anim.AnimationLayerSet; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; - -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; - -/** - * Manages the animations that play as the user pinches to/from overview mode. - * - * It will look like this pinching in: - * - Workspace scales down - * - At some threshold 1, hotseat and QSB fade out (full animation) - * - At a later threshold 2, panel buttons fade in and scrim fades in - * - At a final threshold 3, snap to overview - * - * Pinching out: - * - Workspace scales up - * - At threshold 1, panel buttons fade out - * - At threshold 2, hotseat and QSB fade in and scrim fades out - * - At threshold 3, snap to workspace - * - * @see PinchToOverviewListener - * @see PinchThresholdManager - */ -public class PinchAnimationManager { - private static final String TAG = "PinchAnimationManager"; - - private static final int THRESHOLD_ANIM_DURATION = 150; - private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator(); - - private static final int INDEX_HOTSEAT = 0; - private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1; - private static final int INDEX_SCRIM = 2; - - private final Animator[] mAnimators = new Animator[3]; - - private Launcher mLauncher; - private Workspace mWorkspace; - - private float mOverviewScale; - private float mOverviewTranslationY; - private int mNormalOverviewTransitionDuration; - private boolean mIsAnimating; - - public PinchAnimationManager(Launcher launcher) { - mLauncher = launcher; - mWorkspace = launcher.mWorkspace; - - mOverviewScale = mWorkspace.getOverviewModeShrinkFactor(); - mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY(); - mNormalOverviewTransitionDuration = LauncherAnimUtils.OVERVIEW_TRANSITION_MS; - } - - public int getNormalOverviewTransitionDuration() { - return mNormalOverviewTransitionDuration; - } - - /** - * Interpolate from {@param currentProgress} to {@param toProgress}, calling - * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1, - * the default overview transition duration is used. - */ - public void animateToProgress(float currentProgress, float toProgress, int duration, - final PinchThresholdManager thresholdManager) { - if (duration == -1) { - duration = mNormalOverviewTransitionDuration; - } - ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float pinchProgress = (Float) animation.getAnimatedValue(); - setAnimationProgress(pinchProgress); - thresholdManager.updateAndAnimatePassedThreshold(pinchProgress, - PinchAnimationManager.this); - } - } - ); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mIsAnimating = false; - thresholdManager.reset(); - mWorkspace.onEndStateTransition(); - } - }); - animator.setDuration(duration).start(); - mIsAnimating = true; - } - - public boolean isAnimating() { - return mIsAnimating; - } - - /** - * Animates to the specified progress. This should be called repeatedly throughout the pinch - * gesture to run animations that interpolate throughout the gesture. - * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace. - */ - public void setAnimationProgress(float interpolatedProgress) { - float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale; - float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY; - mWorkspace.setScaleX(interpolatedScale); - mWorkspace.setScaleY(interpolatedScale); - mWorkspace.setTranslationY(interpolatedTranslationY); - int alpha = (int) ((1f - interpolatedProgress) * 255); - setOverviewPanelsAlpha(alpha, 0); - } - - /** - * Animates certain properties based on which threshold was passed, and in what direction. The - * starting state must also be taken into account because the thresholds mean different things - * when going from workspace to overview and vice versa. - * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE}, - * {@link PinchThresholdManager#THRESHOLD_TWO}, or - * {@link PinchThresholdManager#THRESHOLD_THREE} - * @param startState {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}. - * @param goingTowards {@link LauncherState#NORMAL} or {@link LauncherState#OVERVIEW}. - * Note that this doesn't have to be the opposite of startState; - */ - public void animateThreshold(float threshold, LauncherState startState, - LauncherState goingTowards) { - if (threshold == PinchThresholdManager.THRESHOLD_ONE) { - if (startState == OVERVIEW) { - animateOverviewPanelButtons(goingTowards == OVERVIEW); - } else if (startState == NORMAL) { - animateHotseatAndQsb(goingTowards == NORMAL); - } - } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) { - if (startState == OVERVIEW) { - animateHotseatAndQsb(goingTowards == NORMAL); - animateScrim(goingTowards == OVERVIEW); - } else if (startState == NORMAL) { - animateOverviewPanelButtons(goingTowards == OVERVIEW); - animateScrim(goingTowards == OVERVIEW); - } - } else if (threshold == PinchThresholdManager.THRESHOLD_THREE) { - // Passing threshold 3 ends the pinch and snaps to the new state. - if (startState == OVERVIEW && goingTowards == NORMAL) { - mLauncher.getUserEventDispatcher().logActionOnContainer( - Action.Touch.PINCH, Action.Direction.NONE, - ContainerType.OVERVIEW, mWorkspace.getCurrentPage()); - mLauncher.showWorkspace(true); - mWorkspace.snapToPage(mWorkspace.getCurrentPage()); - } else if (startState == NORMAL && goingTowards == OVERVIEW) { - mLauncher.getUserEventDispatcher().logActionOnContainer( - Action.Touch.PINCH, Action.Direction.NONE, - ContainerType.WORKSPACE, mWorkspace.getCurrentPage()); - mLauncher.showOverviewMode(true); - } - } else { - Log.e(TAG, "Received unknown threshold to animate: " + threshold); - } - } - - private void setOverviewPanelsAlpha(int alpha, int duration) { - int childCount = mWorkspace.getChildCount(); - for (int i = 0; i < childCount; i++) { - final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); - if (duration == 0) { - cl.getScrimBackground().setAlpha(alpha); - } else { - ObjectAnimator.ofInt(cl.getScrimBackground(), - LauncherAnimUtils.DRAWABLE_ALPHA, alpha).setDuration(duration).start(); - } - } - } - - private void animateHotseatAndQsb(boolean show) { - startAnimator(INDEX_HOTSEAT, - mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION); - } - - private void animateOverviewPanelButtons(boolean show) { - animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show); - } - - private void animateScrim(boolean show) { - int endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0; - startAnimator(INDEX_SCRIM, ObjectAnimator.ofInt( - mLauncher.getDragLayer().getScrim(), LauncherAnimUtils.DRAWABLE_ALPHA, endValue), - mNormalOverviewTransitionDuration); - } - - private void animateShowHideView(int index, final View view, boolean show) { - Animator animator = ObjectAnimator.ofFloat(view, View.ALPHA, show ? 1 : 0); - animator.addListener(new AnimationLayerSet(view)); - if (show) { - view.setVisibility(View.VISIBLE); - } else { - animator.addListener(new AnimatorListenerAdapter() { - private boolean mCancelled = false; - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!mCancelled) { - view.setVisibility(View.INVISIBLE); - } - } - }); - } - startAnimator(index, animator, THRESHOLD_ANIM_DURATION); - } - - private void startAnimator(int index, Animator animator, long duration) { - if (mAnimators[index] != null) { - mAnimators[index].cancel(); - } - mAnimators[index] = animator; - mAnimators[index].setInterpolator(INTERPOLATOR); - mAnimators[index].setDuration(duration).start(); - } -} diff --git a/src/com/android/launcher3/PinchThresholdManager.java b/src/com/android/launcher3/PinchThresholdManager.java deleted file mode 100644 index 4937633320..0000000000 --- a/src/com/android/launcher3/PinchThresholdManager.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2016 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; - -/** - * Keeps track of when thresholds are passed during a pinch gesture, - * used to inform {@link PinchAnimationManager} throughout. - * - * @see PinchToOverviewListener - * @see PinchAnimationManager - */ -public class PinchThresholdManager { - public static final float THRESHOLD_ZERO = 0.0f; - public static final float THRESHOLD_ONE = 0.40f; - public static final float THRESHOLD_TWO = 0.70f; - public static final float THRESHOLD_THREE = 0.95f; - - private final Workspace mWorkspace; - private final Launcher mLauncher; - - private float mPassedThreshold = THRESHOLD_ZERO; - - public PinchThresholdManager(Workspace workspace) { - mWorkspace = workspace; - mLauncher = mWorkspace.mLauncher; - } - - /** - * Uses the pinch progress to determine whether a threshold has been passed, - * and asks the {@param animationManager} to animate if so. - * @param progress From 0 to 1, where 0 is overview and 1 is workspace. - * @param animationManager Animates the threshold change if one is passed. - * @return The last passed threshold, one of - * {@link PinchThresholdManager#THRESHOLD_ZERO}, - * {@link PinchThresholdManager#THRESHOLD_ONE}, - * {@link PinchThresholdManager#THRESHOLD_TWO}, or - * {@link PinchThresholdManager#THRESHOLD_THREE} - */ - public float updateAndAnimatePassedThreshold(float progress, - PinchAnimationManager animationManager) { - if (!mLauncher.isInState(LauncherState.OVERVIEW)) { - // Invert the progress, because going from workspace to overview is 1 to 0. - progress = 1f - progress; - } - - float previousPassedThreshold = mPassedThreshold; - - if (progress < THRESHOLD_ONE) { - mPassedThreshold = THRESHOLD_ZERO; - } else if (progress < THRESHOLD_TWO) { - mPassedThreshold = THRESHOLD_ONE; - } else if (progress < THRESHOLD_THREE) { - mPassedThreshold = THRESHOLD_TWO; - } else { - mPassedThreshold = THRESHOLD_THREE; - } - - if (mPassedThreshold != previousPassedThreshold) { - LauncherState fromState = mLauncher.getWorkspace().getState(); - LauncherState toState = mLauncher.isInState(LauncherState.OVERVIEW) - ? LauncherState.NORMAL : LauncherState.OVERVIEW; - float thresholdToAnimate = mPassedThreshold; - if (mPassedThreshold < previousPassedThreshold) { - // User reversed pinch, so heading back to the state that they started from. - toState = fromState; - thresholdToAnimate = previousPassedThreshold; - } - animationManager.animateThreshold(thresholdToAnimate, fromState, toState); - } - return mPassedThreshold; - } - - public float getPassedThreshold() { - return mPassedThreshold; - } - - public void reset() { - mPassedThreshold = THRESHOLD_ZERO; - } -} diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index a1288b48ee..47113c9386 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -16,49 +16,52 @@ package com.android.launcher3; -import android.animation.TimeInterpolator; -import android.content.Context; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.annotation.TargetApi; +import android.os.Build; +import android.util.Range; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import android.view.ScaleGestureDetector.OnScaleGestureListener; import com.android.launcher3.util.TouchController; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * Detects pinches and animates the Workspace to/from overview mode. - * - * Usage: Pass MotionEvents to onInterceptTouchEvent() and onTouchEvent(). This class will handle - * the pinch detection, and use {@link PinchAnimationManager} to handle the animations. - * - * @see PinchThresholdManager - * @see PinchAnimationManager */ -public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener - implements TouchController { - private static final float OVERVIEW_PROGRESS = 0f; - private static final float WORKSPACE_PROGRESS = 1f; +@TargetApi(Build.VERSION_CODES.O) +public class PinchToOverviewListener + implements TouchController, OnScaleGestureListener, Runnable { + + private static final float ACCEPT_THRESHOLD = 0.65f; /** * The velocity threshold at which a pinch will be completed instead of canceled, - * even if the first threshold has not been passed. Measured in progress / millisecond + * even if the first threshold has not been passed. Measured in scale / millisecond */ - private static final float FLING_VELOCITY = 0.003f; + private static final float FLING_VELOCITY = 0.001f; - private ScaleGestureDetector mPinchDetector; + private final ScaleGestureDetector mPinchDetector; private Launcher mLauncher; private Workspace mWorkspace = null; private boolean mPinchStarted = false; - private float mPreviousProgress; - private float mProgressDelta; - private long mPreviousTimeMillis; - private long mTimeDelta; - private boolean mPinchCanceled = false; - private TimeInterpolator mInterpolator; - private PinchThresholdManager mThresholdManager; - private PinchAnimationManager mAnimationManager; + private AnimatorSet mCurrentAnimation; + private float mCurrentScale; + private Range mDurationRange; + private boolean mShouldGoToFinalState; + + private LauncherState mToState; public PinchToOverviewListener(Launcher launcher) { mLauncher = launcher; - mPinchDetector = new ScaleGestureDetector((Context) mLauncher, this); + mPinchDetector = new ScaleGestureDetector(mLauncher, this); } public boolean onControllerInterceptTouchEvent(MotionEvent ev) { @@ -67,24 +70,17 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG } public boolean onControllerTouchEvent(MotionEvent ev) { - if (mPinchStarted) { - if (ev.getPointerCount() > 2) { - // Using more than two fingers causes weird behavior, so just cancel the pinch. - cancelPinch(mPreviousProgress, -1); - } else { - return mPinchDetector.onTouchEvent(ev); - } - } - return false; + return mPinchDetector.onTouchEvent(ev); } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { - if (!mLauncher.isInState(LauncherState.NORMAL)) { + if (!mLauncher.isInState(LauncherState.NORMAL) + && !mLauncher.isInState(LauncherState.OVERVIEW)) { // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. return false; } - if (mAnimationManager != null && mAnimationManager.isAnimating()) { + if (mCurrentAnimation != null) { // Don't listen for the pinch gesture if we are already animating from a previous one. return false; } @@ -94,8 +90,6 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG } if (mWorkspace == null) { mWorkspace = mLauncher.getWorkspace(); - mThresholdManager = new PinchThresholdManager(mWorkspace); - mAnimationManager = new PinchAnimationManager(mLauncher); } if (mWorkspace.isSwitchingState() || mWorkspace.mScrollInteractionBegan) { // Don't listen for the pinch gesture while switching state, as it will cause a jump @@ -107,109 +101,87 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG return false; } - mPreviousProgress = mLauncher.isInState(LauncherState.OVERVIEW) ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS; - mPreviousTimeMillis = System.currentTimeMillis(); - mInterpolator = mLauncher.isInState(LauncherState.OVERVIEW) ? new LogDecelerateInterpolator(100, 0) - : new LogAccelerateInterpolator(100, 0); - mPinchStarted = true; - mWorkspace.onPrepareStateTransition(true); - return true; - } - - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - super.onScaleEnd(detector); - - float progressVelocity = mProgressDelta / mTimeDelta; - float passedThreshold = mThresholdManager.getPassedThreshold(); - boolean isFling = mLauncher.isInState(LauncherState.OVERVIEW) && progressVelocity >= FLING_VELOCITY - || !mLauncher.isInState(LauncherState.OVERVIEW) && progressVelocity <= -FLING_VELOCITY; - boolean shouldCancelPinch = !isFling && passedThreshold < PinchThresholdManager.THRESHOLD_ONE; - // If we are going towards overview, mPreviousProgress is how much further we need to - // go, since it is going from 1 to 0. If we are going to workspace, we want - // 1 - mPreviousProgress. - float remainingProgress = mPreviousProgress; - if (mLauncher.isInState(LauncherState.OVERVIEW) || shouldCancelPinch) { - remainingProgress = 1f - mPreviousProgress; - } - int duration = computeDuration(remainingProgress, progressVelocity); - if (shouldCancelPinch) { - cancelPinch(mPreviousProgress, duration); - } else if (passedThreshold < PinchThresholdManager.THRESHOLD_THREE) { - float toProgress = mLauncher.isInState(LauncherState.OVERVIEW) ? - WORKSPACE_PROGRESS : OVERVIEW_PROGRESS; - mAnimationManager.animateToProgress(mPreviousProgress, toProgress, duration, - mThresholdManager); - } else { - mThresholdManager.reset(); - mWorkspace.onEndStateTransition(); - } - mPinchStarted = false; - mPinchCanceled = false; - } - - /** - * Compute the amount of time required to complete the transition based on the current pinch - * speed. If this time is too long, instead return the normal duration, ignoring the speed. - */ - private int computeDuration(float remainingProgress, float progressVelocity) { - float progressSpeed = Math.abs(progressVelocity); - int remainingMillis = (int) (remainingProgress / progressSpeed); - return Math.min(remainingMillis, mAnimationManager.getNormalOverviewTransitionDuration()); - } - - /** - * Cancels the current pinch, returning back to where the pinch started (either workspace or - * overview). If duration is -1, the default overview transition duration is used. - */ - private void cancelPinch(float currentProgress, int duration) { - if (mPinchCanceled) return; - mPinchCanceled = true; - float toProgress = mLauncher.isInState(LauncherState.OVERVIEW) ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS; - mAnimationManager.animateToProgress(currentProgress, toProgress, duration, - mThresholdManager); - mPinchStarted = false; - } - - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (mThresholdManager.getPassedThreshold() == PinchThresholdManager.THRESHOLD_THREE) { - // We completed the pinch, so stop listening to further movement until user lets go. - return true; - } if (mLauncher.getDragController().isDragging()) { mLauncher.getDragController().cancelDrag(); } - float pinchDist = detector.getCurrentSpan() - detector.getPreviousSpan(); - if (pinchDist < 0 && mLauncher.isInState(LauncherState.OVERVIEW) || - pinchDist > 0 && !mLauncher.isInState(LauncherState.OVERVIEW)) { - // Pinching the wrong way, so ignore. - return false; - } - // Pinch distance must equal the workspace width before switching states. - int pinchDistanceToCompleteTransition = mWorkspace.getWidth(); - float overviewScale = mWorkspace.getOverviewModeShrinkFactor(); - float initialWorkspaceScale = mLauncher.isInState(LauncherState.OVERVIEW) ? overviewScale : 1f; - float pinchScale = initialWorkspaceScale + pinchDist / pinchDistanceToCompleteTransition; - // Bound the scale between the overview scale and the normal workspace scale (1f). - pinchScale = Math.max(overviewScale, Math.min(pinchScale, 1f)); - // Progress ranges from 0 to 1, where 0 corresponds to the overview scale and 1 - // corresponds to the normal workspace scale (1f). - float progress = (pinchScale - overviewScale) / (1f - overviewScale); - float interpolatedProgress = mInterpolator.getInterpolation(progress); + mToState = mLauncher.isInState(LauncherState.OVERVIEW) + ? LauncherState.NORMAL : LauncherState.OVERVIEW; + mCurrentAnimation = mLauncher.mStateTransitionAnimation + .createAnimationToNewWorkspace(mToState, this); + mPinchStarted = true; + mCurrentScale = 1; + mDurationRange = Range.create(0, LauncherAnimUtils.OVERVIEW_TRANSITION_MS); + mShouldGoToFinalState = false; - mAnimationManager.setAnimationProgress(interpolatedProgress); - float passedThreshold = mThresholdManager.updateAndAnimatePassedThreshold( - interpolatedProgress, mAnimationManager); - if (passedThreshold == PinchThresholdManager.THRESHOLD_THREE) { - return true; + dispatchOnStart(mCurrentAnimation); + return true; + } + + @Override + public void run() { + mCurrentAnimation = null; + mPinchStarted = false; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + if (mShouldGoToFinalState) { + mCurrentAnimation.start(); + } else { + mCurrentAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mToState == LauncherState.OVERVIEW) { + mLauncher.showWorkspace(false); + } else { + mLauncher.showOverviewMode(false); + } + } + }); + mCurrentAnimation.reverse(); + } + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + mCurrentScale = detector.getScaleFactor() * mCurrentScale; + + // If we are zooming out, inverse the mCurrentScale so that animationFraction = [0, 1] + // 0 => Animation complete + // 1=> Animation started + float animationFraction = mToState == + LauncherState.OVERVIEW ? mCurrentScale : (1 / mCurrentScale); + + float velocity = (1 - detector.getScaleFactor()) / detector.getTimeDelta(); + if (Math.abs(velocity) >= FLING_VELOCITY) { + LauncherState toState = velocity > 0 ? LauncherState.OVERVIEW : LauncherState.NORMAL; + mShouldGoToFinalState = toState == mToState; + } else { + mShouldGoToFinalState = animationFraction <= ACCEPT_THRESHOLD; } - mProgressDelta = interpolatedProgress - mPreviousProgress; - mPreviousProgress = interpolatedProgress; - mTimeDelta = System.currentTimeMillis() - mPreviousTimeMillis; - mPreviousTimeMillis = System.currentTimeMillis(); - return false; + // Move the transition animation to that duration. + long playPosition = mDurationRange.clamp( + (int) ((1 - animationFraction) * mDurationRange.getUpper())); + mCurrentAnimation.setCurrentPlayTime(playPosition); + + return true; + } + + private void dispatchOnStart(Animator animator) { + for (AnimatorListener l : nonNullList(animator.getListeners())) { + l.onAnimationStart(animator); + } + + if (animator instanceof AnimatorSet) { + for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) { + dispatchOnStart(anim); + } + } + } + + private static List nonNullList(ArrayList list) { + return list == null ? Collections.emptyList() : list; } } \ No newline at end of file diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index bc5aafc0a9..f2bad6b624 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -143,7 +143,7 @@ public class DragLayer extends InsettableFrameLayout { public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) { mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled - ? null : new PinchToOverviewListener(mLauncher); + || !Utilities.ATLEAST_OREO ? null : new PinchToOverviewListener(mLauncher); } public boolean isEventOverHotseat(MotionEvent ev) { From 7fb3ccc4a535e8fb2929929538e32ab0bc6ffcc1 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 17 Oct 2017 15:39:46 -0700 Subject: [PATCH 058/885] Log time spent on different UI surfaces - When swipe happens on worskpace, elapsed container ms is reset - Fling DOWN is also logged so that we now know how much time was spent on all apps screen - If screen off or power button trigger onPause, log this event. Bug: 67745115 Change-Id: Ie3a0090c78195a4a028de9935131e9e034dcf48a --- protos/launcher_log.proto | 2 + src/com/android/launcher3/Launcher.java | 16 ++++++-- .../allapps/AllAppsTransitionController.java | 39 ++++++++++++------- .../launcher3/logging/LoggerUtils.java | 12 ++++-- .../logging/UserEventDispatcher.java | 5 +++ 5 files changed, 53 insertions(+), 21 deletions(-) diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index 0bbec188db..de74fcec5f 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -132,7 +132,9 @@ message Action { // not using the HOME_INTENT CANCEL = 3; // Indicates that a confirmation screen was cancelled CONFIRM = 4; // Indicates thata confirmation screen was accepted + STOP = 5; // Indicates onStop() was called (screen time out, power off) } + optional Type type = 1; optional Touch touch = 2; optional Direction dir = 3; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4efb911b77..eda5bb11ce 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -292,6 +292,8 @@ public class Launcher extends BaseActivity public ViewGroupFocusHelper mFocusHandler; private boolean mRotationEnabled = false; + private boolean mAppLaunchSuccess; + @Thunk void setOrientation() { if (mRotationEnabled) { unlockScreenOrientation(true); @@ -781,6 +783,10 @@ public class Launcher extends BaseActivity mAppWidgetHost.stopListening(); } + if (!mAppLaunchSuccess) { + getUserEventDispatcher().logActionCommand(Action.Command.STOP, + mWorkspace.getState().containerType); + } NotificationListener.removeNotificationsChangedListener(); } @@ -827,6 +833,7 @@ public class Launcher extends BaseActivity super.onResume(); TraceHelper.partitionSection("ON_RESUME", "superCall"); + mAppLaunchSuccess = false; getUserEventDispatcher().resetElapsedSessionMillis(); mPaused = false; if (mOnResumeNeedsLoad) { @@ -2115,7 +2122,6 @@ public class Launcher extends BaseActivity throw new IllegalArgumentException("Input must have a valid intent"); } startActivitySafely(v, intent, item); - getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115 } /** @@ -2276,9 +2282,10 @@ public class Launcher extends BaseActivity } public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { + mAppLaunchSuccess = false; if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); - return false; + return mAppLaunchSuccess; } // Only launch using the new animation if the shortcut has not opted out (this is a // private contract between launcher and may be ignored in the future). @@ -2318,12 +2325,13 @@ public class Launcher extends BaseActivity btv.setStayPressed(true); setOnResumeCallback(btv); } - return true; + mAppLaunchSuccess = true; + getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115 } catch (ActivityNotFoundException|SecurityException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e); } - return false; + return mAppLaunchSuccess; } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 2243a9cd87..9247c54f7c 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -26,7 +26,8 @@ import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; @@ -207,16 +208,11 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect final int containerType = mTouchEventStartedOnHotseat ? ContainerType.HOTSEAT : ContainerType.WORKSPACE; - if (fling) { if (velocity < 0) { calculateDuration(velocity, mAppsView.getTranslationY()); - if (!mLauncher.isInState(LauncherState.ALL_APPS)) { - mLauncher.getUserEventDispatcher().logActionOnContainer( - Action.Touch.FLING, - Action.Direction.UP, - containerType); + logSwipeOnContainer(Touch.FLING, Direction.UP, containerType); } mLauncher.showAppsView(true /* animated */); if (hasSpringAnimationHandler()) { @@ -226,26 +222,43 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } } else { calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); - mLauncher.showWorkspace(true); + if (mLauncher.isInState(LauncherState.ALL_APPS)) { + logSwipeOnContainer(Touch.FLING, Direction.DOWN, ContainerType.ALLAPPS); + } + mLauncher.showWorkspace(true /* animated */); } // snap to top or bottom using the release velocity } else { if (mAppsView.getTranslationY() > mShiftRange / 2) { calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); - mLauncher.showWorkspace(true); + if (mLauncher.isInState(LauncherState.ALL_APPS)) { + logSwipeOnContainer(Touch.SWIPE, Direction.DOWN, ContainerType.ALLAPPS); + } + mLauncher.showWorkspace(true /* animated */); } else { calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); if (!mLauncher.isInState(LauncherState.ALL_APPS)) { - mLauncher.getUserEventDispatcher().logActionOnContainer( - Action.Touch.SWIPE, - Action.Direction.UP, - containerType); + logSwipeOnContainer(Touch.SWIPE, Direction.UP, containerType); } mLauncher.showAppsView(true /* animated */); } } } + /** + * Important, make sure that this method is called only when actual launcher state transition + * happen and not when user swipes in one direction only to cancel that swipe seconds later. + * + * @param touchType Swipe or Fling + * @param direction Up or Down + * @param containerType Workspace or Allapps + */ + private void logSwipeOnContainer(int touchType, int direction, int containerType) { + mLauncher.getUserEventDispatcher().logActionOnContainer( + touchType, direction, containerType, + mLauncher.getWorkspace().getCurrentPage()); + } + public boolean isTransitioning() { return mDetector.isDraggingOrSettling(); } diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java index 81333b1a22..00ee0096ad 100644 --- a/src/com/android/launcher3/logging/LoggerUtils.java +++ b/src/com/android/launcher3/logging/LoggerUtils.java @@ -91,7 +91,8 @@ public class LoggerUtils { return getFieldName(t.controlType, ControlType.class); case Target.Type.CONTAINER: String str = getFieldName(t.containerType, ContainerType.class); - if (t.containerType == ContainerType.WORKSPACE) { + if (t.containerType == ContainerType.WORKSPACE || + t.containerType == ContainerType.HOTSEAT) { str += " id=" + t.pageIndex; } else if (t.containerType == ContainerType.FOLDER) { str += " grid(" + t.gridX + "," + t.gridY+ ")"; @@ -105,13 +106,16 @@ public class LoggerUtils { private static String getItemStr(Target t) { String typeStr = getFieldName(t.itemType, ItemType.class); if (t.packageNameHash != 0) { - typeStr += ", packageHash=" + t.packageNameHash + ", predictiveRank=" + t.predictedRank; + typeStr += ", packageHash=" + t.packageNameHash; } if (t.componentHash != 0) { - typeStr += ", componentHash=" + t.componentHash + ", predictiveRank=" + t.predictedRank; + typeStr += ", componentHash=" + t.componentHash; } if (t.intentHash != 0) { - typeStr += ", intentHash=" + t.intentHash + ", predictiveRank=" + t.predictedRank; + typeStr += ", intentHash=" + t.intentHash; + } + if (t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) { + typeStr += ", predictiveRank=" + t.predictedRank; } return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY + "), pageIdx=" + t.pageIndex; diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java index 1a6dade313..243dbeae67 100644 --- a/src/com/android/launcher3/logging/UserEventDispatcher.java +++ b/src/com/android/launcher3/logging/UserEventDispatcher.java @@ -232,6 +232,10 @@ public class UserEventDispatcher { event.action.dir = dir; event.srcTarget[0].pageIndex = pageIndex; dispatchUserEvent(event, null); + + if (action == Action.Touch.SWIPE) { + resetElapsedContainerMillis(); + } } public void logActionOnItem(int action, int dir, int itemType) { @@ -323,6 +327,7 @@ public class UserEventDispatcher { ev.actionDurationMillis); log += "\n isInLandscapeMode " + ev.isInLandscapeMode; log += "\n isInMultiWindowMode " + ev.isInMultiWindowMode; + log += "\n"; Log.d(TAG, log); } From c99cb174c3369c8c192efc12d97dd8e9f6d189b9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 19 Oct 2017 16:15:09 -0700 Subject: [PATCH 059/885] Moving some state specific logic to subclass of LauncherState Bug: 67678570 Change-Id: I1316f91c9f19bd572e4a0da67a22fa8921e1dcf9 --- res/values/config.xml | 2 - src/com/android/launcher3/DeviceProfile.java | 2 +- src/com/android/launcher3/Launcher.java | 60 +----- src/com/android/launcher3/LauncherState.java | 24 ++- src/com/android/launcher3/PagedView.java | 5 +- src/com/android/launcher3/Utilities.java | 6 + src/com/android/launcher3/Workspace.java | 178 ++++++------------ .../WorkspaceStateTransitionAnimation.java | 21 +-- .../launcher3/states/OverviewState.java | 78 ++++++++ .../launcher3/states/SpringLoadedState.java | 108 +++++++++++ 10 files changed, 277 insertions(+), 207 deletions(-) create mode 100644 src/com/android/launcher3/states/OverviewState.java create mode 100644 src/com/android/launcher3/states/SpringLoadedState.java diff --git a/res/values/config.xml b/res/values/config.xml index c0bf360385..7a33ae6535 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -50,8 +50,6 @@ 90 - - 70 diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 8f7e8822b4..8aaad1367e 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -570,7 +570,7 @@ public class DeviceProfile { } } - int getOverviewModeButtonBarHeight() { + public int getOverviewModeButtonBarHeight() { int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); return Utilities.boundToRange(zoneHeight, overviewModeMinIconZoneHeightPx, diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 46eb263c63..b06081b812 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; @@ -47,7 +50,6 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.database.sqlite.SQLiteDatabase; import android.graphics.Point; @@ -264,10 +266,6 @@ public class Launcher extends BaseActivity private PopupDataProvider mPopupDataProvider; - // Determines how long to wait after a rotation before restoring the screen orientation to - // match the sensor state. - private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500; - private final ArrayList mSynchronouslyBoundPages = new ArrayList<>(); // We only want to get the SharedPreferences once since it does an FS stat each time we get @@ -291,18 +289,8 @@ public class Launcher extends BaseActivity public ViewGroupFocusHelper mFocusHandler; private boolean mRotationEnabled = false; - private boolean mAppLaunchSuccess; - @Thunk void setOrientation() { - if (mRotationEnabled) { - unlockScreenOrientation(true); - } else { - setRequestedOrientation( - ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); - } - } - private RotationPrefChangeHandler mRotationPrefChangeHandler; @Override @@ -416,7 +404,8 @@ public class Launcher extends BaseActivity // 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 - setOrientation(); + setRequestedOrientation(mRotationEnabled + ? SCREEN_ORIENTATION_UNSPECIFIED : SCREEN_ORIENTATION_NOSENSOR); setContentView(mLauncherView); @@ -1896,11 +1885,8 @@ public class Launcher extends BaseActivity AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); if (topView != null) { topView.onBackPressed(); - } else if (isInState(LauncherState.ALL_APPS)) { - ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS); - showWorkspace(true); - } else if (isInState(LauncherState.OVERVIEW)) { - ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW); + } else if (!isInState(LauncherState.NORMAL)) { + ued.logActionCommand(Action.Command.BACK, mWorkspace.getState().containerType); showWorkspace(true); } else { // Back button is a no-op here, but give at least some feedback for the button press @@ -2381,7 +2367,7 @@ public class Launcher extends BaseActivity if (!mDragController.isDragging()) { if (itemUnderLongClick == null) { // User long pressed on empty space - if (isInState(LauncherState.OVERVIEW)) { + if (mWorkspace.isPageRearrangeEnabled()) { mWorkspace.startReordering(v); getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS, Action.Direction.NONE, ContainerType.OVERVIEW); @@ -2544,15 +2530,6 @@ public class Launcher extends BaseActivity return; } - // Lock the orientation: - if (mRotationEnabled) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); - } - - // Prevent any Un/InstallShortcutReceivers from updating the db while we are - // in spring loaded mode - InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); - mStateTransitionAnimation.startAnimationToWorkspace( LauncherState.SPRING_LOADED, true /* animated */, null /* onCompleteRunnable */); @@ -2565,13 +2542,6 @@ public class Launcher extends BaseActivity public void exitSpringLoadedDragMode(int delay, final Runnable onCompleteRunnable) { if (!isInState(LauncherState.SPRING_LOADED)) return; - // Unlock rotation lock - unlockScreenOrientation(false); - - // Re-enable any Un/InstallShortcutReceiver and now process any queued items - InstallShortcutReceiver.disableAndFlushInstallQueue( - InstallShortcutReceiver.FLAG_DRAG_AND_DROP, this); - if (mExitSpringLoadedModeRunnable != null) { mHandler.removeCallbacks(mExitSpringLoadedModeRunnable); } @@ -3333,18 +3303,8 @@ public class Launcher extends BaseActivity mModel.refreshAndBindWidgetsAndShortcuts(packageUser); } - public void unlockScreenOrientation(boolean immediate) { - if (mRotationEnabled) { - if (immediate) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - } else { - mHandler.postDelayed(new Runnable() { - public void run() { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - } - }, RESTORE_SCREEN_ORIENTATION_DELAY); - } - } + public boolean isRotationEnabled () { + return mRotationEnabled; } private void markAppsViewShown() { diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 4619f4e58e..0ac27e5385 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -19,9 +19,9 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; -import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; +import com.android.launcher3.states.OverviewState; +import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import java.util.Arrays; @@ -40,19 +40,15 @@ public class LauncherState { private static final LauncherState[] sAllStates = new LauncherState[4]; - public static LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, + public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, 0, FLAG_DO_NOT_RESTORE); - public static LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS, + public static final LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY); - public static LauncherState SPRING_LOADED = new LauncherState(2, ContainerType.WORKSPACE, - SPRING_LOADED_TRANSITION_MS, - FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE); + public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); - public static LauncherState OVERVIEW = new LauncherState(3, ContainerType.OVERVIEW, - OVERVIEW_TRANSITION_MS, - FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT | FLAG_DO_NOT_RESTORE); + public static final LauncherState OVERVIEW = new OverviewState(3); public final int ordinal; @@ -101,4 +97,12 @@ public class LauncherState { public static LauncherState[] values() { return Arrays.copyOf(sAllStates, sAllStates.length); } + + public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { + return new float[] {1, 0}; + } + + public void onStateEnabled(Launcher launcher) { } + + public void onStateDisabled(Launcher launcher) { } } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 87f3ddaf47..5258fba927 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1341,12 +1341,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** * return true if freescroll has been enabled, false otherwise */ - public boolean enableFreeScroll() { + protected void enableFreeScroll() { setEnableFreeScroll(true); - return true; } - public void disableFreeScroll() { + protected void disableFreeScroll() { setEnableFreeScroll(false); } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 71677782df..e6bc770663 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -614,6 +614,12 @@ public final class Utilities { return c == null || c.isEmpty(); } + public static boolean isAccessibilityEnabled(Context context) { + AccessibilityManager accessibilityManager = (AccessibilityManager) + context.getSystemService(Context.ACCESSIBILITY_SERVICE); + return accessibilityManager.isEnabled(); + } + public static void sendCustomAccessibilityEvent(View target, int type, String text) { AccessibilityManager accessibilityManager = (AccessibilityManager) target.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 671ba07720..173ff6d4a8 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FR import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; +import static com.android.launcher3.Utilities.isAccessibilityEnabled; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -82,6 +83,7 @@ import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; +import com.android.launcher3.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -173,16 +175,11 @@ public class Workspace extends PagedView @Thunk final Launcher mLauncher; @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 static final Rect sTempRect = new Rect(); - private final int[] mTempXY = new int[2]; @Thunk float[] mDragViewVisualCenter = new float[2]; private final float[] mTempTouchCoordinates = new float[2]; private SpringLoadedDragController mSpringLoadedDragController; - private final float mOverviewModeShrinkFactor; // Direction used for moving the workspace and hotseat UI public enum Direction { @@ -271,6 +268,8 @@ public class Workspace extends PagedView boolean mOverlayShown = false; private boolean mForceDrawAdjacentPages = false; + private boolean mPageRearrangeEnabled = false; + // Total over scrollX in the overlay direction. private float mOverlayTranslation; @@ -307,8 +306,6 @@ public class Workspace extends PagedView mWallpaperManager = WallpaperManager.getInstance(context); mWallpaperOffset = new WallpaperOffsetInterpolator(this); - mOverviewModeShrinkFactor = - res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f; setOnHierarchyChangeListener(this); setHapticFeedbackEnabled(false); @@ -448,7 +445,8 @@ public class Workspace extends PagedView setClipChildren(false); setClipToPadding(false); - setMinScale(mOverviewModeShrinkFactor); + // TODO: Remove this + setMinScale(OverviewState.SCALE_FACTOR); setupLayoutTransition(); mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); @@ -1126,7 +1124,7 @@ public class Workspace extends PagedView enableHwLayersOnVisiblePages(); } - private void showPageIndicatorAtCurrentScroll() { + public void showPageIndicatorAtCurrentScroll() { if (mPageIndicator != null) { mPageIndicator.setScroll(getScrollX(), computeMaxScrollX()); } @@ -1282,7 +1280,7 @@ public class Workspace extends PagedView } }); - final boolean accessibilityEnabled = isAccessibilityEnabled(); + final boolean accessibilityEnabled = isAccessibilityEnabled(mLauncher); animator.addUpdateListener( new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled)); animator.addUpdateListener( @@ -1291,12 +1289,6 @@ public class Workspace extends PagedView } } - protected boolean isAccessibilityEnabled() { - AccessibilityManager am = (AccessibilityManager) - mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); - return am.isEnabled(); - } - @Override protected void notifyPageSwitchListener(int prevPage) { super.notifyPageSwitchListener(prevPage); @@ -1428,8 +1420,8 @@ public class Workspace extends PagedView } private void updateChildrenLayersEnabled() { - boolean small = mState == LauncherState.OVERVIEW || mIsSwitchingState; - boolean enableChildrenLayers = small || isPageInTransition(); + boolean enableChildrenLayers = + isPageRearrangeEnabled() || mIsSwitchingState || isPageInTransition(); if (enableChildrenLayers != mChildrenLayersEnabled) { mChildrenLayersEnabled = enableChildrenLayers; @@ -1551,59 +1543,33 @@ public class Workspace extends PagedView snapToPage(whichPage, OVERVIEW_TRANSITION_MS, new ZoomInInterpolator()); } - int getOverviewModeTranslationY() { - DeviceProfile grid = mLauncher.getDeviceProfile(); - int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight(); + private void onStartStateTransition(LauncherState state) { + // Change the internal state only when the transition actually starts + mState.onStateDisabled(mLauncher); + mState = state; + mState.onStateEnabled(mLauncher); - int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight()); - Rect workspacePadding = grid.getWorkspacePadding(sTempRect); - int workspaceTop = mInsets.top + workspacePadding.top; - int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom; - int overviewTop = mInsets.top; - int overviewBottom = getViewportHeight() - mInsets.bottom - overviewButtonBarHeight; - int workspaceOffsetTopEdge = workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2; - int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2; - return -workspaceOffsetTopEdge + overviewOffsetTopEdge; + mIsSwitchingState = true; + mTransitionProgress = 0; + + updateChildrenLayersEnabled(); } - float getSpringLoadedTranslationY() { - DeviceProfile grid = mLauncher.getDeviceProfile(); - if (grid.isVerticalBarLayout() || getChildCount() == 0) { - return 0; - } + private void onEndStateTransition() { + mIsSwitchingState = false; + mForceDrawAdjacentPages = false; + mTransitionProgress = 1; - float scaledHeight = grid.workspaceSpringLoadShrinkFactor * getNormalChildHeight(); - float shrunkTop = mInsets.top + grid.dropTargetBarSizePx; - float shrunkBottom = getViewportHeight() - mInsets.bottom - - grid.getWorkspacePadding(sTempRect).bottom - - grid.workspaceSpringLoadedBottomSpace; - float totalShrunkSpace = shrunkBottom - shrunkTop; - - float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2; - - float halfHeight = getHeight() / 2; - float myCenter = getTop() + halfHeight; - float cellTopFromCenter = halfHeight - getChildAt(0).getTop(); - float actualCellTop = myCenter - cellTopFromCenter * grid.workspaceSpringLoadShrinkFactor; - return (desiredCellTop - actualCellTop) / grid.workspaceSpringLoadShrinkFactor; + updateChildrenLayersEnabled(); + updateAccessibilityFlags(); } - float getOverviewModeShrinkFactor() { - return mOverviewModeShrinkFactor; - } - - /** * Sets the current workspace {@link LauncherState} and updates the UI without any animations */ public void setState(LauncherState toState) { - // Update the current state - mState = toState; + onStartStateTransition(toState); mStateTransitionAnimation.setState(mState); - - updateAccessibilityFlags(); - onPrepareStateTransition(mState.hasMultipleVisiblePages); - onStartStateTransition(); onEndStateTransition(); } @@ -1612,20 +1578,18 @@ public class Workspace extends PagedView */ public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, AnimatorSet anim, AnimationConfig config) { - final LauncherState fromState = mState; + StateTransitionListener listener = new StateTransitionListener(toState); + mStateTransitionAnimation.setStateWithAnimation(mState, toState, anim, layerViews, config); - // Update the current state - mState = toState; - mStateTransitionAnimation.setStateWithAnimation( - fromState, toState, anim, layerViews, config); + // Invalidate the pages now, so that we have the visible pages before the + // animation is started + if (toState.hasMultipleVisiblePages) { + mForceDrawAdjacentPages = true; + } + invalidate(); // This will call dispatchDraw(), which calls getVisiblePages(). - updateAccessibilityFlags(); - onPrepareStateTransition(mState.hasMultipleVisiblePages); - - StateTransitionListener listener = new StateTransitionListener(); ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1); stepAnimator.addUpdateListener(listener); - anim.play(stepAnimator); anim.addListener(listener); } @@ -1646,7 +1610,7 @@ public class Workspace extends PagedView } private void updateAccessibilityFlags(CellLayout page, int pageNo) { - if (mState == LauncherState.OVERVIEW) { + if (isPageRearrangeEnabled()) { page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); page.getShortcutsAndWidgets().setImportantForAccessibility( IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); @@ -1668,43 +1632,19 @@ public class Workspace extends PagedView } } - public void onPrepareStateTransition(boolean multiplePagesVisible) { - mIsSwitchingState = true; - mTransitionProgress = 0; - - if (multiplePagesVisible) { - mForceDrawAdjacentPages = true; + public void setPageRearrangeEnabled(boolean isEnabled) { + if (mPageRearrangeEnabled != isEnabled) { + mPageRearrangeEnabled = isEnabled; + if (isEnabled) { + enableFreeScroll(); + } else { + disableFreeScroll(); + } } - invalidate(); // This will call dispatchDraw(), which calls getVisiblePages(). - - updateChildrenLayersEnabled(); } - private void onStartStateTransition() { - if (mState == LauncherState.SPRING_LOADED) { - // Show the page indicator at the same time as the rest of the transition. - showPageIndicatorAtCurrentScroll(); - } - getPageIndicator().setShouldAutoHide(mState != LauncherState.SPRING_LOADED); - } - - public void onEndStateTransition() { - mIsSwitchingState = false; - updateChildrenLayersEnabled(); - mForceDrawAdjacentPages = false; - mTransitionProgress = 1; - - if (mState == LauncherState.OVERVIEW) { - enableFreeScroll(); - } else { - disableFreeScroll(); - } - - ViewGroup overviewPanel = mLauncher.getOverviewPanel(); - if (isAccessibilityEnabled() && overviewPanel.getVisibility() == View.VISIBLE) { - overviewPanel.getChildAt(0).performAccessibilityAction( - AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } + public boolean isPageRearrangeEnabled() { + return mPageRearrangeEnabled; } public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) { @@ -1803,8 +1743,8 @@ public class Workspace extends PagedView } private boolean transitionStateShouldAllowDrop() { - return ((!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) && - (mState == LauncherState.NORMAL || mState == LauncherState.SPRING_LOADED)); + return (!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) && + workspaceIconsCanBeDragged(); } /** @@ -3022,10 +2962,6 @@ public class Workspace extends PagedView } } - public WorkspaceStateTransitionAnimation getStateTransitionAnimation() { - return mStateTransitionAnimation; - } - /** * Return the current CellInfo describing our current drag; this method exists * so that Launcher can sync this object with the correct info when the activity is created/ @@ -3563,16 +3499,6 @@ public class Workspace extends PagedView } } - @Override - public boolean enableFreeScroll() { - if (getState() == LauncherState.OVERVIEW) { - return super.enableFreeScroll(); - } else { - Log.w(TAG, "enableFreeScroll called but not in overview: state=" + getState()); - return false; - } - } - /** * Used as a workaround to ensure that the AppWidgetService receives the * PACKAGE_ADDED broadcast before updating widgets. @@ -3629,6 +3555,13 @@ public class Workspace extends PagedView private class StateTransitionListener extends AnimatorListenerAdapter implements AnimatorUpdateListener { + + private final LauncherState mToState; + + StateTransitionListener(LauncherState toState) { + mToState = toState; + } + @Override public void onAnimationUpdate(ValueAnimator anim) { mTransitionProgress = anim.getAnimatedFraction(); @@ -3636,8 +3569,7 @@ public class Workspace extends PagedView @Override public void onAnimationStart(Animator animation) { - mTransitionProgress = 0; - onStartStateTransition(); + onStartStateTransition(mToState); } @Override diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index af56fd77c6..d626b65818 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -18,8 +18,6 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.LauncherState.SPRING_LOADED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -145,8 +143,6 @@ public class WorkspaceStateTransitionAnimation { private final Launcher mLauncher; private final Workspace mWorkspace; - private final float mSpringLoadedShrinkFactor; - private final float mOverviewModeShrinkFactor; private final boolean mWorkspaceFadeInAdjacentScreens; private float mNewScale; @@ -157,9 +153,6 @@ public class WorkspaceStateTransitionAnimation { DeviceProfile grid = mLauncher.getDeviceProfile(); Resources res = launcher.getResources(); - mSpringLoadedShrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor; - mOverviewModeShrinkFactor = - res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f; mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha); mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); } @@ -188,17 +181,9 @@ public class WorkspaceStateTransitionAnimation { // Update the workspace state int finalBackgroundAlpha = state.hasScrim ? 255 : 0; - final float finalWorkspaceTranslationY; - if (state == OVERVIEW) { - mNewScale = mOverviewModeShrinkFactor; - finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY(); - } else if (state == SPRING_LOADED) { - mNewScale = mSpringLoadedShrinkFactor; - finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY(); - } else { - mNewScale = 1f; - finalWorkspaceTranslationY = 0; - } + float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher); + final float mNewScale = scaleAndTranslationY[0]; + final float finalWorkspaceTranslationY = scaleAndTranslationY[1]; int toPage = mWorkspace.getPageNearestToCenterOfScreen(); final int childCount = mWorkspace.getChildCount(); diff --git a/src/com/android/launcher3/states/OverviewState.java b/src/com/android/launcher3/states/OverviewState.java new file mode 100644 index 0000000000..911cec62f5 --- /dev/null +++ b/src/com/android/launcher3/states/OverviewState.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.states; + +import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.Utilities.isAccessibilityEnabled; + +import android.graphics.Rect; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Workspace; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +/** + * Definition for overview state + */ +public class OverviewState extends LauncherState { + + // The percent to shrink the workspace during overview mode + public static final float SCALE_FACTOR = 0.7f; + + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT | + FLAG_DO_NOT_RESTORE; + + public OverviewState(int id) { + super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS); + } + + @Override + public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { + DeviceProfile grid = launcher.getDeviceProfile(); + Workspace ws = launcher.getWorkspace(); + Rect insets = launcher.getDragLayer().getInsets(); + + int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight(); + int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight()); + Rect workspacePadding = grid.getWorkspacePadding(null); + int workspaceTop = insets.top + workspacePadding.top; + int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom; + int overviewTop = insets.top; + int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight; + int workspaceOffsetTopEdge = + workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2; + int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2; + return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge }; + } + + @Override + public void onStateEnabled(Launcher launcher) { + launcher.getWorkspace().setPageRearrangeEnabled(true); + + if (isAccessibilityEnabled(launcher)) { + launcher.getOverviewPanel().getChildAt(0).performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } + } + + @Override + public void onStateDisabled(Launcher launcher) { + launcher.getWorkspace().setPageRearrangeEnabled(false); + } +} diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java new file mode 100644 index 0000000000..f60ef0ca1b --- /dev/null +++ b/src/com/android/launcher3/states/SpringLoadedState.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.states; + +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; + +import android.content.pm.ActivityInfo; +import android.graphics.Rect; +import android.os.Handler; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InstallShortcutReceiver; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Workspace; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +/** + * Definition for spring loaded state used during drag and drop. + */ +public class SpringLoadedState extends LauncherState { + + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | + FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE; + + // Determines how long to wait after a rotation before restoring the screen orientation to + // match the sensor state. + private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500; + + public SpringLoadedState(int id) { + super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS); + } + + @Override + public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { + DeviceProfile grid = launcher.getDeviceProfile(); + Workspace ws = launcher.getWorkspace(); + if (grid.isVerticalBarLayout() || ws.getChildCount() == 0) { + return super.getWorkspaceScaleAndTranslation(launcher); + } + + float scale = grid.workspaceSpringLoadShrinkFactor; + Rect insets = launcher.getDragLayer().getInsets(); + + float scaledHeight = scale * ws.getNormalChildHeight(); + float shrunkTop = insets.top + grid.dropTargetBarSizePx; + float shrunkBottom = ws.getViewportHeight() - insets.bottom + - grid.getWorkspacePadding(null).bottom + - grid.workspaceSpringLoadedBottomSpace; + float totalShrunkSpace = shrunkBottom - shrunkTop; + + float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2; + + float halfHeight = ws.getHeight() / 2; + float myCenter = ws.getTop() + halfHeight; + float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop(); + float actualCellTop = myCenter - cellTopFromCenter * scale; + return new float[] { scale, (desiredCellTop - actualCellTop) / scale}; + } + + @Override + public void onStateEnabled(Launcher launcher) { + Workspace ws = launcher.getWorkspace(); + ws.showPageIndicatorAtCurrentScroll(); + ws.getPageIndicator().setShouldAutoHide(false); + + // Lock the orientation: + if (launcher.isRotationEnabled()) { + launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } + + // Prevent any Un/InstallShortcutReceivers from updating the db while we are + // in spring loaded mode + InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP); + } + + @Override + public void onStateDisabled(final Launcher launcher) { + launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true); + + // Unlock rotation lock + if (launcher.isRotationEnabled()) { + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + } + }, RESTORE_SCREEN_ORIENTATION_DELAY); + } + + // Re-enable any Un/InstallShortcutReceiver and now process any queued items + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher); + } +} From 2104c3025a3c5987164c7cf625168705ee018f55 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 23 Oct 2017 15:04:10 -0700 Subject: [PATCH 060/885] Fixing mNewScale was never getting initialized Change-Id: I03507cedc65a805d122ab2161a0162c21ac5975f --- .../android/launcher3/WorkspaceStateTransitionAnimation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index d626b65818..d66255bda2 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -182,7 +182,7 @@ public class WorkspaceStateTransitionAnimation { int finalBackgroundAlpha = state.hasScrim ? 255 : 0; float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher); - final float mNewScale = scaleAndTranslationY[0]; + mNewScale = scaleAndTranslationY[0]; final float finalWorkspaceTranslationY = scaleAndTranslationY[1]; int toPage = mWorkspace.getPageNearestToCenterOfScreen(); From be93f264d74eed6d43d6c75694624d53d6877357 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 20 Oct 2017 17:05:27 -0700 Subject: [PATCH 061/885] Moving all-apps state logic to an independent class Unifying all the paths for state change to a single flow Bug: 67678570 Change-Id: I0773c0f59ae1ef324c507bc1aae188d8c059dea4 --- src/com/android/launcher3/Launcher.java | 34 +-- src/com/android/launcher3/LauncherState.java | 35 ++- .../LauncherStateTransitionAnimation.java | 238 ++++------------ .../launcher3/PinchToOverviewListener.java | 2 +- .../allapps/AllAppsTransitionController.java | 263 ++++++++---------- .../anim/AnimationSuccessListener.java | 42 +++ .../launcher3/states/AllAppsState.java | 50 ++++ .../launcher3/states/OverviewState.java | 8 +- .../launcher3/states/SpringLoadedState.java | 8 +- 9 files changed, 314 insertions(+), 366 deletions(-) create mode 100644 src/com/android/launcher3/anim/AnimationSuccessListener.java create mode 100644 src/com/android/launcher3/states/AllAppsState.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b06081b812..775dad2c80 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -117,6 +117,7 @@ import com.android.launcher3.popup.BaseActionPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.states.AllAppsState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -202,9 +203,7 @@ public class Launcher extends BaseActivity // Type: SparseArray private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; - static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; - - @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; + private LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -431,6 +430,10 @@ public class Launcher extends BaseActivity recreate(); } + public LauncherStateTransitionAnimation getStateTransition() { + return mStateTransitionAnimation; + } + protected void overrideTheme(boolean isDark, boolean supportsDarkText) { if (isDark) { setTheme(R.style.LauncherThemeDark); @@ -2441,8 +2444,7 @@ public class Launcher extends BaseActivity boolean changed = !isInState(LauncherState.NORMAL); if (changed || mAllAppsController.isTransitioning()) { mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace( - LauncherState.NORMAL, animated, onCompleteRunnable); + mStateTransitionAnimation.goToState(LauncherState.NORMAL, animated, onCompleteRunnable); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { @@ -2483,8 +2485,7 @@ public class Launcher extends BaseActivity }; } mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace( - LauncherState.OVERVIEW, animated, postAnimRunnable); + mStateTransitionAnimation.goToState(LauncherState.OVERVIEW, animated, postAnimRunnable); // If animated from long press, then don't allow any of the controller in the drag // layer to intercept any remaining touch. @@ -2499,8 +2500,6 @@ public class Launcher extends BaseActivity // TODO: calling method should use the return value so that when {@code false} is returned // the workspace transition doesn't fall into invalid state. public boolean showAppsView(boolean animated) { - markAppsViewShown(); - if (!(isInState(LauncherState.NORMAL) || (isInState(LauncherState.ALL_APPS) && mAllAppsController.isTransitioning()))) { return false; @@ -2512,7 +2511,7 @@ public class Launcher extends BaseActivity mExitSpringLoadedModeRunnable = null; } - mStateTransitionAnimation.startAnimationToAllApps(animated); + mStateTransitionAnimation.goToState(LauncherState.ALL_APPS, animated, null); // Change the state *after* we've called all the transition code AbstractFloatingView.closeAllOpenViews(this); @@ -2529,10 +2528,7 @@ public class Launcher extends BaseActivity if (isInState(LauncherState.SPRING_LOADED)) { return; } - - mStateTransitionAnimation.startAnimationToWorkspace( - LauncherState.SPRING_LOADED, true /* animated */, - null /* onCompleteRunnable */); + mStateTransitionAnimation.goToState(LauncherState.SPRING_LOADED, true, null); } public void exitSpringLoadedDragMode(int delay) { @@ -3307,15 +3303,9 @@ public class Launcher extends BaseActivity return mRotationEnabled; } - private void markAppsViewShown() { - if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) { - return; - } - mSharedPrefs.edit().putBoolean(APPS_VIEW_SHOWN, true).apply(); - } - private boolean shouldShowDiscoveryBounce() { - return isInState(LauncherState.NORMAL) && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) + return isInState(LauncherState.NORMAL) + && !mSharedPrefs.getBoolean(AllAppsState.APPS_VIEW_SHOWN, false) && !UserManagerCompat.getInstance(this).isDemoUser(); } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 0ac27e5385..9d01ed82d4 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -20,6 +20,9 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; +import android.view.View; + +import com.android.launcher3.states.AllAppsState; import com.android.launcher3.states.OverviewState; import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -28,7 +31,7 @@ import java.util.Arrays; /** - * Various states for launcher + * Base state for various states used for the Launcher */ public class LauncherState { @@ -37,14 +40,14 @@ public class LauncherState { protected static final int FLAG_HIDE_HOTSEAT = 1 << 2; protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; protected static final int FLAG_DO_NOT_RESTORE = 1 << 4; + protected static final int FLAG_HAS_SPRING = 1 << 5; private static final LauncherState[] sAllStates = new LauncherState[4]; public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, - 0, FLAG_DO_NOT_RESTORE); + 0, 1f, FLAG_DO_NOT_RESTORE); - public static final LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS, - ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY); + public static final LauncherState ALL_APPS = new AllAppsState(1); public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); @@ -73,12 +76,25 @@ public class LauncherState { */ public final int workspaceAccessibilityFlag; - // Properties related to state transition animation. + /** + * Properties related to state transition animation + * + * @see WorkspaceStateTransitionAnimation + */ public final boolean hasScrim; public final boolean hideHotseat; public final int transitionDuration; - public LauncherState(int id, int containerType, int transitionDuration, int flags) { + /** + * Fraction shift in the vertical translation UI and related properties + * + * @see com.android.launcher3.allapps.AllAppsTransitionController + */ + public final float verticalProgress; + public final boolean hasVerticalSpring; + + public LauncherState(int id, int containerType, int transitionDuration, float verticalProgress, + int flags) { this.containerType = containerType; this.transitionDuration = transitionDuration; @@ -90,6 +106,9 @@ public class LauncherState { : IMPORTANT_FOR_ACCESSIBILITY_AUTO; this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0; + this.verticalProgress = verticalProgress; + this.hasVerticalSpring = (flags & FLAG_HAS_SPRING) != 0; + this.ordinal = id; sAllStates[id] = this; } @@ -105,4 +124,8 @@ public class LauncherState { public void onStateEnabled(Launcher launcher) { } public void onStateDisabled(Launcher launcher) { } + + public View getFinalFocus(Launcher launcher) { + return launcher.getWorkspace(); + } } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 0e6cead0c4..eec8b31c41 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -19,14 +19,13 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.util.Log; +import android.os.Handler; +import android.os.Looper; import android.view.View; -import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationLayerSet; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.util.Thunk; +import com.android.launcher3.anim.AnimationSuccessListener; /** * TODO: figure out what kind of tests we can write for this @@ -74,223 +73,75 @@ public class LauncherStateTransitionAnimation { public static final String TAG = "LSTAnimation"; private final AnimationConfig mConfig = new AnimationConfig(); - @Thunk Launcher mLauncher; - @Thunk AnimatorSet mCurrentAnimation; - AllAppsTransitionController mAllAppsController; + private final Handler mUiHandler; + private final Launcher mLauncher; + private final AllAppsTransitionController mAllAppsController; - public LauncherStateTransitionAnimation(Launcher l, AllAppsTransitionController allAppsController) { + public LauncherStateTransitionAnimation( + Launcher l, AllAppsTransitionController allAppsController) { + mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; mAllAppsController = allAppsController; } - /** - * Starts an animation to the apps view. - */ - public void startAnimationToAllApps(boolean animated) { - final AllAppsContainerView toView = mLauncher.getAppsView(); - - // If for some reason our views aren't initialized, don't animate - animated = animated && (toView != null); - - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - - final AnimationLayerSet layerViews = new AnimationLayerSet(); - + public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) { // Cancel the current animation - cancelAnimation(); - - if (!animated) { - mLauncher.getWorkspace().setState(LauncherState.ALL_APPS); - - mAllAppsController.finishPullUp(); - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setScaleX(1.0f); - toView.setScaleY(1.0f); - toView.setAlpha(1.0f); - toView.setVisibility(View.VISIBLE); - - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - return; - } - - if (!FeatureFlags.LAUNCHER3_PHYSICS) { - // We are animating the content view alpha, so ensure we have a layer for it. - layerViews.addView(toView); - } - - animation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - cleanupAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - } - }); - mConfig.reset(); - mAllAppsController.animateToAllApps(animation, mConfig); - mLauncher.getWorkspace().setStateWithAnimation(LauncherState.ALL_APPS, - layerViews, animation, mConfig); - - Runnable startAnimRunnable = new StartAnimRunnable(animation, toView); - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); - if (mConfig.shouldPost) { - toView.post(startAnimRunnable); - } else { - startAnimRunnable.run(); - } - } - - /** - * Starts an animation to the workspace from the current overlay view. - */ - public void startAnimationToWorkspace(final LauncherState toWorkspaceState, - final boolean animated, final Runnable onCompleteRunnable) { - if (toWorkspaceState != LauncherState.NORMAL && - toWorkspaceState != LauncherState.SPRING_LOADED && - toWorkspaceState != LauncherState.OVERVIEW) { - Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); - } - - if (mLauncher.isInState(LauncherState.ALL_APPS) || mAllAppsController.isTransitioning()) { - startAnimationToWorkspaceFromAllApps(mLauncher.getWorkspace().getState(), - toWorkspaceState, animated, onCompleteRunnable); - } else { - startAnimationToNewWorkspaceState(toWorkspaceState, animated, onCompleteRunnable); - } - } - - /** - * Starts an animation to the workspace from the apps view. - */ - private void startAnimationToWorkspaceFromAllApps(final LauncherState fromWorkspaceState, - final LauncherState toWorkspaceState, boolean animated, - final Runnable onCompleteRunnable) { - final AllAppsContainerView fromView = mLauncher.getAppsView(); - // If for some reason our views aren't initialized, don't animate - animated = animated & (fromView != null); - - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); - final AnimationLayerSet layerViews = new AnimationLayerSet(); - - // Cancel the current animation - cancelAnimation(); if (!animated) { - if (fromWorkspaceState == LauncherState.ALL_APPS) { - mAllAppsController.finishPullDown(); - } - fromView.setVisibility(View.GONE); - mLauncher.getWorkspace().setState(toWorkspaceState); + mAllAppsController.setFinalProgress(state.verticalProgress); + mLauncher.getWorkspace().setState(state); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - // Run any queued runnables + // Run any queued runnable if (onCompleteRunnable != null) { onCompleteRunnable.run(); } return; } - animation.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - @Override - public void onAnimationCancel(Animator animation) { - canceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) return; - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - cleanupAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - } - - }); - - mConfig.reset(); - mAllAppsController.animateToWorkspace(animation, mConfig); - mLauncher.getWorkspace().setStateWithAnimation(toWorkspaceState, layerViews, animation, - mConfig); - - Runnable startAnimRunnable = new StartAnimRunnable(animation, mLauncher.getWorkspace()); - mCurrentAnimation = animation; - mCurrentAnimation.addListener(layerViews); + AnimatorSet animation = createAnimationToNewWorkspace(state, onCompleteRunnable); + Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher)); if (mConfig.shouldPost) { - fromView.post(startAnimRunnable); + mUiHandler.post(runnable); } else { - startAnimRunnable.run(); + runnable.run(); } } - /** - * Starts an animation to the workspace from another workspace state, e.g. normal to overview. - */ - private void startAnimationToNewWorkspaceState( - final LauncherState toWorkspaceState, final boolean animated, - final Runnable onCompleteRunnable) { - // Cancel the current animation - cancelAnimation(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); - - if (!animated) { - mLauncher.getWorkspace().setState(toWorkspaceState); - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - return; - } - - final AnimatorSet animation = - createAnimationToNewWorkspace(toWorkspaceState, onCompleteRunnable); - mLauncher.getWorkspace().post(new StartAnimRunnable(animation, null)); - mCurrentAnimation = animation; - } - protected AnimatorSet createAnimationToNewWorkspace(LauncherState state, final Runnable onCompleteRunnable) { - cancelAnimation(); - - final AnimationLayerSet layerViews = new AnimationLayerSet(); - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); mConfig.reset(); - mLauncher.getWorkspace().setStateWithAnimation(state, layerViews, animation, mConfig); - animation.addListener(new AnimatorListenerAdapter() { + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); + final AnimationLayerSet layerViews = new AnimationLayerSet(); + + mAllAppsController.animateToFinalProgress(state.verticalProgress, + state.hasVerticalSpring, animation, mConfig); + mLauncher.getWorkspace().setStateWithAnimation(state, + layerViews, animation, mConfig); + + animation.addListener(layerViews); + animation.addListener(new AnimationSuccessListener() { @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationSuccess(Animator animator) { // Run any queued runnables if (onCompleteRunnable != null) { onCompleteRunnable.run(); } - // This can hold unnecessary references to views. - cleanupAnimation(); + mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } }); - animation.addListener(layerViews); - return animation; + mConfig.setAnimation(animation); + return mConfig.mCurrentAnimation; } /** * Cancels the current animation. */ - private void cancelAnimation() { - if (mCurrentAnimation != null) { - mCurrentAnimation.setDuration(0); - mCurrentAnimation.cancel(); - mCurrentAnimation = null; - } - } - - @Thunk void cleanupAnimation() { - mCurrentAnimation = null; + public void cancelAnimation() { + mConfig.reset(); } private class StartAnimRunnable implements Runnable { @@ -305,7 +156,7 @@ public class LauncherStateTransitionAnimation { @Override public void run() { - if (mCurrentAnimation != mAnim) { + if (mConfig.mCurrentAnimation != mAnim) { return; } if (mViewToFocus != null) { @@ -315,14 +166,21 @@ public class LauncherStateTransitionAnimation { } } - public static class AnimationConfig { + public static class AnimationConfig extends AnimatorListenerAdapter { public boolean shouldPost; private long mOverriddenDuration = -1; + private AnimatorSet mCurrentAnimation; public void reset() { shouldPost = false; mOverriddenDuration = -1; + + if (mCurrentAnimation != null) { + mCurrentAnimation.setDuration(0); + mCurrentAnimation.cancel(); + mCurrentAnimation = null; + } } public void overrideDuration(long duration) { @@ -332,5 +190,17 @@ public class LauncherStateTransitionAnimation { public long getDuration(long defaultDuration) { return mOverriddenDuration >= 0 ? mOverriddenDuration : defaultDuration; } + + @Override + public void onAnimationEnd(Animator animation) { + if (mCurrentAnimation == animation) { + mCurrentAnimation = null; + } + } + + public void setAnimation(AnimatorSet animation) { + mCurrentAnimation = animation; + mCurrentAnimation.addListener(this); + } } } diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index 47113c9386..407f0b59d2 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -107,7 +107,7 @@ public class PinchToOverviewListener mToState = mLauncher.isInState(LauncherState.OVERVIEW) ? LauncherState.NORMAL : LauncherState.OVERVIEW; - mCurrentAnimation = mLauncher.mStateTransitionAnimation + mCurrentAnimation = mLauncher.getStateTransition() .createAnimationToNewWorkspace(mToState, this); mPinchStarted = true; mCurrentScale = 1; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 9247c54f7c..35dfa8122b 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -4,10 +4,11 @@ import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.support.animation.SpringAnimation; import android.support.v4.view.animation.FastOutSlowInInterpolator; +import android.util.Property; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; @@ -22,12 +23,13 @@ import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; @@ -46,6 +48,25 @@ import com.android.launcher3.util.TouchController; public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener, SearchUiManager.OnScrollRangeChangeListener { + private static final Property PROGRESS = + new Property(Float.class, "progress") { + + @Override + public Float get(AllAppsTransitionController controller) { + return controller.mProgress; + } + + @Override + public void set(AllAppsTransitionController controller, Float progress) { + controller.setProgress(progress); + } + }; + + // Spring values used when the user has not flung all apps. + private static final float SPRING_MAX_RELEASE_VELOCITY = 10000; + // The delay (as a % of the animation duration) to start the springs. + private static final float SPRING_DELAY = 0.3f; + private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f); private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f); private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator(); @@ -61,11 +82,8 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private AllAppsCaretController mCaretController; - private float mStatusBarHeight; - private final Launcher mLauncher; private final SwipeDetector mDetector; - private final ArgbEvaluator mEvaluator; private final boolean mIsDarkTheme; // Animation in this class is controlled by a single variable {@link mProgress}. @@ -87,7 +105,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private long mAnimationDuration; - private AnimatorSet mCurrentAnimation; private boolean mNoIntercept; private boolean mTouchEventStartedOnHotseat; @@ -105,7 +122,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mShiftRange = DEFAULT_SHIFT_RANGE; mProgress = 1f; - mEvaluator = new ArgbEvaluator(); mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); } @@ -177,10 +193,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect @Override public void onDragStart(boolean start) { mCaretController.onDragStart(); - cancelAnimation(); - mCurrentAnimation = LauncherAnimUtils.createAnimatorSet(); + mLauncher.getStateTransition().cancelAnimation(); + cancelDiscoveryAnimation(); mShiftStart = mAppsView.getTranslationY(); - preparePull(start); + onProgressAnimationStart(); if (hasSpringAnimationHandler()) { mSpringAnimationHandler.skipToEnd(); } @@ -263,16 +279,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect return mDetector.isDraggingOrSettling(); } - /** - * @param start {@code true} if start of new drag. - */ - public void preparePull(boolean start) { - if (start) { - // Initialize values that should not change until #onDragEnd - mStatusBarHeight = mLauncher.getDragLayer().getInsets().top; - mHotseat.setVisibility(View.VISIBLE); - mAppsView.setVisibility(View.VISIBLE); - } + private void onProgressAnimationStart() { + // Initialize values that should not change until #onDragEnd + mHotseat.setVisibility(View.VISIBLE); + mAppsView.setVisibility(View.VISIBLE); } private void updateLightStatusBar(float shift) { @@ -296,7 +306,13 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } /** - * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace + * Note this method should not be called outside this class. This is public because it is used + * in xml-based animations which also handle updating the appropriate UI. + * + * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace + * + * @see #setFinalProgress(float) + * @see #animateToFinalProgress(float, boolean, AnimatorSet, AnimationConfig) */ public void setProgress(float progress) { float shiftPrevious = mProgress * mShiftRange; @@ -344,74 +360,77 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange); } - public void animateToAllApps(AnimatorSet animationOut, AnimationConfig outConfig) { - outConfig.shouldPost = true; - if (animationOut == null) { + /** + * Sets the vertical transition progress to {@param progress} and updates all the dependent UI + * accordingly. + */ + public void setFinalProgress(float progress) { + setProgress(progress); + onProgressAnimationEnd(); + } + + /** + * Creates an animation which updates the vertical transition progress and updates all the + * dependent UI using various animation events + * + * @param progress the final vertical progress at the end of the animation + * @param addSpring should there be an addition spring animation for the sub-views + * @param animationOut the target AnimatorSet where this animation should be added + * @param outConfig an in/out configuration which can be shared with other animations + */ + public void animateToFinalProgress(float progress, boolean addSpring, + AnimatorSet animationOut, AnimationConfig outConfig) { + if (Float.compare(mProgress, progress) == 0) { + // Fail fast + onProgressAnimationEnd(); return; } + + outConfig.shouldPost = true; Interpolator interpolator; if (mDetector.isIdleState()) { - preparePull(true); mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; mShiftStart = mAppsView.getTranslationY(); interpolator = mFastOutSlowInInterpolator; } else { mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity)); interpolator = mScrollInterpolator; - float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange; - if (nextFrameProgress >= 0f) { - mProgress = nextFrameProgress; - } + mProgress = Utilities.boundToRange( + mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange, 0f, 1f); outConfig.shouldPost = false; } outConfig.overrideDuration(mAnimationDuration); - ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", - mProgress, 0f); - driftAndAlpha.setDuration(mAnimationDuration); - driftAndAlpha.setInterpolator(interpolator); - animationOut.play(driftAndAlpha); - - animationOut.addListener(new AnimatorListenerAdapter() { - // Spring values used when the user has not flung all apps. - private final float MAX_RELEASE_VELOCITY = 10000; - // The delay (as a % of the animation duration) to start the springs. - private final float DELAY = 0.3f; - - boolean canceled = false; - + ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, progress); + anim.setDuration(mAnimationDuration); + anim.setInterpolator(interpolator); + anim.addListener(new AnimationSuccessListener() { @Override - public void onAnimationCancel(Animator animation) { - canceled = true; + public void onAnimationSuccess(Animator animator) { + onProgressAnimationEnd(); } @Override public void onAnimationStart(Animator animation) { - // Add springs for cases where the user has not flung. - // ie. clicking on the caret, releasing all apps so it snaps up. - mAppsView.postDelayed(new Runnable() { - @Override - public void run() { - if (!canceled && !mSpringAnimationHandler.isRunning()) { - float velocity = mProgress * MAX_RELEASE_VELOCITY; - mSpringAnimationHandler.animateToPositionWithVelocity(0, 1, velocity); - } - } - }, (long) (mAnimationDuration * DELAY)); - } - - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) { - return; - } else { - finishPullUp(); - cleanUpAnimation(); - mDetector.finishedScrolling(); - } + onProgressAnimationStart(); } }); - mCurrentAnimation = animationOut; + + animationOut.play(anim); + if (addSpring) { + ValueAnimator springAnim = ValueAnimator.ofFloat(0, 1); + springAnim.setDuration((long) (mAnimationDuration * SPRING_DELAY)); + springAnim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (!mSpringAnimationHandler.isRunning()) { + float velocity = mProgress * SPRING_MAX_RELEASE_VELOCITY; + mSpringAnimationHandler.animateToPositionWithVelocity(0, 1, velocity); + } + } + }); + animationOut.play(anim); + } } public void showDiscoveryBounce() { @@ -425,12 +444,12 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect @Override public void onAnimationStart(Animator animator) { mIsTranslateWithoutWorkspace = true; - preparePull(true); + onProgressAnimationStart(); } @Override public void onAnimationEnd(Animator animator) { - finishPullDown(); + onProgressAnimationEnd(); mDiscoBounceAnimation = null; mIsTranslateWithoutWorkspace = false; } @@ -447,83 +466,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect }); } - public void animateToWorkspace(AnimatorSet animationOut, AnimationConfig outconfig) { - outconfig.shouldPost = true; - if (animationOut == null) { - return; - } - Interpolator interpolator; - if (mDetector.isIdleState()) { - preparePull(true); - mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; - mShiftStart = mAppsView.getTranslationY(); - interpolator = mFastOutSlowInInterpolator; - } else { - mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity)); - interpolator = mScrollInterpolator; - float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange; - if (nextFrameProgress <= 1f) { - mProgress = nextFrameProgress; - } - outconfig.shouldPost = false; - } - - outconfig.overrideDuration(mAnimationDuration); - ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", - mProgress, 1f); - driftAndAlpha.setDuration(mAnimationDuration); - driftAndAlpha.setInterpolator(interpolator); - animationOut.play(driftAndAlpha); - - animationOut.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - - @Override - public void onAnimationCancel(Animator animation) { - canceled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (canceled) { - return; - } else { - finishPullDown(); - cleanUpAnimation(); - mDetector.finishedScrolling(); - } - } - }); - mCurrentAnimation = animationOut; - } - - public void finishPullUp() { - mHotseat.setVisibility(View.INVISIBLE); - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.remove(mSearchSpring); - mSpringAnimationHandler.reset(); - } - setProgress(0f); - } - - public void finishPullDown() { - mAppsView.setVisibility(View.INVISIBLE); - mHotseat.setVisibility(View.VISIBLE); - mAppsView.reset(); - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.reset(); - } - setProgress(1f); - } - - private void cancelAnimation() { - if (mCurrentAnimation != null) { - mCurrentAnimation.cancel(); - mCurrentAnimation = null; - } - cancelDiscoveryAnimation(); - } - public void cancelDiscoveryAnimation() { if (mDiscoBounceAnimation == null) { return; @@ -532,10 +474,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mDiscoBounceAnimation = null; } - private void cleanUpAnimation() { - mCurrentAnimation = null; - } - public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) { mAppsView = appsView; mHotseat = hotseat; @@ -557,4 +495,27 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mShiftRange = scrollRange; setProgress(mProgress); } + + /** + * Set the final view states based on the progress. + * TODO: This logic should go in {@link LauncherState} + */ + private void onProgressAnimationEnd() { + if (Float.compare(mProgress, 1f) == 0) { + mAppsView.setVisibility(View.INVISIBLE); + mHotseat.setVisibility(View.VISIBLE); + mAppsView.reset(); + } else if (Float.compare(mProgress, 0f) == 0) { + mHotseat.setVisibility(View.INVISIBLE); + mAppsView.setVisibility(View.VISIBLE); + } + if (hasSpringAnimationHandler()) { + mSpringAnimationHandler.remove(mSearchSpring); + mSpringAnimationHandler.reset(); + } + + // TODO: This call should no longer be needed once caret stops animating. + setProgress(mProgress); + mDetector.finishedScrolling(); + } } diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java new file mode 100644 index 0000000000..feebc6c5de --- /dev/null +++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.anim; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; + +/** + * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations + */ +public abstract class AnimationSuccessListener extends AnimatorListenerAdapter { + + private boolean mCancelled = false; + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + onAnimationSuccess(animation); + } + } + + public abstract void onAnimationSuccess(Animator animator); +} diff --git a/src/com/android/launcher3/states/AllAppsState.java b/src/com/android/launcher3/states/AllAppsState.java new file mode 100644 index 0000000000..9922d999cd --- /dev/null +++ b/src/com/android/launcher3/states/AllAppsState.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.states; + +import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; + +import android.view.View; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +/** + * Definition for AllApps state + */ +public class AllAppsState extends LauncherState { + + public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; + + private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_HAS_SPRING; + + public AllAppsState(int id) { + super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS); + } + + @Override + public void onStateEnabled(Launcher launcher) { + if (!launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)) { + launcher.getSharedPrefs().edit().putBoolean(APPS_VIEW_SHOWN, true).apply(); + } + } + + @Override + public View getFinalFocus(Launcher launcher) { + return launcher.getAppsView(); + } +} diff --git a/src/com/android/launcher3/states/OverviewState.java b/src/com/android/launcher3/states/OverviewState.java index 911cec62f5..57f023cc6d 100644 --- a/src/com/android/launcher3/states/OverviewState.java +++ b/src/com/android/launcher3/states/OverviewState.java @@ -19,6 +19,7 @@ import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.Utilities.isAccessibilityEnabled; import android.graphics.Rect; +import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import com.android.launcher3.DeviceProfile; @@ -39,7 +40,7 @@ public class OverviewState extends LauncherState { FLAG_DO_NOT_RESTORE; public OverviewState(int id) { - super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS); + super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS); } @Override @@ -75,4 +76,9 @@ public class OverviewState extends LauncherState { public void onStateDisabled(Launcher launcher) { launcher.getWorkspace().setPageRearrangeEnabled(false); } + + @Override + public View getFinalFocus(Launcher launcher) { + return launcher.getOverviewPanel(); + } } diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java index f60ef0ca1b..3864e3a857 100644 --- a/src/com/android/launcher3/states/SpringLoadedState.java +++ b/src/com/android/launcher3/states/SpringLoadedState.java @@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_M import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.Handler; +import android.view.View; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InstallShortcutReceiver; @@ -41,7 +42,7 @@ public class SpringLoadedState extends LauncherState { private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500; public SpringLoadedState(int id) { - super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS); + super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, 1f, STATE_FLAGS); } @Override @@ -105,4 +106,9 @@ public class SpringLoadedState extends LauncherState { InstallShortcutReceiver.disableAndFlushInstallQueue( InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher); } + + @Override + public View getFinalFocus(Launcher launcher) { + return null; + } } From 4d519f25687a95d450666cf8452b9ba57bd36eb9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 24 Oct 2017 10:32:40 -0700 Subject: [PATCH 062/885] Enable overview state restore > Reapplying CellLayout state when new pages are added (page bind comes after restore) > Removing support for different scroll range for freescroll (the calculations were not consistant with maxScroll) Bug: 67678570 Change-Id: Ic1911de1b707f2f6940e7040f07ca7e733e2ef2a --- src/com/android/launcher3/Launcher.java | 4 +- src/com/android/launcher3/PagedView.java | 70 ++++--------------- src/com/android/launcher3/Workspace.java | 3 +- .../WorkspaceStateTransitionAnimation.java | 33 +++++---- .../launcher3/states/OverviewState.java | 3 +- 5 files changed, 36 insertions(+), 77 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 775dad2c80..e3acf70b2e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1031,8 +1031,8 @@ public class Launcher extends BaseActivity if (!state.doNotRestore) { if (state == LauncherState.ALL_APPS) { showAppsView(false /* animated */); - } else { - // TODO: Add logic for other states + } else if (state == LauncherState.OVERVIEW) { + showOverviewMode(false); } } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 5258fba927..f55455b7ef 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -49,7 +49,6 @@ import android.view.animation.Interpolator; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.touch.OverScroll; -import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -87,8 +86,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public static final int INVALID_RESTORE_PAGE = -1001; private boolean mFreeScroll = false; - private int mFreeScrollMinScrollX = -1; - private int mFreeScrollMaxScrollX = -1; protected int mFlingThresholdVelocity; protected int mMinFlingVelocity; @@ -137,7 +134,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mTouchSlop; private int mMaximumVelocity; protected boolean mAllowOverScroll = true; - protected int[] mTempVisiblePagesRange = new int[2]; protected static final int INVALID_POINTER = -1; @@ -380,16 +376,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } private int validateNewPage(int newPage) { - int validatedPage = newPage; - // When in free scroll mode, we need to clamp to the free scroll page range. - if (mFreeScroll) { - getFreeScrollPageRange(mTempVisiblePagesRange); - validatedPage = Math.max(mTempVisiblePagesRange[0], - Math.min(newPage, mTempVisiblePagesRange[1])); - } // Ensure that it is clamped by the actual set of children in all cases - validatedPage = Utilities.boundToRange(validatedPage, 0, getPageCount() - 1); - return validatedPage; + return Utilities.boundToRange(newPage, 0, getPageCount() - 1); } /** @@ -491,13 +479,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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)) { + if (!mScroller.isFinished() && (x > mMaxScrollX || x < 0)) { forceFinishScroller(false); } - x = Math.min(x, mFreeScrollMaxScrollX); - x = Math.max(x, mFreeScrollMinScrollX); + x = Utilities.boundToRange(x, 0, mMaxScrollX); } mUnboundedScrollX = x; @@ -886,7 +872,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return 0; } - @Thunk void updateMaxScrollX() { + private void updateMaxScrollX() { mMaxScrollX = computeMaxScrollX(); } @@ -915,13 +901,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // This ensures that when children are added, they get the correct transforms / alphas // in accordance with any scroll effects. - updateFreescrollBounds(); invalidate(); } @Override public void onChildViewRemoved(View parent, View child) { - updateFreescrollBounds(); mCurrentPage = validateNewPage(mCurrentPage); invalidate(); } @@ -974,11 +958,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return offset; } - protected void getFreeScrollPageRange(int[] range) { - range[0] = 0; - range[1] = Math.max(0, getChildCount() - 1); - } - @Override public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { int page = indexToPage(indexOfChild(child)); @@ -1349,29 +1328,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setEnableFreeScroll(false); } - void updateFreescrollBounds() { - getFreeScrollPageRange(mTempVisiblePagesRange); - if (mIsRtl) { - mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]); - mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]); - } else { - mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[0]); - mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[1]); - } - } - private void setEnableFreeScroll(boolean freeScroll) { boolean wasFreeScroll = mFreeScroll; mFreeScroll = freeScroll; if (mFreeScroll) { - updateFreescrollBounds(); - getFreeScrollPageRange(mTempVisiblePagesRange); - if (getCurrentPage() < mTempVisiblePagesRange[0]) { - setCurrentPage(mTempVisiblePagesRange[0]); - } else if (getCurrentPage() > mTempVisiblePagesRange[1]) { - setCurrentPage(mTempVisiblePagesRange[1]); - } + setCurrentPage(getNextPage()); } else if (wasFreeScroll) { snapToPage(getNextPage()); } @@ -1387,12 +1349,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (mDragView != null) { int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2) + mDragView.getTranslationX()); - getFreeScrollPageRange(mTempVisiblePagesRange); int minDistance = Integer.MAX_VALUE; int minIndex = indexOfChild(mDragView); - for (int i = mTempVisiblePagesRange[0]; i <= mTempVisiblePagesRange[1]; i++) { + int maxPageNo = getChildCount() - 1; + for (int i = 0; i <= maxPageNo; i++) { View page = getPageAt(i); - int pageX = (int) (page.getLeft() + page.getMeasuredWidth() / 2); + int pageX = (page.getLeft() + page.getMeasuredWidth() / 2); int d = Math.abs(dragX - pageX); if (d < minDistance) { minIndex = i; @@ -1487,11 +1449,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final int pageUnderPointIndex = getNearestHoverOverPageIndex(); // Do not allow any page to be moved to 0th position. if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) { - mTempVisiblePagesRange[0] = 0; - mTempVisiblePagesRange[1] = getPageCount() - 1; - getFreeScrollPageRange(mTempVisiblePagesRange); - if (mTempVisiblePagesRange[0] <= pageUnderPointIndex && - pageUnderPointIndex <= mTempVisiblePagesRange[1] && + if (0 <= pageUnderPointIndex && pageUnderPointIndex <= getPageCount() - 1 && pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) { mSidePageHoverIndex = pageUnderPointIndex; mSidePageHoverRunnable = new Runnable() { @@ -2025,18 +1983,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Do not allow the first page to be moved around if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false; - mTempVisiblePagesRange[0] = 0; - mTempVisiblePagesRange[1] = getPageCount() - 1; - getFreeScrollPageRange(mTempVisiblePagesRange); - mReorderingStarted = true; - // Check if we are within the reordering range - if (mTempVisiblePagesRange[0] <= dragViewIndex && - dragViewIndex <= mTempVisiblePagesRange[1]) { + if (0 <= dragViewIndex && dragViewIndex <= getPageCount() - 1) { // Find the drag view under the pointer mDragView = getChildAt(dragViewIndex); mDragView.animate().scaleX(1.15f).scaleY(1.15f).setDuration(100).start(); mDragViewBaselineLeft = mDragView.getLeft(); + mReorderingStarted = true; + snapToPage(getPageNearestToCenterOfScreen()); disableFreeScroll(); onStartReordering(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 173ff6d4a8..27d860b560 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -54,8 +54,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.Toast; @@ -576,6 +574,7 @@ public class Workspace extends PagedView mWorkspaceScreens.put(screenId, newScreen); mScreenOrder.add(insertIndex, screenId); addView(newScreen, insertIndex); + mStateTransitionAnimation.applyChildState(mState, newScreen, insertIndex); if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) { newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index d66255bda2..ff653d7ea7 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -178,9 +178,6 @@ public class WorkspaceStateTransitionAnimation { * Starts a transition animation for the workspace. */ private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter) { - // Update the workspace state - int finalBackgroundAlpha = state.hasScrim ? 255 : 0; - float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher); mNewScale = scaleAndTranslationY[0]; final float finalWorkspaceTranslationY = scaleAndTranslationY[1]; @@ -188,16 +185,8 @@ public class WorkspaceStateTransitionAnimation { int toPage = mWorkspace.getPageNearestToCenterOfScreen(); final int childCount = mWorkspace.getChildCount(); for (int i = 0; i < childCount; i++) { - final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); - propertySetter.setInt(cl.getScrimBackground(), - DRAWABLE_ALPHA, finalBackgroundAlpha, mZoomInInterpolator); - - // Only animate the page alpha when we actually fade pages - if (mWorkspaceFadeInAdjacentScreens) { - float finalAlpha = state == LauncherState.NORMAL && i != toPage ? 0 : 1f; - propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, - finalAlpha, mZoomInInterpolator); - } + applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, toPage, + propertySetter); } float finalHotseatAlpha = state.hideHotseat ? 0f : 1f; @@ -217,6 +206,24 @@ public class WorkspaceStateTransitionAnimation { state.hasScrim ? mWorkspaceScrimAlpha : 0, new DecelerateInterpolator(1.5f)); } + public void applyChildState(LauncherState state, CellLayout cl, int childIndex) { + applyChildState(state, cl, childIndex, mWorkspace.getPageNearestToCenterOfScreen(), + NO_ANIM_PROPERTY_SETTER); + } + + private void applyChildState(LauncherState state, CellLayout cl, int childIndex, + int centerPage, PropertySetter propertySetter) { + propertySetter.setInt(cl.getScrimBackground(), + DRAWABLE_ALPHA, state.hasScrim ? 255 : 0, mZoomInInterpolator); + + // Only animate the page alpha when we actually fade pages + if (mWorkspaceFadeInAdjacentScreens) { + float finalAlpha = state == LauncherState.NORMAL && childIndex != centerPage ? 0 : 1f; + propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, + finalAlpha, mZoomInInterpolator); + } + } + private static class PropertySetter { public void setViewAlpha(Animator anim, View view, float alpha) { diff --git a/src/com/android/launcher3/states/OverviewState.java b/src/com/android/launcher3/states/OverviewState.java index 57f023cc6d..344a4f95fd 100644 --- a/src/com/android/launcher3/states/OverviewState.java +++ b/src/com/android/launcher3/states/OverviewState.java @@ -36,8 +36,7 @@ public class OverviewState extends LauncherState { // The percent to shrink the workspace during overview mode public static final float SCALE_FACTOR = 0.7f; - private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT | - FLAG_DO_NOT_RESTORE; + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT; public OverviewState(int id) { super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS); From 0236d0b0bac7aae939852bd4acc5d1eadc2577fb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 24 Oct 2017 14:54:30 -0700 Subject: [PATCH 063/885] Caching the uninstall disabled state for each userHandle > Removing static access to varios drop targets > Creating a cache at UI level with 5sec timeout Bug: 67104426 Change-Id: Ide6e2e0c01606f9b5fb9281f95dc009873c18fb9 --- .../android/launcher3/ButtonDropTarget.java | 8 +++ .../android/launcher3/DeleteDropTarget.java | 25 +++++-- src/com/android/launcher3/DropTargetBar.java | 52 ++++++++------ src/com/android/launcher3/InfoDropTarget.java | 15 ++-- .../launcher3/UninstallDropTarget.java | 71 +++++++++++-------- .../LauncherAccessibilityDelegate.java | 36 +++++----- 6 files changed, 125 insertions(+), 82 deletions(-) diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 1a1c3198f6..5c8b080a3c 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -201,6 +201,10 @@ public abstract class ButtonDropTarget extends TextView protected abstract boolean supportsDrop(ItemInfo info); + public boolean supportsAccessibilityDrop(ItemInfo info) { + return supportsDrop(info); + } + @Override public boolean isDropEnabled() { return mActive && (mAccessibleDrag || @@ -241,9 +245,13 @@ public abstract class ButtonDropTarget extends TextView DragLayer.ANIMATION_END_DISAPPEAR, null); } + public abstract int getAccessibilityAction(); + @Override public void prepareAccessibilityDrop() { } + public abstract void onAccessibilityDrop(View view, ItemInfo item); + public abstract void completeDrop(DragObject d); @Override diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index fdd4f34bd5..c12ea57c37 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -21,6 +21,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.folder.Folder; @@ -49,13 +50,21 @@ public class DeleteDropTarget extends ButtonDropTarget { setTextBasedOnDragSource(dragObject.dragInfo); } - /** @return true for items that should have a "Remove" action in accessibility. */ - public static boolean supportsAccessibleDrop(ItemInfo info) { + /** + * @return true for items that should have a "Remove" action in accessibility. + */ + @Override + public boolean supportsAccessibilityDrop(ItemInfo info) { return (info instanceof ShortcutInfo) || (info instanceof LauncherAppWidgetInfo) || (info instanceof FolderInfo); } + @Override + public int getAccessibilityAction() { + return LauncherAccessibilityDelegate.REMOVE; + } + @Override protected boolean supportsDrop(ItemInfo info) { return true; @@ -77,19 +86,21 @@ public class DeleteDropTarget extends ButtonDropTarget { public void completeDrop(DragObject d) { ItemInfo item = d.dragInfo; if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) { - removeWorkspaceOrFolderItem(mLauncher, item, null); + onAccessibilityDrop(null, item); } } /** * Removes the item from the workspace. If the view is not null, it also removes the view. */ - public static void removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) { + @Override + public void onAccessibilityDrop(View view, ItemInfo item) { // Remove the item from launcher and the db, we can ignore the containerInfo in this call // because we already remove the drag view from the folder (if the drag originated from // a folder) in Folder.beginDrag() - launcher.removeItem(view, item, true /* deleteFromDb */); - launcher.getWorkspace().stripEmptyScreens(); - launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed)); + mLauncher.removeItem(view, item, true /* deleteFromDb */); + mLauncher.getWorkspace().stripEmptyScreens(); + mLauncher.getDragLayer() + .announceForAccessibility(getContext().getString(R.string.item_removed)); } } diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index 29a1349d5c..2f8374a5e5 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static com.android.launcher3.AlphaUpdateListener.updateVisibility; +import static com.android.launcher3.Utilities.isAccessibilityEnabled; + import android.animation.TimeInterpolator; import android.content.Context; import android.util.AttributeSet; @@ -23,13 +26,14 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; -import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.widget.LinearLayout; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; +import java.util.ArrayList; + /* * The top bar containing various drop targets: Delete/App Info/Uninstall. */ @@ -42,10 +46,7 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi @Override public void run() { - AccessibilityManager am = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - boolean accessibilityEnabled = am.isEnabled(); - AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled); + updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext())); } }; @@ -55,6 +56,7 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi @ViewDebug.ExportedProperty(category = "launcher") protected boolean mVisible = false; + private ButtonDropTarget[] mDropTargets; private ViewPropertyAnimator mCurrentAnimation; public DropTargetBar(Context context, AttributeSet attrs) { @@ -75,7 +77,27 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi public void setup(DragController dragController) { dragController.addDragListener(this); - setupButtonDropTarget(this, dragController); + ArrayList outList = new ArrayList<>(); + findDropTargets(this, outList); + + mDropTargets = new ButtonDropTarget[outList.size()]; + for (int i = 0; i < mDropTargets.length; i++) { + mDropTargets[i] = outList.get(i); + mDropTargets[i].setDropTargetBar(this); + dragController.addDragListener(mDropTargets[i]); + dragController.addDropTarget(mDropTargets[i]); + } + } + + private static void findDropTargets(View view, ArrayList outTargets) { + if (view instanceof ButtonDropTarget) { + outTargets.add((ButtonDropTarget) view); + } else if (view instanceof ViewGroup) { + ViewGroup vg = (ViewGroup) view; + for (int i = vg.getChildCount() - 1; i >= 0; i--) { + findDropTargets(vg.getChildAt(i), outTargets); + } + } } @Override @@ -130,20 +152,6 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi return result; } - private void setupButtonDropTarget(View view, DragController dragController) { - if (view instanceof ButtonDropTarget) { - ButtonDropTarget bdt = (ButtonDropTarget) view; - bdt.setDropTargetBar(this); - dragController.addDragListener(bdt); - dragController.addDropTarget(bdt); - } else if (view instanceof ViewGroup) { - ViewGroup vg = (ViewGroup) view; - for (int i = vg.getChildCount() - 1; i >= 0; i--) { - setupButtonDropTarget(vg.getChildAt(i), dragController); - } - } - } - private void animateToVisibility(boolean isVisible) { if (mVisible != isVisible) { mVisible = isVisible; @@ -190,4 +198,8 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi mDeferOnDragEnd = false; } } + + public ButtonDropTarget[] getDropTargets() { + return mDropTargets; + } } diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index f78cde5b8b..289242f61e 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -26,6 +26,7 @@ import android.util.AttributeSet; import android.util.Log; import android.widget.Toast; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.util.Themes; @@ -49,8 +50,8 @@ public class InfoDropTarget extends UninstallDropTarget { } @Override - protected ComponentName performDropAction(DragObject d) { - return performDropAction(mLauncher, d.dragInfo, null, null); + protected ComponentName performDropAction(ItemInfo item) { + return performDropAction(mLauncher, item, null, null); } /** @@ -96,13 +97,15 @@ public class InfoDropTarget extends UninstallDropTarget { } @Override - protected boolean supportsDrop(ItemInfo info) { - return supportsDrop(getContext(), info); + public int getAccessibilityAction() { + return LauncherAccessibilityDelegate.INFO; } - public static boolean supportsDrop(Context context, ItemInfo info) { + @Override + protected boolean supportsDrop(ItemInfo info) { // Only show the App Info drop target if developer settings are enabled. - boolean developmentSettingsEnabled = Settings.Global.getInt(context.getContentResolver(), + boolean developmentSettingsEnabled = Settings.Global.getInt( + getContext().getContentResolver(), Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1; if (!developmentSettingsEnabled) { return false; diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 43938194e9..8e83a30c0a 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -10,22 +10,28 @@ import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.Toast; import com.android.launcher3.Launcher.OnResumeCallback; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import java.net.URISyntaxException; -public class UninstallDropTarget extends ButtonDropTarget { +public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmListener { private static final String TAG = "UninstallDropTarget"; - private static Boolean sUninstallDisabled; + + private static final long CACHE_EXPIRE_TIMEOUT = 5000; + private final ArrayMap mUninstallDisabledCache = new ArrayMap<>(1); + + private final Alarm mCacheExpireAlarm; public UninstallDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -33,6 +39,9 @@ public class UninstallDropTarget extends ButtonDropTarget { public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + + mCacheExpireAlarm = new Alarm(); + mCacheExpireAlarm.setOnAlarmListener(this); } @Override @@ -48,18 +57,29 @@ public class UninstallDropTarget extends ButtonDropTarget { } @Override - protected boolean supportsDrop(ItemInfo info) { - return supportsDrop(getContext(), info); + public void onAlarm(Alarm alarm) { + mUninstallDisabledCache.clear(); } - public static boolean supportsDrop(Context context, ItemInfo info) { - if (sUninstallDisabled == null) { - UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - Bundle restrictions = userManager.getUserRestrictions(); - sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) + @Override + public int getAccessibilityAction() { + return LauncherAccessibilityDelegate.UNINSTALL; + } + + @Override + protected boolean supportsDrop(ItemInfo info) { + Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user); + if (uninstallDisabled == null) { + UserManager userManager = + (UserManager) getContext().getSystemService(Context.USER_SERVICE); + Bundle restrictions = userManager.getUserRestrictions(info.user); + uninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false); + mUninstallDisabledCache.put(info.user, uninstallDisabled); } - if (sUninstallDisabled) { + // Cancel any pending alarm and set cache expiry after some time + mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT); + if (uninstallDisabled) { return false; } @@ -69,13 +89,13 @@ public class UninstallDropTarget extends ButtonDropTarget { return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0; } } - return getUninstallTarget(context, info) != null; + return getUninstallTarget(info) != null; } /** * @return the component name that should be uninstalled or null. */ - private static ComponentName getUninstallTarget(Context context, ItemInfo item) { + private ComponentName getUninstallTarget(ItemInfo item) { Intent intent = null; UserHandle user = null; if (item != null && @@ -84,7 +104,7 @@ public class UninstallDropTarget extends ButtonDropTarget { user = item.user; } if (intent != null) { - LauncherActivityInfo info = LauncherAppsCompat.getInstance(context) + LauncherActivityInfo info = LauncherAppsCompat.getInstance(mLauncher) .resolveActivity(intent, user); if (info != null && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) { @@ -103,7 +123,7 @@ public class UninstallDropTarget extends ButtonDropTarget { @Override public void completeDrop(final DragObject d) { - ComponentName target = performDropAction(d); + ComponentName target = performDropAction(d.dragInfo); if (d.dragSource instanceof DeferredOnComplete) { DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource; if (target != null) { @@ -119,27 +139,19 @@ public class UninstallDropTarget extends ButtonDropTarget { * Performs the drop action and returns the target component for the dragObject or null if * the action was not performed. */ - protected ComponentName performDropAction(DragObject d) { - return performDropAction(mLauncher, d.dragInfo); - } - - /** - * Performs the drop action and returns the target component for the dragObject or null if - * the action was not performed. - */ - private static ComponentName performDropAction(Context context, ItemInfo info) { - ComponentName cn = getUninstallTarget(context, info); + protected ComponentName performDropAction(ItemInfo info) { + ComponentName cn = getUninstallTarget(info); if (cn == null) { // System applications cannot be installed. For now, show a toast explaining that. // We may give them the option of disabling apps this way. - Toast.makeText(context, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); + Toast.makeText(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); return null; } try { - Intent i = Intent.parseUri(context.getString(R.string.delete_package_intent), 0) + Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0) .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) .putExtra(Intent.EXTRA_USER, info.user); - context.startActivity(i); + mLauncher.startActivity(i); return cn; } catch (URISyntaxException e) { Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info); @@ -147,8 +159,9 @@ public class UninstallDropTarget extends ButtonDropTarget { } } - public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { - return performDropAction(launcher, info) != null; + @Override + public void onAccessibilityDrop(View view, ItemInfo item) { + performDropAction(item); } /** diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 583492e5a3..f17818a42b 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -17,6 +17,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.launcher3.AppInfo; import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.ButtonDropTarget; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DropTarget.DragObject; @@ -45,9 +46,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme private static final String TAG = "LauncherAccessibilityDelegate"; - protected static final int REMOVE = R.id.action_remove; - protected static final int INFO = R.id.action_info; - protected static final int UNINSTALL = R.id.action_uninstall; + 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; protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; protected static final int MOVE = R.id.action_move; protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; @@ -108,14 +109,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme info.addAction(mActions.get(DEEP_SHORTCUTS)); } - if (DeleteDropTarget.supportsAccessibleDrop(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)); + for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) { + if (target.supportsAccessibilityDrop(item)) { + info.addAction(mActions.get(target.getAccessibilityAction())); + } } // Do not add move actions for keyboard request as this uses virtual nodes. @@ -148,15 +145,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } public boolean performAction(final View host, final ItemInfo item, int action) { - if (action == REMOVE) { - DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host); - return true; - } else if (action == INFO) { - InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null, null); - return true; - } else if (action == UNINSTALL) { - return UninstallDropTarget.startUninstallActivity(mLauncher, item); - } else if (action == MOVE) { + if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { final int[] coordinates = new int[2]; @@ -231,6 +220,13 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme return true; } else if (action == DEEP_SHORTCUTS) { return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null; + } else { + for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) { + if (action == dropTarget.getAccessibilityAction()) { + dropTarget.onAccessibilityDrop(host, item); + return true; + } + } } return false; } From 3e3f44c3ade314c6186332d081c7d4d1a39155db Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 23 Oct 2017 17:14:52 -0700 Subject: [PATCH 064/885] Exposing the state manager directly instead of providing various helper methods for state change Bug: 67678570 Change-Id: If3d05c804c034ffa5e403da8eaa23e85e373c863 --- .../android/launcher3/ButtonDropTarget.java | 4 +- src/com/android/launcher3/Launcher.java | 225 ++++-------------- .../android/launcher3/LauncherAnimUtils.java | 1 - src/com/android/launcher3/LauncherState.java | 15 +- ...imation.java => LauncherStateManager.java} | 60 ++++- .../launcher3/PinchToOverviewListener.java | 24 +- src/com/android/launcher3/Workspace.java | 28 ++- .../WorkspaceStateTransitionAnimation.java | 2 +- .../LauncherAccessibilityDelegate.java | 9 +- .../OverviewAccessibilityDelegate.java | 3 +- .../ShortcutMenuAccessibilityDelegate.java | 6 +- .../allapps/AllAppsTransitionController.java | 15 +- .../launcher3/dragndrop/DragController.java | 3 +- src/com/android/launcher3/folder/Folder.java | 3 +- .../launcher3/states/AllAppsState.java | 10 + .../launcher3/util/FlingAnimation.java | 4 +- 16 files changed, 184 insertions(+), 228 deletions(-) rename src/com/android/launcher3/{LauncherStateTransitionAnimation.java => LauncherStateManager.java} (78%) diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 1a1c3198f6..bebdbdb351 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -16,7 +16,7 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; +import static com.android.launcher3.LauncherState.NORMAL; import android.animation.AnimatorSet; import android.animation.FloatArrayEvaluator; @@ -231,7 +231,7 @@ public abstract class ButtonDropTarget extends TextView public void run() { completeDrop(d); mDropTargetBar.onDragEnd(); - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); + mLauncher.getStateManager().goToState(NORMAL); } }; dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index e3acf70b2e..cd9fffc565 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -19,8 +19,10 @@ package com.android.launcher3; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS; import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS; @@ -58,7 +60,6 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; @@ -203,7 +204,7 @@ public class Launcher extends BaseActivity // Type: SparseArray private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel"; - private LauncherStateTransitionAnimation mStateTransitionAnimation; + private LauncherStateManager mStateManager; private boolean mIsSafeModeEnabled; @@ -257,7 +258,6 @@ public class Launcher extends BaseActivity private ModelWriter mModelWriter; private IconCache mIconCache; private LauncherAccessibilityDelegate mAccessibilityDelegate; - private final Handler mHandler = new Handler(); private boolean mHasFocus = false; private ObjectAnimator mScrimAnimator; @@ -271,11 +271,6 @@ public class Launcher extends BaseActivity // it from the context. private SharedPreferences mSharedPrefs; - // Exiting spring loaded mode happens with a delay. This runnable object triggers the - // state transition. If another state transition happened during this delay, - // simply unregister this runnable. - private Runnable mExitSpringLoadedModeRunnable; - // Activity result which needs to be processed after workspace has loaded. private ActivityResultInfo mPendingActivityResult; /** @@ -342,7 +337,7 @@ public class Launcher extends BaseActivity mDragController = new DragController(this); mAllAppsController = new AllAppsTransitionController(this); - mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController); + mStateManager = new LauncherStateManager(this, mAllAppsController); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); @@ -430,8 +425,8 @@ public class Launcher extends BaseActivity recreate(); } - public LauncherStateTransitionAnimation getStateTransition() { - return mStateTransitionAnimation; + public LauncherStateManager getStateManager() { + return mStateManager; } protected void overrideTheme(boolean isDark, boolean supportsDarkText) { @@ -565,7 +560,7 @@ public class Launcher extends BaseActivity Runnable exitSpringLoaded = new Runnable() { @Override public void run() { - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); } }; @@ -585,11 +580,11 @@ public class Launcher extends BaseActivity } return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { - if (resultCode == RESULT_OK && isInState(LauncherState.OVERVIEW)) { + if (resultCode == RESULT_OK && isInState(OVERVIEW)) { // User could have free-scrolled between pages before picking a wallpaper; make sure // we move to the closest one now. mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen()); - showWorkspace(false); + mStateManager.goToState(NORMAL, false); } return; } @@ -617,7 +612,7 @@ public class Launcher extends BaseActivity final Runnable onComplete = new Runnable() { @Override public void run() { - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); + getStateManager().goToState(NORMAL); } }; @@ -745,7 +740,7 @@ public class Launcher extends BaseActivity @Override public void run() { completeAddAppWidget(appWidgetId, requestArgs, layout, null); - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); } }; } else if (resultCode == RESULT_CANCELED) { @@ -990,9 +985,16 @@ public class Launcher extends BaseActivity AbstractFloatingView.closeAllOpenViews(this); // Show the overview mode if we are on the workspace - if (isInState(LauncherState.NORMAL) && !mWorkspace.isSwitchingState()) { - mOverviewPanel.requestFocus(); - showOverviewMode(true, true /* requestButtonFocus */); + if (isInState(NORMAL) && !mWorkspace.isSwitchingState()) { + mStateManager.goToState(OVERVIEW, true /* animate */, new Runnable() { + @Override + public void run() { + // Hitting the menu button when in touch mode does not trigger touch + // mode to be disabled, so if requested, force focus on one of the + // overview panel buttons. + mOverviewPanel.requestFocusFromTouch(); + } + }); } } return true; @@ -1025,15 +1027,11 @@ public class Launcher extends BaseActivity return; } - int stateOrdinal = savedState.getInt(RUNTIME_STATE, LauncherState.NORMAL.ordinal); + int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal); LauncherState[] stateValues = LauncherState.values(); LauncherState state = stateValues[stateOrdinal]; if (!state.doNotRestore) { - if (state == LauncherState.ALL_APPS) { - showAppsView(false /* animated */); - } else if (state == LauncherState.OVERVIEW) { - showOverviewMode(false); - } + mStateManager.goToState(state, false /* animated */); } PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS); @@ -1311,10 +1309,7 @@ public class Launcher extends BaseActivity // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop if (mAppsView != null && mPendingRequestArgs == null) { - if (!showWorkspace(false)) { - // If we are already on the workspace, then manually reset all apps - mAppsView.reset(); - } + mStateManager.goToState(NORMAL); } mShouldFadeInScrim = true; } else if (Intent.ACTION_USER_PRESENT.equals(action)) { @@ -1414,7 +1409,7 @@ public class Launcher extends BaseActivity != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); // Check this condition before handling isActionMain, as this will get reset. - boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(LauncherState.NORMAL) + boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL) && AbstractFloatingView.getTopOpenView(this) == null; boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); @@ -1438,7 +1433,7 @@ public class Launcher extends BaseActivity // In all these cases, only animate if we're already on home AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome); - showWorkspace(alreadyOnHome /* animated */); + mStateManager.goToState(NORMAL, alreadyOnHome /* animated */); final View v = getWindow().peekDecorView(); if (v != null && v.getWindowToken() != null) { @@ -1615,7 +1610,7 @@ public class Launcher extends BaseActivity } // We need to show the workspace after starting the search - showWorkspace(true); + mStateManager.goToState(NORMAL); } /** @@ -1707,7 +1702,7 @@ public class Launcher extends BaseActivity @Override public void run() { // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); } }; completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this)); @@ -1888,9 +1883,9 @@ public class Launcher extends BaseActivity AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this); if (topView != null) { topView.onBackPressed(); - } else if (!isInState(LauncherState.NORMAL)) { + } else if (!isInState(NORMAL)) { ued.logActionCommand(Action.Command.BACK, mWorkspace.getState().containerType); - showWorkspace(true); + mStateManager.goToState(NORMAL); } else { // Back button is a no-op here, but give at least some feedback for the button press mWorkspace.showOutlinesTemporarily(); @@ -1914,23 +1909,23 @@ public class Launcher extends BaseActivity } if (v instanceof Workspace) { - if (isInState(LauncherState.OVERVIEW)) { + if (isInState(OVERVIEW)) { getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH, LauncherLogProto.Action.Direction.NONE, LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage()); - showWorkspace(true); + mStateManager.goToState(NORMAL); } return; } if (v instanceof CellLayout) { - if (isInState(LauncherState.OVERVIEW)) { + if (isInState(OVERVIEW)) { int page = mWorkspace.indexOfChild(v); getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH, LauncherLogProto.Action.Direction.NONE, LauncherLogProto.ContainerType.OVERVIEW, page); mWorkspace.snapToPageFromOverView(page); - showWorkspace(true); + mStateManager.goToState(NORMAL); } return; } @@ -2002,12 +1997,12 @@ public class Launcher extends BaseActivity */ protected void onClickAllAppsButton(View v) { if (LOGD) Log.d(TAG, "onClickAllAppsButton"); - if (!isInState(LauncherState.ALL_APPS)) { + if (!isInState(ALL_APPS)) { getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, ControlType.ALL_APPS_BUTTON); - showAppsView(true /* animated */); + mStateManager.goToState(ALL_APPS); } else { - showWorkspace(true); + mStateManager.goToState(NORMAL); } } @@ -2333,18 +2328,18 @@ public class Launcher extends BaseActivity public boolean onLongClick(View v) { if (!isDraggingEnabled()) return false; if (isWorkspaceLocked()) return false; - if (!isInState(LauncherState.NORMAL) && !isInState(LauncherState.OVERVIEW)) return false; + if (!isInState(NORMAL) && !isInState(OVERVIEW)) return false; boolean ignoreLongPressToOverview = mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX); if (v instanceof Workspace) { - if (!isInState(LauncherState.OVERVIEW)) { + if (!isInState(OVERVIEW)) { if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) { getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS, Action.Direction.NONE, ContainerType.WORKSPACE, mWorkspace.getCurrentPage()); - showOverviewMode(true); + getStateManager().goToState(OVERVIEW); mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); return true; @@ -2381,7 +2376,7 @@ public class Launcher extends BaseActivity getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS, Action.Direction.NONE, ContainerType.WORKSPACE, mWorkspace.getCurrentPage()); - showOverviewMode(true); + getStateManager().goToState(OVERVIEW); } mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); @@ -2436,134 +2431,16 @@ public class Launcher extends BaseActivity } } - public boolean showWorkspace(boolean animated) { - return showWorkspace(animated, null); - } - - public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) { - boolean changed = !isInState(LauncherState.NORMAL); - if (changed || mAllAppsController.isTransitioning()) { - mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.goToState(LauncherState.NORMAL, animated, onCompleteRunnable); - - // Set focus to the AppsCustomize button - if (mAllAppsButton != null) { - mAllAppsButton.requestFocus(); - } - } - - if (changed) { - // Send an accessibility event to announce the context change - getWindow().getDecorView() - .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - } - return changed; - } - - /** - * Shows the overview button. - */ - public void showOverviewMode(boolean animated) { - showOverviewMode(animated, false); - } - - /** - * Shows the overview button, and if {@param requestButtonFocus} is set, will force the focus - * onto one of the overview panel buttons. - */ - void showOverviewMode(boolean animated, boolean requestButtonFocus) { - Runnable postAnimRunnable = null; - if (requestButtonFocus) { - postAnimRunnable = new Runnable() { - @Override - public void run() { - // Hitting the menu button when in touch mode does not trigger touch mode to - // be disabled, so if requested, force focus on one of the overview panel - // buttons. - mOverviewPanel.requestFocusFromTouch(); - } - }; - } - mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.goToState(LauncherState.OVERVIEW, animated, postAnimRunnable); - - // If animated from long press, then don't allow any of the controller in the drag - // layer to intercept any remaining touch. - mWorkspace.requestDisallowInterceptTouchEvent(animated); - } - - /** - * Shows the apps view. - * - * @return whether the current from and to state allowed this operation - */ - // TODO: calling method should use the return value so that when {@code false} is returned - // the workspace transition doesn't fall into invalid state. - public boolean showAppsView(boolean animated) { - if (!(isInState(LauncherState.NORMAL) || - (isInState(LauncherState.ALL_APPS) && mAllAppsController.isTransitioning()))) { - return false; - } - - // This is a safe and supported transition to bypass spring_loaded mode. - if (mExitSpringLoadedModeRunnable != null) { - mHandler.removeCallbacks(mExitSpringLoadedModeRunnable); - mExitSpringLoadedModeRunnable = null; - } - - mStateTransitionAnimation.goToState(LauncherState.ALL_APPS, animated, null); - - // Change the state *after* we've called all the transition code - AbstractFloatingView.closeAllOpenViews(this); - - // Send an accessibility event to announce the context change - getWindow().getDecorView() - .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - return true; - } - - public void enterSpringLoadedDragMode() { - if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", - mWorkspace.getState().ordinal)); - if (isInState(LauncherState.SPRING_LOADED)) { - return; - } - mStateTransitionAnimation.goToState(LauncherState.SPRING_LOADED, true, null); - } - - public void exitSpringLoadedDragMode(int delay) { - exitSpringLoadedDragMode(delay, null); - } - - public void exitSpringLoadedDragMode(int delay, final Runnable onCompleteRunnable) { - if (!isInState(LauncherState.SPRING_LOADED)) return; - - if (mExitSpringLoadedModeRunnable != null) { - mHandler.removeCallbacks(mExitSpringLoadedModeRunnable); - } - mExitSpringLoadedModeRunnable = new Runnable() { - @Override - public void run() { - showWorkspace(true, onCompleteRunnable); - mExitSpringLoadedModeRunnable = null; - } - }; - mHandler.postDelayed(mExitSpringLoadedModeRunnable, delay); - } - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); final List text = event.getText(); text.clear(); // Populate event with a fake title based on the current state. - if (isInState(LauncherState.ALL_APPS)) { - text.add(getString(R.string.all_apps_button_label)); - } else if (mWorkspace != null) { - text.add(mWorkspace.getCurrentPageDescription()); - } else { - text.add(getString(R.string.all_apps_home_button_label)); - } + // TODO: When can workspace be null? + text.add(mWorkspace == null + ? getString(R.string.all_apps_home_button_label) + : mWorkspace.getState().getDescription(this)); return result; } @@ -3114,7 +2991,7 @@ public class Launcher extends BaseActivity if (mAppsView != null) { Executor pendingExecutor = getPendingExecutor(); - if (pendingExecutor != null && !isInState(LauncherState.ALL_APPS)) { + if (pendingExecutor != null && !isInState(ALL_APPS)) { // Wait until the fade in animation has finished before setting all apps list. pendingExecutor.execute(r); return; @@ -3304,7 +3181,7 @@ public class Launcher extends BaseActivity } private boolean shouldShowDiscoveryBounce() { - return isInState(LauncherState.NORMAL) + return isInState(NORMAL) && !mSharedPrefs.getBoolean(AllAppsState.APPS_VIEW_SHOWN, false) && !UserManagerCompat.getInstance(this).isDemoUser(); } @@ -3364,7 +3241,7 @@ public class Launcher extends BaseActivity List data, Menu menu, int deviceId) { ArrayList shortcutInfos = new ArrayList<>(); - if (isInState(LauncherState.NORMAL)) { + if (isInState(NORMAL)) { shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label), KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); } @@ -3390,8 +3267,8 @@ public class Launcher extends BaseActivity if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { switch (keyCode) { case KeyEvent.KEYCODE_A: - if (isInState(LauncherState.NORMAL)) { - showAppsView(true); + if (isInState(NORMAL)) { + getStateManager().goToState(ALL_APPS); return true; } break; diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 358511046c..dfe51af868 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -38,7 +38,6 @@ public class LauncherAnimUtils { public static final int OVERVIEW_TRANSITION_MS = 250; public static final int SPRING_LOADED_TRANSITION_MS = 150; public static final int SPRING_LOADED_EXIT_DELAY = 500; - public static final int SPRING_LOADED_EXIT_NEXT_FRAME = 0; static WeakHashMap sAnimators = new WeakHashMap(); static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() { diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 9d01ed82d4..de3f441c23 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -17,8 +17,7 @@ package com.android.launcher3; import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; - -import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; +import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; import android.view.View; @@ -121,11 +120,21 @@ public class LauncherState { return new float[] {1, 0}; } - public void onStateEnabled(Launcher launcher) { } + public void onStateEnabled(Launcher launcher) { + dispatchWindowStateChanged(launcher); + } public void onStateDisabled(Launcher launcher) { } public View getFinalFocus(Launcher launcher) { return launcher.getWorkspace(); } + + public String getDescription(Launcher launcher) { + return launcher.getWorkspace().getCurrentPageDescription(); + } + + protected static void dispatchWindowStateChanged(Launcher launcher) { + launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED); + } } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateManager.java similarity index 78% rename from src/com/android/launcher3/LauncherStateTransitionAnimation.java rename to src/com/android/launcher3/LauncherStateManager.java index eec8b31c41..4298174aa1 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -68,23 +68,73 @@ import com.android.launcher3.anim.AnimationSuccessListener; * - From the center workspace * - From another workspace */ -public class LauncherStateTransitionAnimation { +public class LauncherStateManager { - public static final String TAG = "LSTAnimation"; + public static final String TAG = "StateManager"; private final AnimationConfig mConfig = new AnimationConfig(); private final Handler mUiHandler; private final Launcher mLauncher; private final AllAppsTransitionController mAllAppsController; - public LauncherStateTransitionAnimation( + public LauncherStateManager( Launcher l, AllAppsTransitionController allAppsController) { mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; mAllAppsController = allAppsController; } + /** + * @see #goToState(LauncherState, boolean, Runnable) + */ + public void goToState(LauncherState state) { + goToState(state, true, 0, null); + } + + /** + * @see #goToState(LauncherState, boolean, Runnable) + */ + public void goToState(LauncherState state, boolean animated) { + goToState(state, animated, 0, null); + } + + /** + * Changes the Launcher state to the provided state. + * + * @param animated false if the state should change immediately without any animation, + * true otherwise + * @paras onCompleteRunnable any action to perform at the end of the transition, of null. + */ public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) { + goToState(state, animated, 0, onCompleteRunnable); + } + + /** + * Changes the Launcher state to the provided state after the given delay. + */ + public void goToState(LauncherState state, long delay, Runnable onCompleteRunnable) { + goToState(state, true, delay, onCompleteRunnable); + } + + /** + * Changes the Launcher state to the provided state after the given delay. + */ + public void goToState(LauncherState state, long delay) { + goToState(state, true, delay, null); + } + + private void goToState(LauncherState state, boolean animated, long delay, + Runnable onCompleteRunnable) { + if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null + && !mAllAppsController.isTransitioning()) { + + // Run any queued runnable + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + return; + } + // Cancel the current animation mConfig.reset(); @@ -102,7 +152,9 @@ public class LauncherStateTransitionAnimation { AnimatorSet animation = createAnimationToNewWorkspace(state, onCompleteRunnable); Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher)); - if (mConfig.shouldPost) { + if (delay > 0) { + mUiHandler.postDelayed(runnable, delay); + } else if (mConfig.shouldPost) { mUiHandler.post(runnable); } else { runnable.run(); diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index 407f0b59d2..fc75fe3f44 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; + import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; @@ -75,8 +78,8 @@ public class PinchToOverviewListener @Override public boolean onScaleBegin(ScaleGestureDetector detector) { - if (!mLauncher.isInState(LauncherState.NORMAL) - && !mLauncher.isInState(LauncherState.OVERVIEW)) { + if (!mLauncher.isInState(NORMAL) + && !mLauncher.isInState(OVERVIEW)) { // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. return false; } @@ -105,9 +108,8 @@ public class PinchToOverviewListener mLauncher.getDragController().cancelDrag(); } - mToState = mLauncher.isInState(LauncherState.OVERVIEW) - ? LauncherState.NORMAL : LauncherState.OVERVIEW; - mCurrentAnimation = mLauncher.getStateTransition() + mToState = mLauncher.isInState(OVERVIEW) ? NORMAL : OVERVIEW; + mCurrentAnimation = mLauncher.getStateManager() .createAnimationToNewWorkspace(mToState, this); mPinchStarted = true; mCurrentScale = 1; @@ -132,11 +134,8 @@ public class PinchToOverviewListener mCurrentAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (mToState == LauncherState.OVERVIEW) { - mLauncher.showWorkspace(false); - } else { - mLauncher.showOverviewMode(false); - } + mLauncher.getStateManager().goToState( + mToState == OVERVIEW ? NORMAL : OVERVIEW, false); } }); mCurrentAnimation.reverse(); @@ -150,12 +149,11 @@ public class PinchToOverviewListener // If we are zooming out, inverse the mCurrentScale so that animationFraction = [0, 1] // 0 => Animation complete // 1=> Animation started - float animationFraction = mToState == - LauncherState.OVERVIEW ? mCurrentScale : (1 / mCurrentScale); + float animationFraction = mToState == OVERVIEW ? mCurrentScale : (1 / mCurrentScale); float velocity = (1 - detector.getScaleFactor()) / detector.getTimeDelta(); if (Math.abs(velocity) >= FLING_VELOCITY) { - LauncherState toState = velocity > 0 ? LauncherState.OVERVIEW : LauncherState.NORMAL; + LauncherState toState = velocity > 0 ? OVERVIEW : NORMAL; mShouldGoToFinalState = toState == mToState; } else { mShouldGoToFinalState = animationFraction <= ACCEPT_THRESHOLD; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 27d860b560..900e5bfa94 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -16,10 +16,12 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.Utilities.isAccessibilityEnabled; import android.animation.Animator; @@ -60,7 +62,7 @@ import android.widget.Toast; import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; -import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; +import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.OverviewAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; @@ -205,7 +207,7 @@ public class Workspace extends PagedView private final float[] mHotseatAlpha = new float[] {1, 1, 1}; @ViewDebug.ExportedProperty(category = "launcher") - private LauncherState mState = LauncherState.NORMAL; + private LauncherState mState = NORMAL; private boolean mIsSwitchingState = false; boolean mChildrenLayersEnabled = true; @@ -411,7 +413,7 @@ public class Workspace extends PagedView } // Always enter the spring loaded mode - mLauncher.enterSpringLoadedDragMode(); + mLauncher.getStateManager().goToState(SPRING_LOADED); } public void deferRemoveExtraEmptyScreen() { @@ -1342,7 +1344,7 @@ public class Workspace extends PagedView @Override public void announceForAccessibility(CharSequence text) { // Don't announce if apps is on top of us. - if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + if (!mLauncher.isInState(ALL_APPS)) { super.announceForAccessibility(text); } } @@ -1410,12 +1412,12 @@ public class Workspace extends PagedView } public boolean workspaceInModalState() { - return mState != LauncherState.NORMAL; + return mState != NORMAL; } /** Returns whether a drag should be allowed to be started from the current workspace state. */ public boolean workspaceIconsCanBeDragged() { - return mState == LauncherState.NORMAL || mState == LauncherState.SPRING_LOADED; + return mState == NORMAL || mState == SPRING_LOADED; } private void updateChildrenLayersEnabled() { @@ -2014,7 +2016,7 @@ public class Workspace extends PagedView dropTargetLayout, mTargetCell, distance, false, d.dragView) || addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, d, false)) { - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); return; } @@ -2129,7 +2131,7 @@ public class Workspace extends PagedView // spring-loaded mode so the page meets the icon where it was picked up. mLauncher.getDragController().animateDragViewToOriginalPosition( onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS); - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); + mLauncher.getStateManager().goToState(NORMAL); mLauncher.getDropTargetBar().onDragEnd(); parent.onDropChild(cell); return; @@ -2152,8 +2154,8 @@ public class Workspace extends PagedView } parent.onDropChild(cell); - mLauncher.exitSpringLoadedDragMode( - SPRING_LOADED_EXIT_DELAY, onCompleteRunnable); + mLauncher.getStateManager().goToState( + NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable); } if (d.stateAnnouncer != null && !droppedOnOriginalCell) { @@ -2685,7 +2687,7 @@ public class Workspace extends PagedView final long screenId = getIdForScreen(cellLayout); if (!mLauncher.isHotseatLayout(cellLayout) && screenId != getScreenIdForPageIndex(mCurrentPage) - && mState != LauncherState.SPRING_LOADED) { + && mState != SPRING_LOADED) { snapToPage(getPageIndexForScreenId(screenId)); } @@ -2760,7 +2762,7 @@ public class Workspace extends PagedView animationStyle, finalView, true); } else { // This is for other drag/drop cases, like dragging from All Apps - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); View view; diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index ff653d7ea7..0ccb8ad91a 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -32,7 +32,7 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import android.view.animation.DecelerateInterpolator; -import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; +import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.anim.AnimationLayerSet; /** diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 583492e5a3..2b3a113f37 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -1,5 +1,7 @@ package com.android.launcher3.accessibility; +import static com.android.launcher3.LauncherState.NORMAL; + import android.app.AlertDialog; import android.appwidget.AppWidgetProviderInfo; import android.content.DialogInterface; @@ -27,6 +29,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetHostView; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; @@ -161,14 +164,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } else if (action == ADD_TO_WORKSPACE) { final int[] coordinates = new int[2]; final long screenId = findSpaceOnWorkspace(item, coordinates); - mLauncher.showWorkspace(true, new Runnable() { + mLauncher.getStateManager().goToState(NORMAL, true, new Runnable() { @Override public void run() { if (item instanceof AppInfo) { ShortcutInfo info = ((AppInfo) item).makeShortcut(); mLauncher.getModelWriter().addItemToDatabase(info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, + Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); ArrayList itemList = new ArrayList<>(); @@ -178,7 +181,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme PendingAddItemInfo info = (PendingAddItemInfo) item; Workspace workspace = mLauncher.getWorkspace(); workspace.snapToPage(workspace.getPageIndexForScreenId(screenId)); - mLauncher.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, + mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP, screenId, coordinates, info.spanX, info.spanY); } announceConfirmation(R.string.item_added_to_workspace); diff --git a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java index 29dd95c1a4..771353e77b 100644 --- a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java @@ -24,6 +24,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -55,7 +56,7 @@ public class OverviewAccessibilityDelegate extends AccessibilityDelegate { public boolean performAccessibilityAction(View host, int action, Bundle args) { Launcher launcher = Launcher.getLauncher(host.getContext()); if (action == OVERVIEW) { - launcher.showOverviewMode(true); + launcher.getStateManager().goToState(LauncherState.OVERVIEW); return true; } else if (action == WALLPAPERS) { launcher.onClickWallpaperPicker(host); diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java index 5b7353aa18..cfb0520293 100644 --- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java @@ -16,6 +16,8 @@ package com.android.launcher3.accessibility; +import static com.android.launcher3.LauncherState.NORMAL; + import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -79,9 +81,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele } }; - if (!mLauncher.showWorkspace(true, onComplete)) { - onComplete.run(); - } + mLauncher.getStateManager().goToState(NORMAL, true, onComplete); return true; } else if (action == DISMISS_NOTIFICATION) { if (!(host instanceof NotificationMainView)) { diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 35dfa8122b..d62cd4b5f4 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -1,5 +1,8 @@ package com.android.launcher3.allapps; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; + import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; @@ -19,7 +22,7 @@ import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherState; -import com.android.launcher3.LauncherStateTransitionAnimation.AnimationConfig; +import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; @@ -193,7 +196,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect @Override public void onDragStart(boolean start) { mCaretController.onDragStart(); - mLauncher.getStateTransition().cancelAnimation(); + mLauncher.getStateManager().cancelAnimation(); cancelDiscoveryAnimation(); mShiftStart = mAppsView.getTranslationY(); onProgressAnimationStart(); @@ -230,7 +233,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (!mLauncher.isInState(LauncherState.ALL_APPS)) { logSwipeOnContainer(Touch.FLING, Direction.UP, containerType); } - mLauncher.showAppsView(true /* animated */); + mLauncher.getStateManager().goToState(ALL_APPS); if (hasSpringAnimationHandler()) { mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */); // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) @@ -241,7 +244,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (mLauncher.isInState(LauncherState.ALL_APPS)) { logSwipeOnContainer(Touch.FLING, Direction.DOWN, ContainerType.ALLAPPS); } - mLauncher.showWorkspace(true /* animated */); + mLauncher.getStateManager().goToState(NORMAL); } // snap to top or bottom using the release velocity } else { @@ -250,13 +253,13 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (mLauncher.isInState(LauncherState.ALL_APPS)) { logSwipeOnContainer(Touch.SWIPE, Direction.DOWN, ContainerType.ALLAPPS); } - mLauncher.showWorkspace(true /* animated */); + mLauncher.getStateManager().goToState(NORMAL); } else { calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); if (!mLauncher.isInState(LauncherState.ALL_APPS)) { logSwipeOnContainer(Touch.SWIPE, Direction.UP, containerType); } - mLauncher.showAppsView(true /* animated */); + mLauncher.getStateManager().goToState(ALL_APPS); } } } diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 10bd67de8e..94023833ce 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -17,6 +17,7 @@ package com.android.launcher3.dragndrop; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherState.NORMAL; import android.content.ComponentName; import android.content.res.Resources; @@ -263,7 +264,7 @@ public class DragController implements DragDriver.EventListener, TouchController if (!accepted) { // If it was not accepted, cleanup the state. If it was accepted, it is the // responsibility of the drop target to cleanup the state. - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); mDragObject.deferDragViewCleanupPostAnimation = false; } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index fcc4f0efc3..2168001ca3 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -17,6 +17,7 @@ package com.android.launcher3.folder; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherState.NORMAL; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -1243,7 +1244,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter()); } - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_DELAY); + mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); if (d.stateAnnouncer != null) { d.stateAnnouncer.completeAction(R.string.item_moved); } diff --git a/src/com/android/launcher3/states/AllAppsState.java b/src/com/android/launcher3/states/AllAppsState.java index 9922d999cd..ee35b4d7c3 100644 --- a/src/com/android/launcher3/states/AllAppsState.java +++ b/src/com/android/launcher3/states/AllAppsState.java @@ -19,8 +19,10 @@ import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; import android.view.View; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.R; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; /** @@ -41,6 +43,14 @@ public class AllAppsState extends LauncherState { if (!launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)) { launcher.getSharedPrefs().edit().putBoolean(APPS_VIEW_SHOWN, true).apply(); } + + AbstractFloatingView.closeAllOpenViews(launcher); + dispatchWindowStateChanged(launcher); + } + + @Override + public String getDescription(Launcher launcher) { + return launcher.getString(R.string.all_apps_button_label); } @Override diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java index ec62764781..fe0571b8ad 100644 --- a/src/com/android/launcher3/util/FlingAnimation.java +++ b/src/com/android/launcher3/util/FlingAnimation.java @@ -1,6 +1,6 @@ package com.android.launcher3.util; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME; +import static com.android.launcher3.LauncherState.NORMAL; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; @@ -97,7 +97,7 @@ public class FlingAnimation implements AnimatorUpdateListener, Runnable { Runnable onAnimationEndRunnable = new Runnable() { @Override public void run() { - mLauncher.exitSpringLoadedDragMode(SPRING_LOADED_EXIT_NEXT_FRAME); + mLauncher.getStateManager().goToState(NORMAL); mDropTarget.completeDrop(mDragObject); } }; From ea60926c3f7d04bab6afd4cd559c54452a7013c0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 25 Oct 2017 15:47:38 -0700 Subject: [PATCH 065/885] Moving mState from Workspace to StateManager Bug: 67678570 Change-Id: I8cdab044d0760fcc4c188830cde4f963d072b907 --- src/com/android/launcher3/Launcher.java | 12 +++--- .../launcher3/LauncherStateManager.java | 39 +++++++++++++------ src/com/android/launcher3/Workspace.java | 36 +++++++---------- .../WorkspaceStateTransitionAnimation.java | 8 ++-- .../allapps/AllAppsTransitionController.java | 16 ++++---- 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index cd9fffc565..2593c49405 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -772,7 +772,7 @@ public class Launcher extends BaseActivity if (!mAppLaunchSuccess) { getUserEventDispatcher().logActionCommand(Action.Command.STOP, - mWorkspace.getState().containerType); + mStateManager.getState().containerType); } NotificationListener.removeNotificationsChangedListener(); } @@ -1014,7 +1014,7 @@ public class Launcher extends BaseActivity } public boolean isInState(LauncherState state) { - return mWorkspace.getState() == state; + return mStateManager.getState() == state; } /** @@ -1426,7 +1426,7 @@ public class Launcher extends BaseActivity if (topOpenView != null) { topOpenView.logActionCommand(Action.Command.HOME_INTENT); } else if (alreadyOnHome) { - Target target = newContainerTarget(mWorkspace.getState().containerType); + Target target = newContainerTarget(mStateManager.getState().containerType); target.pageIndex = mWorkspace.getCurrentPage(); ued.logActionCommand(Action.Command.HOME_INTENT, target); } @@ -1489,7 +1489,7 @@ public class Launcher extends BaseActivity outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage()); } - outState.putInt(RUNTIME_STATE, mWorkspace.getState().ordinal); + outState.putInt(RUNTIME_STATE, mStateManager.getState().ordinal); AbstractFloatingView widgets = AbstractFloatingView @@ -1884,7 +1884,7 @@ public class Launcher extends BaseActivity if (topView != null) { topView.onBackPressed(); } else if (!isInState(NORMAL)) { - ued.logActionCommand(Action.Command.BACK, mWorkspace.getState().containerType); + ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType); mStateManager.goToState(NORMAL); } else { // Back button is a no-op here, but give at least some feedback for the button press @@ -2440,7 +2440,7 @@ public class Launcher extends BaseActivity // TODO: When can workspace be null? text.add(mWorkspace == null ? getString(R.string.all_apps_home_button_label) - : mWorkspace.getState().getDescription(this)); + : mStateManager.getState().getDescription(this)); return result; } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 4298174aa1..863cae7329 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherState.NORMAL; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -77,6 +79,8 @@ public class LauncherStateManager { private final Launcher mLauncher; private final AllAppsTransitionController mAllAppsController; + private LauncherState mState = NORMAL; + public LauncherStateManager( Launcher l, AllAppsTransitionController allAppsController) { mUiHandler = new Handler(Looper.getMainLooper()); @@ -84,6 +88,10 @@ public class LauncherStateManager { mAllAppsController = allAppsController; } + public LauncherState getState() { + return mState; + } + /** * @see #goToState(LauncherState, boolean, Runnable) */ @@ -139,6 +147,7 @@ public class LauncherStateManager { mConfig.reset(); if (!animated) { + setState(state); mAllAppsController.setFinalProgress(state.verticalProgress); mLauncher.getWorkspace().setState(state); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); @@ -161,9 +170,10 @@ public class LauncherStateManager { } } - protected AnimatorSet createAnimationToNewWorkspace(LauncherState state, + protected AnimatorSet createAnimationToNewWorkspace(final LauncherState state, final Runnable onCompleteRunnable) { mConfig.reset(); + mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration; final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final AnimationLayerSet layerViews = new AnimationLayerSet(); @@ -175,6 +185,13 @@ public class LauncherStateManager { animation.addListener(layerViews); animation.addListener(new AnimationSuccessListener() { + + @Override + public void onAnimationStart(Animator animation) { + // Change the internal state only when the transition actually starts + setState(state); + } + @Override public void onAnimationSuccess(Animator animator) { // Run any queued runnables @@ -189,6 +206,12 @@ public class LauncherStateManager { return mConfig.mCurrentAnimation; } + private void setState(LauncherState state) { + mState.onStateDisabled(mLauncher); + mState = state; + mState.onStateEnabled(mLauncher); + } + /** * Cancels the current animation. */ @@ -220,13 +243,13 @@ public class LauncherStateManager { public static class AnimationConfig extends AnimatorListenerAdapter { public boolean shouldPost; + public long duration; - private long mOverriddenDuration = -1; private AnimatorSet mCurrentAnimation; public void reset() { - shouldPost = false; - mOverriddenDuration = -1; + shouldPost = true; + duration = 0; if (mCurrentAnimation != null) { mCurrentAnimation.setDuration(0); @@ -235,14 +258,6 @@ public class LauncherStateManager { } } - public void overrideDuration(long duration) { - mOverriddenDuration = duration; - } - - public long getDuration(long defaultDuration) { - return mOverriddenDuration >= 0 ? mOverriddenDuration : defaultDuration; - } - @Override public void onAnimationEnd(Animator animation) { if (mCurrentAnimation == animation) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 900e5bfa94..71e8c5560a 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -206,8 +206,6 @@ public class Workspace extends PagedView */ private final float[] mHotseatAlpha = new float[] {1, 1, 1}; - @ViewDebug.ExportedProperty(category = "launcher") - private LauncherState mState = NORMAL; private boolean mIsSwitchingState = false; boolean mChildrenLayersEnabled = true; @@ -576,7 +574,8 @@ public class Workspace extends PagedView mWorkspaceScreens.put(screenId, newScreen); mScreenOrder.add(insertIndex, screenId); addView(newScreen, insertIndex); - mStateTransitionAnimation.applyChildState(mState, newScreen, insertIndex); + mStateTransitionAnimation.applyChildState( + mLauncher.getStateManager().getState(), newScreen, insertIndex); if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) { newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); @@ -1411,13 +1410,13 @@ public class Workspace extends PagedView return super.getDescendantFocusability(); } - public boolean workspaceInModalState() { - return mState != NORMAL; + private boolean workspaceInModalState() { + return !mLauncher.isInState(NORMAL); } /** Returns whether a drag should be allowed to be started from the current workspace state. */ public boolean workspaceIconsCanBeDragged() { - return mState == NORMAL || mState == SPRING_LOADED; + return mLauncher.isInState(NORMAL) || mLauncher.isInState(SPRING_LOADED); } private void updateChildrenLayersEnabled() { @@ -1545,11 +1544,6 @@ public class Workspace extends PagedView } private void onStartStateTransition(LauncherState state) { - // Change the internal state only when the transition actually starts - mState.onStateDisabled(mLauncher); - mState = state; - mState.onStateEnabled(mLauncher); - mIsSwitchingState = true; mTransitionProgress = 0; @@ -1570,7 +1564,7 @@ public class Workspace extends PagedView */ public void setState(LauncherState toState) { onStartStateTransition(toState); - mStateTransitionAnimation.setState(mState); + mStateTransitionAnimation.setState(toState); onEndStateTransition(); } @@ -1580,7 +1574,7 @@ public class Workspace extends PagedView public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, AnimatorSet anim, AnimationConfig config) { StateTransitionListener listener = new StateTransitionListener(toState); - mStateTransitionAnimation.setStateWithAnimation(mState, toState, anim, layerViews, config); + mStateTransitionAnimation.setStateWithAnimation(toState, anim, layerViews, config); // Invalidate the pages now, so that we have the visible pages before the // animation is started @@ -1595,22 +1589,19 @@ public class Workspace extends PagedView anim.addListener(listener); } - public LauncherState getState() { - return mState; - } - public void updateAccessibilityFlags() { // TODO: Update the accessibility flags appropriately when dragging. + int accessibilityFlag = mLauncher.getStateManager().getState().workspaceAccessibilityFlag; if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) { int total = getPageCount(); for (int i = 0; i < total; i++) { - updateAccessibilityFlags((CellLayout) getPageAt(i), i); + updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i), i); } - setImportantForAccessibility(mState.workspaceAccessibilityFlag); + setImportantForAccessibility(accessibilityFlag); } } - private void updateAccessibilityFlags(CellLayout page, int pageNo) { + private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page, int pageNo) { if (isPageRearrangeEnabled()) { page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); page.getShortcutsAndWidgets().setImportantForAccessibility( @@ -1626,8 +1617,7 @@ public class Workspace extends PagedView } } else { page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - page.getShortcutsAndWidgets() - .setImportantForAccessibility(mState.workspaceAccessibilityFlag); + page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag); page.setContentDescription(null); page.setAccessibilityDelegate(null); } @@ -2687,7 +2677,7 @@ public class Workspace extends PagedView final long screenId = getIdForScreen(cellLayout); if (!mLauncher.isHotseatLayout(cellLayout) && screenId != getScreenIdForPageIndex(mCurrentPage) - && mState != SPRING_LOADED) { + && !mLauncher.isInState(SPRING_LOADED)) { snapToPage(getPageIndexForScreenId(screenId)); } diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 0ccb8ad91a..9ae0fe4baf 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -161,12 +161,10 @@ public class WorkspaceStateTransitionAnimation { setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER); } - public void setStateWithAnimation(LauncherState fromState, LauncherState toState, - AnimatorSet anim, AnimationLayerSet layerViews, AnimationConfig config) { - long duration = config.getDuration(toState == LauncherState.NORMAL - ? fromState.transitionDuration : toState.transitionDuration); + public void setStateWithAnimation(LauncherState toState, AnimatorSet anim, + AnimationLayerSet layerViews, AnimationConfig config) { AnimatedPropertySetter propertySetter = - new AnimatedPropertySetter(duration, layerViews, anim); + new AnimatedPropertySetter(config.duration, layerViews, anim); setWorkspaceProperty(toState, propertySetter); } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index d62cd4b5f4..7de11bb9f6 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -133,9 +133,9 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = false; mTouchEventStartedOnHotseat = mLauncher.getDragLayer().isEventOverHotseat(ev); - if (!mLauncher.isInState(LauncherState.ALL_APPS) && mLauncher.getWorkspace().workspaceInModalState()) { + if (!mLauncher.isInState(ALL_APPS) && !mLauncher.isInState(NORMAL)) { mNoIntercept = true; - } else if (mLauncher.isInState(LauncherState.ALL_APPS) && + } else if (mLauncher.isInState(ALL_APPS) && !mAppsView.shouldContainerScroll(ev)) { mNoIntercept = true; } else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { @@ -147,7 +147,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect boolean ignoreSlopWhenSettling = false; if (mDetector.isIdleState()) { - if (mLauncher.isInState(LauncherState.ALL_APPS)) { + if (mLauncher.isInState(ALL_APPS)) { directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE; } else { directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE; @@ -230,7 +230,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect if (fling) { if (velocity < 0) { calculateDuration(velocity, mAppsView.getTranslationY()); - if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + if (!mLauncher.isInState(ALL_APPS)) { logSwipeOnContainer(Touch.FLING, Direction.UP, containerType); } mLauncher.getStateManager().goToState(ALL_APPS); @@ -241,7 +241,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } } else { calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); - if (mLauncher.isInState(LauncherState.ALL_APPS)) { + if (mLauncher.isInState(ALL_APPS)) { logSwipeOnContainer(Touch.FLING, Direction.DOWN, ContainerType.ALLAPPS); } mLauncher.getStateManager().goToState(NORMAL); @@ -250,13 +250,13 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } else { if (mAppsView.getTranslationY() > mShiftRange / 2) { calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); - if (mLauncher.isInState(LauncherState.ALL_APPS)) { + if (mLauncher.isInState(ALL_APPS)) { logSwipeOnContainer(Touch.SWIPE, Direction.DOWN, ContainerType.ALLAPPS); } mLauncher.getStateManager().goToState(NORMAL); } else { calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); - if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + if (!mLauncher.isInState(ALL_APPS)) { logSwipeOnContainer(Touch.SWIPE, Direction.UP, containerType); } mLauncher.getStateManager().goToState(ALL_APPS); @@ -403,7 +403,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect outConfig.shouldPost = false; } - outConfig.overrideDuration(mAnimationDuration); + outConfig.duration = mAnimationDuration; ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, progress); anim.setDuration(mAnimationDuration); anim.setInterpolator(interpolator); From b871c13ee3391d69a8df10448c4cc414b51dc185 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 24 Oct 2017 12:01:39 -0700 Subject: [PATCH 066/885] Adding a compat implementation for playback control on AnimatorSet Change-Id: I9f01fc319341cf2499fffb59521d32c2c81eba45 --- .../launcher3/PinchToOverviewListener.java | 43 +--- .../launcher3/compat/AnimatorSetCompat.java | 204 ++++++++++++++++++ .../launcher3/dragndrop/DragLayer.java | 2 +- 3 files changed, 212 insertions(+), 37 deletions(-) create mode 100644 src/com/android/launcher3/compat/AnimatorSetCompat.java diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index fc75fe3f44..d1a253893a 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -16,30 +16,22 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import android.animation.Animator; -import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.annotation.TargetApi; -import android.os.Build; -import android.util.Range; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; +import com.android.launcher3.compat.AnimatorSetCompat; import com.android.launcher3.util.TouchController; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** * Detects pinches and animates the Workspace to/from overview mode. */ -@TargetApi(Build.VERSION_CODES.O) public class PinchToOverviewListener implements TouchController, OnScaleGestureListener, Runnable { @@ -55,9 +47,8 @@ public class PinchToOverviewListener private Workspace mWorkspace = null; private boolean mPinchStarted = false; - private AnimatorSet mCurrentAnimation; + private AnimatorSetCompat mCurrentAnimation; private float mCurrentScale; - private Range mDurationRange; private boolean mShouldGoToFinalState; private LauncherState mToState; @@ -109,14 +100,13 @@ public class PinchToOverviewListener } mToState = mLauncher.isInState(OVERVIEW) ? NORMAL : OVERVIEW; - mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(mToState, this); + mCurrentAnimation = AnimatorSetCompat.wrap(mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, this), OVERVIEW_TRANSITION_MS); mPinchStarted = true; mCurrentScale = 1; - mDurationRange = Range.create(0, LauncherAnimUtils.OVERVIEW_TRANSITION_MS); mShouldGoToFinalState = false; - dispatchOnStart(mCurrentAnimation); + mCurrentAnimation.dispatchOnStart(); return true; } @@ -160,26 +150,7 @@ public class PinchToOverviewListener } // Move the transition animation to that duration. - long playPosition = mDurationRange.clamp( - (int) ((1 - animationFraction) * mDurationRange.getUpper())); - mCurrentAnimation.setCurrentPlayTime(playPosition); - + mCurrentAnimation.setPlayFraction(1 - animationFraction); return true; } - - private void dispatchOnStart(Animator animator) { - for (AnimatorListener l : nonNullList(animator.getListeners())) { - l.onAnimationStart(animator); - } - - if (animator instanceof AnimatorSet) { - for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) { - dispatchOnStart(anim); - } - } - } - - private static List nonNullList(ArrayList list) { - return list == null ? Collections.emptyList() : list; - } } \ No newline at end of file diff --git a/src/com/android/launcher3/compat/AnimatorSetCompat.java b/src/com/android/launcher3/compat/AnimatorSetCompat.java new file mode 100644 index 0000000000..497dd14973 --- /dev/null +++ b/src/com/android/launcher3/compat/AnimatorSetCompat.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.compat; + +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.os.Build; +import android.view.animation.LinearInterpolator; + +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Compat implementation for various new APIs in {@link AnimatorSet} + * + * Note: The compat implementation does not support start delays on child animations or + * sequential playbacks. + */ +public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateListener { + + public static AnimatorSetCompat wrap(AnimatorSet anim, int duration) { + if (Utilities.ATLEAST_OREO) { + return new AnimatorSetCompatVO(anim, duration); + } else { + return new AnimatorSetCompatVL(anim, duration); + } + } + + private final ValueAnimator mAnimationPlayer; + private final long mDuration; + + protected final AnimatorSet mAnim; + + protected float mCurrentFraction; + + protected AnimatorSetCompat(AnimatorSet anim, int duration) { + mAnim = anim; + mDuration = duration; + + mAnimationPlayer = ValueAnimator.ofFloat(0, 1); + mAnimationPlayer.setInterpolator(new LinearInterpolator()); + mAnimationPlayer.addUpdateListener(this); + } + + /** + * Starts playing the animation forward from current position. + */ + public void start() { + mAnimationPlayer.setFloatValues(mCurrentFraction, 1); + mAnimationPlayer.setDuration(clampDuration(1 - mCurrentFraction)); + mAnimationPlayer.addListener(new OnAnimationEndDispatcher()); + mAnimationPlayer.start(); + } + + /** + * Starts playing the animation backwards from current position + */ + public void reverse() { + mAnimationPlayer.setFloatValues(mCurrentFraction, 0); + mAnimationPlayer.setDuration(clampDuration(mCurrentFraction)); + mAnimationPlayer.addListener(new OnAnimationEndDispatcher()); + mAnimationPlayer.start(); + } + + /** + * Sets the current animation position and updates all the child animators accordingly. + */ + public abstract void setPlayFraction(float fraction); + + /** + * @see Animator#addListener(AnimatorListener) + */ + public void addListener(Animator.AnimatorListener listener) { + mAnimationPlayer.addListener(listener); + } + + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + setPlayFraction((float) valueAnimator.getAnimatedValue()); + } + + protected long clampDuration(float fraction) { + float playPos = mDuration * fraction; + if (playPos <= 0) { + return 0; + } else { + return Math.min((long) playPos, mDuration); + } + } + + public void dispatchOnStart() { + dispatchOnStartRecursively(mAnim); + } + + private void dispatchOnStartRecursively(Animator animator) { + for (AnimatorListener l : nonNullList(animator.getListeners())) { + l.onAnimationStart(animator); + } + + if (animator instanceof AnimatorSet) { + for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) { + dispatchOnStartRecursively(anim); + } + } + } + + public static class AnimatorSetCompatVL extends AnimatorSetCompat { + + private final ValueAnimator[] mChildAnimations; + + private AnimatorSetCompatVL(AnimatorSet anim, int duration) { + super(anim, duration); + + // Build animation list + ArrayList childAnims = new ArrayList<>(); + getAnimationsRecur(mAnim, childAnims); + mChildAnimations = childAnims.toArray(new ValueAnimator[childAnims.size()]); + } + + private void getAnimationsRecur(AnimatorSet anim, ArrayList out) { + long forceDuration = anim.getDuration(); + for (Animator child : anim.getChildAnimations()) { + if (forceDuration > 0) { + child.setDuration(forceDuration); + } + if (child instanceof ValueAnimator) { + out.add((ValueAnimator) child); + } else if (child instanceof AnimatorSet) { + getAnimationsRecur((AnimatorSet) child, out); + } else { + throw new RuntimeException("Unknown animation type " + child); + } + } + } + + @Override + public void setPlayFraction(float fraction) { + mCurrentFraction = fraction; + long playPos = clampDuration(fraction); + for (ValueAnimator anim : mChildAnimations) { + anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration())); + } + } + + } + + @TargetApi(Build.VERSION_CODES.O) + private static class AnimatorSetCompatVO extends AnimatorSetCompat { + + private AnimatorSetCompatVO(AnimatorSet anim, int duration) { + super(anim, duration); + } + + @Override + public void setPlayFraction(float fraction) { + mCurrentFraction = fraction; + mAnim.setCurrentPlayTime(clampDuration(fraction)); + } + } + + private class OnAnimationEndDispatcher extends AnimationSuccessListener { + + @Override + public void onAnimationSuccess(Animator animator) { + dispatchOnEndRecursively(mAnim); + } + + private void dispatchOnEndRecursively(Animator animator) { + for (AnimatorListener l : nonNullList(animator.getListeners())) { + l.onAnimationEnd(animator); + } + + if (animator instanceof AnimatorSet) { + for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) { + dispatchOnEndRecursively(anim); + } + } + } + } + + private static List nonNullList(ArrayList list) { + return list == null ? Collections.emptyList() : list; + } +} diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index f2bad6b624..bc5aafc0a9 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -143,7 +143,7 @@ public class DragLayer extends InsettableFrameLayout { public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) { mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled - || !Utilities.ATLEAST_OREO ? null : new PinchToOverviewListener(mLauncher); + ? null : new PinchToOverviewListener(mLauncher); } public boolean isEventOverHotseat(MotionEvent ev) { From 5254944a686311d94897de7f023e966532faf8c9 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Thu, 26 Oct 2017 11:28:06 -0700 Subject: [PATCH 067/885] Fix bug where recycled BubbleTextView kept their badges. Bug: 68324671 Change-Id: Id5f4db6706afd002242d95d44422fc61042798c8 --- src/com/android/launcher3/BubbleTextView.java | 9 +++++++++ .../android/launcher3/allapps/AllAppsGridAdapter.java | 1 + 2 files changed, 10 insertions(+) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index ac842f92e6..f20baea039 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -177,6 +177,15 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver { } + /** + * Resets the view so it can be recycled. + */ + public void reset() { + mBadgeInfo = null; + mBadgePalette = null; + mForceHideBadge = false; + } + public void applyFromShortcutInfo(ShortcutInfo info) { applyFromShortcutInfo(info, false); } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 1f60fcc735..48e6caa23e 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -336,6 +336,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter Date: Thu, 26 Oct 2017 15:36:10 -0700 Subject: [PATCH 068/885] Consolidating various interpolators Change-Id: I9588eee3552001b162a1e8d5ccefcfb44d221880 --- build.gradle | 2 +- .../android/launcher3/ButtonDropTarget.java | 6 +- src/com/android/launcher3/CellLayout.java | 6 +- src/com/android/launcher3/DropTargetBar.java | 4 +- src/com/android/launcher3/PagedView.java | 13 +-- src/com/android/launcher3/Workspace.java | 11 +-- .../WorkspaceStateTransitionAnimation.java | 60 ++----------- .../allapps/AllAppsCaretController.java | 7 +- .../allapps/AllAppsTransitionController.java | 15 ++-- .../android/launcher3/anim/Interpolators.java | 90 +++++++++++++++++++ .../launcher3/compat/AnimatorSetCompat.java | 4 +- .../launcher3/dragndrop/DragLayer.java | 4 +- .../android/launcher3/dragndrop/DragView.java | 4 +- .../android/launcher3/folder/FolderIcon.java | 11 ++- .../launcher3/folder/FolderPagedView.java | 4 +- .../launcher3/graphics/GradientView.java | 4 +- .../graphics/PreloadIconDrawable.java | 5 +- .../launcher3/notification/Interpolators.java | 37 -------- .../notification/NotificationMainView.java | 8 +- .../launcher3/touch/SwipeDetector.java | 21 +---- .../util/WallpaperOffsetInterpolator.java | 4 +- .../launcher3/widget/BaseWidgetSheet.java | 15 ++-- .../launcher3/widget/WidgetsBottomSheet.java | 8 +- 23 files changed, 153 insertions(+), 190 deletions(-) create mode 100644 src/com/android/launcher3/anim/Interpolators.java delete mode 100644 src/com/android/launcher3/notification/Interpolators.java diff --git a/build.gradle b/build.gradle index 2376146fd0..a1b3a6037a 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0' } } diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 6f404086b6..d8c4efaa4e 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -37,10 +37,9 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.LinearInterpolator; import android.widget.TextView; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; @@ -240,8 +239,7 @@ public abstract class ButtonDropTarget extends TextView }; dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, DRAG_VIEW_DROP_DURATION, - new DecelerateInterpolator(2), - new LinearInterpolator(), onAnimationEndRunnable, + Interpolators.DEACCEL_2, Interpolators.LINEAR, onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 2ab08b2eb4..3162286c1c 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -44,12 +44,13 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.DecelerateInterpolator; + import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.PreviewBackground; @@ -59,6 +60,7 @@ import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.ParcelableSparseArray; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -223,7 +225,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx); // Initialize the data structures used for the drag visualization. - mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out + mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out mDragCell[0] = mDragCell[1] = -1; for (int i = 0; i < mDragOutlines.length; i++) { mDragOutlines[i] = new Rect(-1, -1, -1, -1); diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index 2f8374a5e5..3eca5cd44a 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -26,9 +26,9 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; -import android.view.animation.AccelerateInterpolator; import android.widget.LinearLayout; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; @@ -40,7 +40,7 @@ import java.util.ArrayList; public class DropTargetBar extends LinearLayout implements DragController.DragListener { protected static final int DEFAULT_DRAG_FADE_DURATION = 175; - protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(); + protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL; private final Runnable mFadeAnimationEndRunnable = new Runnable() { diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index f55455b7ef..4240a30b4f 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -46,6 +46,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.touch.OverScroll; @@ -214,7 +215,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc */ protected void init() { mScroller = new LauncherScroller(getContext()); - setDefaultInterpolator(new ScrollInterpolator()); + setDefaultInterpolator(Interpolators.SCROLL); mCurrentPage = 0; final ViewConfiguration configuration = ViewConfiguration.get(getContext()); @@ -1766,16 +1767,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return PAGE_SNAP_ANIMATION_DURATION; } - public static class ScrollInterpolator implements Interpolator { - public ScrollInterpolator() { - } - - public float getInterpolation(float t) { - t -= 1.0f; - return t*t*t*t*t + 1; - } - } - // We want the duration of the page snap animation to be influenced by the distance that // 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 diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 900e5bfa94..b0a33c8bd0 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -16,8 +16,8 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; @@ -56,8 +56,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; import android.widget.Toast; import com.android.launcher3.Launcher.LauncherOverlay; @@ -68,6 +66,7 @@ import com.android.launcher3.accessibility.OverviewAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.AnimationLayerSet; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.badge.FolderBadgeInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.config.FeatureFlags; @@ -1163,8 +1162,6 @@ public class Workspace extends PagedView super.shouldFlingForVelocity(velocityX); } - private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f); - /** * The overlay scroll is being controlled locally, just update our overlay effect */ @@ -1189,7 +1186,7 @@ public class Workspace extends PagedView scroll = Math.max(scroll - offset, 0); scroll = Math.min(1, scroll / (1 - offset)); - float alpha = 1 - mAlphaInterpolator.getInterpolation(scroll); + float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll); float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll; transX *= 1 - slip; @@ -1541,7 +1538,7 @@ public class Workspace extends PagedView } public void snapToPageFromOverView(int whichPage) { - snapToPage(whichPage, OVERVIEW_TRANSITION_MS, new ZoomInInterpolator()); + snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN); } private void onStartStateTransition(LauncherState state) { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 0ccb8ad91a..fcccdf9853 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -30,10 +30,10 @@ import android.content.res.Resources; import android.util.Property; import android.view.View; import android.view.accessibility.AccessibilityManager; -import android.view.animation.DecelerateInterpolator; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.anim.AnimationLayerSet; +import com.android.launcher3.anim.Interpolators; /** * A convenience class to update a view's visibility state after an alpha animation. @@ -85,50 +85,6 @@ class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimat } } -/** - * 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. */ @@ -136,8 +92,6 @@ public class WorkspaceStateTransitionAnimation { private static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); - private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); - public final int mWorkspaceScrimAlpha; private final Launcher mLauncher; @@ -197,13 +151,13 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setViewAlpha(mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha), mLauncher.getHotseat(), finalHotseatAlpha); - propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, mZoomInInterpolator); + propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN); propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y, - finalWorkspaceTranslationY, mZoomInInterpolator); + finalWorkspaceTranslationY, Interpolators.ZOOM_IN); // Set scrim propertySetter.setInt(mLauncher.getDragLayer().getScrim(), DRAWABLE_ALPHA, - state.hasScrim ? mWorkspaceScrimAlpha : 0, new DecelerateInterpolator(1.5f)); + state.hasScrim ? mWorkspaceScrimAlpha : 0, Interpolators.DEACCEL_1_5); } public void applyChildState(LauncherState state, CellLayout cl, int childIndex) { @@ -214,13 +168,13 @@ public class WorkspaceStateTransitionAnimation { private void applyChildState(LauncherState state, CellLayout cl, int childIndex, int centerPage, PropertySetter propertySetter) { propertySetter.setInt(cl.getScrimBackground(), - DRAWABLE_ALPHA, state.hasScrim ? 255 : 0, mZoomInInterpolator); + DRAWABLE_ALPHA, state.hasScrim ? 255 : 0, Interpolators.ZOOM_IN); // Only animate the page alpha when we actually fade pages if (mWorkspaceFadeInAdjacentScreens) { float finalAlpha = state == LauncherState.NORMAL && childIndex != centerPage ? 0 : 1f; propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, - finalAlpha, mZoomInInterpolator); + finalAlpha, Interpolators.ZOOM_IN); } } @@ -302,7 +256,7 @@ public class WorkspaceStateTransitionAnimation { } private TimeInterpolator getFadeInterpolator(float finalAlpha) { - return finalAlpha == 0 ? new DecelerateInterpolator(2) : null; + return finalAlpha == 0 ? Interpolators.DEACCEL_2 : null; } } } \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java index 583b49f7bc..81d005a707 100644 --- a/src/com/android/launcher3/allapps/AllAppsCaretController.java +++ b/src/com/android/launcher3/allapps/AllAppsCaretController.java @@ -16,11 +16,10 @@ package com.android.launcher3.allapps; import android.animation.ObjectAnimator; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.pageindicators.CaretDrawable; import com.android.launcher3.touch.SwipeDetector; @@ -44,13 +43,11 @@ public class AllAppsCaretController { final long caretAnimationDuration = launcher.getResources().getInteger( R.integer.config_caretAnimationDuration); - final Interpolator caretInterpolator = AnimationUtils.loadInterpolator(launcher, - android.R.interpolator.fast_out_slow_in); // We will set values later mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0); mCaretAnimator.setDuration(caretAnimationDuration); - mCaretAnimator.setInterpolator(caretInterpolator); + mCaretAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); } /** diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index d62cd4b5f4..31ce013388 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -2,6 +2,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import android.animation.Animator; import android.animation.AnimatorInflater; @@ -10,11 +11,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.support.animation.SpringAnimation; -import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.util.Property; import android.view.MotionEvent; import android.view.View; -import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; @@ -27,6 +26,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GradientView; @@ -70,11 +70,9 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect // The delay (as a % of the animation duration) to start the springs. private static final float SPRING_DELAY = 0.3f; - private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f); - private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f); - private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator(); - private final SwipeDetector.ScrollInterpolator mScrollInterpolator - = new SwipeDetector.ScrollInterpolator(); + private final Interpolator mWorkspaceAccelnterpolator = Interpolators.ACCEL_2; + private final Interpolator mHotseatAccelInterpolator = Interpolators.ACCEL_1_5; + private final Interpolator mFastOutSlowInInterpolator = Interpolators.FAST_OUT_SLOW_IN; private static final float PARALLAX_COEFFICIENT = .125f; private static final int SINGLE_FRAME_MS = 16; @@ -396,8 +394,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mShiftStart = mAppsView.getTranslationY(); interpolator = mFastOutSlowInInterpolator; } else { - mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity)); - interpolator = mScrollInterpolator; + interpolator = scrollInterpolatorForVelocity(mContainerVelocity); mProgress = Utilities.boundToRange( mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange, 0f, 1f); outConfig.shouldPost = false; diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java new file mode 100644 index 0000000000..8826e64d8e --- /dev/null +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.anim; + +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.PathInterpolator; + + +/** + * Common interpolators used in Launcher + */ +public class Interpolators { + + public static final Interpolator LINEAR = new LinearInterpolator(); + + public static final Interpolator ACCEL = new AccelerateInterpolator(); + public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f); + public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2); + + public static final Interpolator DEACCEL = new DecelerateInterpolator(); + public static final Interpolator DEACCEL_1_5 = new DecelerateInterpolator(1.5f); + public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2); + public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f); + public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f); + + public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + + /** + * Inversion of zInterpolate, compounded with an ease-out. + */ + public static final Interpolator ZOOM_IN = new Interpolator() { + + private static final float FOCAL_LENGTH = 0.35f; + + @Override + public float getInterpolation(float v) { + return DEACCEL_3.getInterpolation(1 - zInterpolate(1 - v)); + } + + /** + * 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. + */ + private float zInterpolate(float input) { + return (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + input)) / + (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + 1.0f)); + } + }; + + public static final Interpolator SCROLL = new Interpolator() { + @Override + public float getInterpolation(float t) { + t -= 1.0f; + return t*t*t*t*t + 1; + } + }; + + public static final Interpolator SCROLL_CUBIC = new Interpolator() { + @Override + public float getInterpolation(float t) { + t -= 1.0f; + return t*t*t + 1; + } + }; + + private static final float FAST_FLING_PX_MS = 10; + + public static Interpolator scrollInterpolatorForVelocity(float velocity) { + return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC; + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/compat/AnimatorSetCompat.java b/src/com/android/launcher3/compat/AnimatorSetCompat.java index 497dd14973..6676725930 100644 --- a/src/com/android/launcher3/compat/AnimatorSetCompat.java +++ b/src/com/android/launcher3/compat/AnimatorSetCompat.java @@ -21,10 +21,10 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.os.Build; -import android.view.animation.LinearInterpolator; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.Interpolators; import java.util.ArrayList; import java.util.Collections; @@ -58,7 +58,7 @@ public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateL mDuration = duration; mAnimationPlayer = ValueAnimator.ofFloat(0, 1); - mAnimationPlayer.setInterpolator(new LinearInterpolator()); + mAnimationPlayer.setInterpolator(Interpolators.LINEAR); mAnimationPlayer.addUpdateListener(this); } diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index bc5aafc0a9..5b1a4dc6cf 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -33,7 +33,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.TextView; @@ -48,6 +47,7 @@ import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; @@ -74,7 +74,7 @@ public class DragLayer extends InsettableFrameLayout { // Variables relating to animation of views after drop private ValueAnimator mDropAnim = null; - private final TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); + private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5; @Thunk DragView mDropView = null; @Thunk int mAnchorViewInitialScrollX = 0; @Thunk View mAnchorView = null; diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 17fad84968..7c89df37c0 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -43,7 +43,6 @@ import android.support.animation.FloatPropertyCompat; import android.support.animation.SpringAnimation; import android.support.animation.SpringForce; import android.view.View; -import android.view.animation.DecelerateInterpolator; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.ItemInfo; @@ -54,6 +53,7 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; import com.android.launcher3.config.FeatureFlags; @@ -472,7 +472,7 @@ public class DragView extends View { public void crossFade(int duration) { ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f); va.setDuration(duration); - va.setInterpolator(new DecelerateInterpolator(1.5f)); + va.setInterpolator(Interpolators.DEACCEL_1_5); va.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 900781cbb3..5983029749 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -16,6 +16,9 @@ package com.android.launcher3.folder; +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; +import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; @@ -33,8 +36,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import com.android.launcher3.Alarm; @@ -56,6 +57,7 @@ import com.android.launcher3.SimpleOnStylusPressListener; import com.android.launcher3.StylusEventHelper; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.badge.FolderBadgeInfo; import com.android.launcher3.dragndrop.BaseItemDragListener; @@ -68,9 +70,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; import java.util.List; -import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; -import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION; - /** * An icon that can appear on in the workspace representing an {@link Folder}. */ @@ -350,7 +349,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { float finalScale = scale * scaleRelativeToDragLayer; dragLayer.animateView(animateView, from, to, finalAlpha, 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, - new DecelerateInterpolator(2), new AccelerateInterpolator(2), + Interpolators.DEACCEL_2, Interpolators.ACCEL_2, null, DragLayer.ANIMATION_END_DISAPPEAR, null); mFolder.hideItem(item); diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index f4ac0a1404..9e5bc4f093 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -27,7 +27,6 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewDebug; -import android.view.animation.DecelerateInterpolator; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; @@ -43,6 +42,7 @@ import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.util.Thunk; @@ -511,7 +511,7 @@ public class FolderPagedView extends PagedView { int scroll = getScrollForPage(getNextPage()) + hint; int delta = scroll - getScrollX(); if (delta != 0) { - mScroller.setInterpolator(new DecelerateInterpolator()); + mScroller.setInterpolator(Interpolators.DEACCEL); mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION); invalidate(); } diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java index 5455b43ec6..bacb06323d 100644 --- a/src/com/android/launcher3/graphics/GradientView.java +++ b/src/com/android/launcher3/graphics/GradientView.java @@ -29,12 +29,12 @@ import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; -import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dynamicui.WallpaperColorInfo; import com.android.launcher3.util.Themes; @@ -64,7 +64,7 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private final int mMaskHeight, mMaskWidth; private final int mAlphaColors; private final Paint mDebugPaint = DEBUG ? new Paint() : null; - private final Interpolator mAccelerator = new AccelerateInterpolator(); + private final Interpolator mAccelerator = Interpolators.ACCEL; private final float mAlphaStart; private final WallpaperColorInfo mWallpaperColorInfo; private final int mScrimColor; diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index 06dc7acfe0..6d486eed72 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -30,9 +30,9 @@ import android.graphics.PathMeasure; import android.graphics.Rect; import android.util.Property; import android.util.SparseArray; -import android.view.animation.LinearInterpolator; import com.android.launcher3.FastBitmapDrawable; +import com.android.launcher3.anim.Interpolators; import java.lang.ref.WeakReference; @@ -226,7 +226,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable { mCurrentAnim = ObjectAnimator.ofFloat(this, INTERNAL_STATE, finalProgress); mCurrentAnim.setDuration( (long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE)); - mCurrentAnim.setInterpolator(new LinearInterpolator()); + mCurrentAnim.setInterpolator(Interpolators.LINEAR); if (isFinish) { mCurrentAnim.addListener(new AnimatorListenerAdapter() { @Override @@ -237,7 +237,6 @@ public class PreloadIconDrawable extends FastBitmapDrawable { } mCurrentAnim.start(); } - } /** diff --git a/src/com/android/launcher3/notification/Interpolators.java b/src/com/android/launcher3/notification/Interpolators.java deleted file mode 100644 index 5c3b22ab11..0000000000 --- a/src/com/android/launcher3/notification/Interpolators.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.notification; - -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; - -/** - * Utility class to receive interpolators from. - * - * This class was copied from com.android.systemui. - */ -public class Interpolators { - public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); - public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); - public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); - - /** - * Interpolator to be used when animating a move based on a click. Pair with enough duration. - */ - public static final Interpolator TOUCH_RESPONSE = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); -} diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index 5aff28db4c..c8e1fdb3f5 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -16,6 +16,8 @@ package com.android.launcher3.notification; +import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; + import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.ColorStateList; @@ -25,7 +27,6 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; import android.widget.TextView; @@ -167,14 +168,11 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L endTranslation = 0; } - SwipeDetector.ScrollInterpolator interpolator = new SwipeDetector.ScrollInterpolator(); - interpolator.setVelocityAtZero(velocity); - long duration = SwipeDetector.calculateDuration(velocity, (endTranslation - getTranslationX()) / getWidth()); animate() .setDuration(duration) - .setInterpolator(interpolator) + .setInterpolator(scrollInterpolatorForVelocity(velocity)) .translationX(endTranslation) .withEndAction(new Runnable() { @Override diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java index be4648eef3..351f88dddb 100644 --- a/src/com/android/launcher3/touch/SwipeDetector.java +++ b/src/com/android/launcher3/touch/SwipeDetector.java @@ -16,6 +16,7 @@ package com.android.launcher3.touch; import static android.view.MotionEvent.INVALID_POINTER_ID; + import android.content.Context; import android.graphics.PointF; import android.support.annotation.NonNull; @@ -23,7 +24,6 @@ import android.support.annotation.VisibleForTesting; import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; -import android.view.animation.Interpolator; /** * One dimensional scroll/drag/swipe gesture detector. @@ -43,7 +43,6 @@ public class SwipeDetector { public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE; private static final float ANIMATION_DURATION = 1200; - private static final float FAST_FLING_PX_MS = 10; protected int mActivePointerId = INVALID_POINTER_ID; @@ -351,22 +350,4 @@ public class SwipeDetector { } return duration; } - - public static class ScrollInterpolator implements Interpolator { - - boolean mSteeper; - - public void setVelocityAtZero(float velocity) { - mSteeper = velocity > FAST_FLING_PX_MS; - } - - public float getInterpolation(float t) { - t -= 1.0f; - float output = t * t * t; - if (mSteeper) { - output *= t * t; // Make interpolation initial slope steeper - } - return output + 1; - } - } } diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java index ec494f1818..5c24687404 100644 --- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java +++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java @@ -10,11 +10,11 @@ import android.os.IBinder; import android.os.Message; import android.os.SystemClock; import android.util.Log; -import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.anim.Interpolators; /** * Utility class to handle wallpaper scrolling along with workspace. @@ -199,7 +199,7 @@ public class WallpaperOffsetInterpolator extends BroadcastReceiver { public OffsetHandler(Context context) { super(UiThreadHelper.getBackgroundLooper()); - mInterpolator = new DecelerateInterpolator(1.5f); + mInterpolator = Interpolators.DEACCEL_1_5; mWM = WallpaperManager.getInstance(context); } diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 61a733338f..e328759265 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.widget; +import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import android.animation.Animator; @@ -29,8 +30,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; import android.widget.Toast; import com.android.launcher3.AbstractFloatingView; @@ -41,6 +41,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.touch.SwipeDetector; @@ -76,12 +77,12 @@ abstract class BaseWidgetSheet extends AbstractFloatingView private Toast mWidgetInstructionToast; protected final Launcher mLauncher; - protected final SwipeDetector.ScrollInterpolator mScrollInterpolator; protected final SwipeDetector mSwipeDetector; protected final ObjectAnimator mOpenCloseAnimator; protected View mContent; protected GradientView mGradientView; + protected Interpolator mScrollInterpolator; // range [0, 1], 0=> completely open, 1=> completely closed protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED; @@ -92,7 +93,7 @@ abstract class BaseWidgetSheet extends AbstractFloatingView super(context, attrs, defStyleAttr); mLauncher = Launcher.getLauncher(context); - mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); + mScrollInterpolator = Interpolators.SCROLL_CUBIC; mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this); @@ -206,7 +207,7 @@ abstract class BaseWidgetSheet extends AbstractFloatingView @Override public void onDragEnd(float velocity, boolean fling) { if ((fling && velocity > 0) || mTranslationShift > 0.5f) { - mScrollInterpolator.setVelocityAtZero(velocity); + mScrollInterpolator = scrollInterpolatorForVelocity(velocity); mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration( velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift)); close(true); @@ -215,7 +216,7 @@ abstract class BaseWidgetSheet extends AbstractFloatingView TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); mOpenCloseAnimator.setDuration( SwipeDetector.calculateDuration(velocity, mTranslationShift)) - .setInterpolator(new DecelerateInterpolator()); + .setInterpolator(Interpolators.DEACCEL); mOpenCloseAnimator.start(); } } @@ -236,7 +237,7 @@ abstract class BaseWidgetSheet extends AbstractFloatingView if (mSwipeDetector.isIdleState()) { mOpenCloseAnimator .setDuration(defaultDuration) - .setInterpolator(new AccelerateInterpolator()); + .setInterpolator(Interpolators.ACCEL); } else { mOpenCloseAnimator.setInterpolator(mScrollInterpolator); } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 3bb3fcb981..7fa5ff054b 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -24,8 +24,6 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import android.widget.TextView; import com.android.launcher3.Insettable; @@ -33,6 +31,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.PackageUserKey; @@ -46,7 +45,6 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { private static final int DEFAULT_CLOSE_DURATION = 200; private ItemInfo mOriginalItemInfo; - private Interpolator mFastOutSlowInInterpolator; private Rect mInsets; public WidgetsBottomSheet(Context context, AttributeSet attrs) { @@ -56,8 +54,6 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setWillNotDraw(false); - mFastOutSlowInInterpolator = - AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); mInsets = new Rect(); mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate( @@ -148,7 +144,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { if (animate) { mOpenCloseAnimator.setValues( PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator); + mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mOpenCloseAnimator.start(); } else { setTranslationShift(TRANSLATION_SHIFT_OPENED); From 4d49f9a8ac86ccaf5f6f3d80dd8694310b620364 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 26 Oct 2017 16:26:13 -0700 Subject: [PATCH 069/885] Removing animated caret drawable > Both landscape and portrait UI will show a static drag handle > In landscape the handle is drawn below the all-apps scrim, while in portrait it moves with the hotseat Change-Id: Ia6964c6f98feb8ba90517e23667d8c68bc2f6d35 --- res/drawable/all_apps_handle_landscape.xml | 37 +++++ res/drawable/all_apps_handle_portrait.xml | 34 ++++ res/layout-land/launcher.xml | 20 ++- res/layout/page_indicator.xml | 5 +- res/values/dimens.xml | 3 - .../allapps/AllAppsCaretController.java | 120 -------------- .../allapps/AllAppsTransitionController.java | 6 - .../pageindicators/CaretDrawable.java | 152 ------------------ .../pageindicators/PageIndicator.java | 23 --- ...scape.java => PageIndicatorLandscape.java} | 27 +--- ...rLineCaret.java => PageIndicatorLine.java} | 36 ++--- 11 files changed, 108 insertions(+), 355 deletions(-) create mode 100644 res/drawable/all_apps_handle_landscape.xml create mode 100644 res/drawable/all_apps_handle_portrait.xml delete mode 100644 src/com/android/launcher3/allapps/AllAppsCaretController.java delete mode 100644 src/com/android/launcher3/pageindicators/CaretDrawable.java rename src/com/android/launcher3/pageindicators/{PageIndicatorCaretLandscape.java => PageIndicatorLandscape.java} (56%) rename src/com/android/launcher3/pageindicators/{PageIndicatorLineCaret.java => PageIndicatorLine.java} (84%) diff --git a/res/drawable/all_apps_handle_landscape.xml b/res/drawable/all_apps_handle_landscape.xml new file mode 100644 index 0000000000..23826ab06d --- /dev/null +++ b/res/drawable/all_apps_handle_landscape.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/res/drawable/all_apps_handle_portrait.xml b/res/drawable/all_apps_handle_portrait.xml new file mode 100644 index 0000000000..75aa44853b --- /dev/null +++ b/res/drawable/all_apps_handle_portrait.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index 0c7999ec78..4ea32b46f4 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -42,6 +42,19 @@ android:layout_gravity="center" launcher:pageIndicator="@id/page_indicator" /> + + + + @@ -66,13 +79,6 @@ android:layout_height="match_parent" android:visibility="invisible" /> - - diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml index 92f52d672c..2df511b573 100644 --- a/res/layout/page_indicator.xml +++ b/res/layout/page_indicator.xml @@ -14,7 +14,7 @@ limitations under the License. --> - - + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 481199e73e..eb12dc8210 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -86,9 +86,6 @@ 144dp 700dp 475dp - 2dp - 1dp - 13dp 18dp diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java deleted file mode 100644 index 81d005a707..0000000000 --- a/src/com/android/launcher3/allapps/AllAppsCaretController.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.launcher3.Launcher; -import com.android.launcher3.R; -import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.pageindicators.CaretDrawable; -import com.android.launcher3.touch.SwipeDetector; - -public class AllAppsCaretController { - // Determines when the caret should flip. Should be accessed via getThreshold() - private static final float CARET_THRESHOLD = 0.015f; - private static final float CARET_THRESHOLD_LAND = 0.5f; - // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend) - private static final float PEAK_VELOCITY = SwipeDetector.RELEASE_VELOCITY_PX_MS * .7f; - - private Launcher mLauncher; - - private ObjectAnimator mCaretAnimator; - private CaretDrawable mCaretDrawable; - private float mLastCaretProgress; - private boolean mThresholdCrossed; - - public AllAppsCaretController(CaretDrawable caret, Launcher launcher) { - mLauncher = launcher; - mCaretDrawable = caret; - - final long caretAnimationDuration = launcher.getResources().getInteger( - R.integer.config_caretAnimationDuration); - - // We will set values later - mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0); - mCaretAnimator.setDuration(caretAnimationDuration); - mCaretAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - } - - /** - * Updates the state of the caret based on the progress of the {@link AllAppsContainerView}, as - * defined by the {@link AllAppsTransitionController}. Uses the container's velocity to adjust - * angle of caret. - * - * @param containerProgress The progress of the container in the range [0..1] - * @param velocity The velocity of the container - * @param dragging {@code true} if the container is being dragged - */ - public void updateCaret(float containerProgress, float velocity, boolean dragging) { - // If we're in portrait and the shift is not 0 or 1, adjust the caret based on velocity - if (getThreshold() < containerProgress && containerProgress < 1 - getThreshold() && - !mLauncher.useVerticalBarLayout()) { - mThresholdCrossed = true; - - // How fast are we moving as a percentage of the peak velocity? - final float pctOfFlingVelocity = Math.max(-1, Math.min(velocity / PEAK_VELOCITY, 1)); - - mCaretDrawable.setCaretProgress(pctOfFlingVelocity); - - // Set the last caret progress to this progress to prevent animator cancellation - mLastCaretProgress = pctOfFlingVelocity; - // Animate to neutral. This is necessary so the caret doesn't "freeze" when the - // container stops moving (e.g., during a drag or when the threshold is reached). - animateCaretToProgress(CaretDrawable.PROGRESS_CARET_NEUTRAL); - } else if (!dragging) { - // Otherwise, if we're not dragging, match the caret to the appropriate state - if (containerProgress <= getThreshold()) { // All Apps is up - animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_DOWN); - } else if (containerProgress >= 1 - getThreshold()) { // All Apps is down - animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_UP); - } - } - } - - private void animateCaretToProgress(float progress) { - // If the new progress is the same as the last progress we animated to, terminate early - if (Float.compare(mLastCaretProgress, progress) == 0) { - return; - } - - if (mCaretAnimator.isRunning()) { - mCaretAnimator.cancel(); // Stop the animator in its tracks - } - - // Update the progress and start the animation - mLastCaretProgress = progress; - mCaretAnimator.setFloatValues(progress); - mCaretAnimator.start(); - } - - private float getThreshold() { - // In landscape, just return the landscape threshold. - if (mLauncher.useVerticalBarLayout()) { - return CARET_THRESHOLD_LAND; - } - - // Before the threshold is crossed, it is reported as zero. This makes the caret immediately - // responsive when a drag begins. After the threshold is crossed, we return the constant - // value. This means the caret will start its state-based adjustment sooner. That is, we - // won't have to wait until the panel is completely settled to begin animation. - return mThresholdCrossed ? CARET_THRESHOLD : 0f; - } - - public void onDragStart() { - mThresholdCrossed = false; - } -} diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 080f4dcca6..a84172ddf5 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -81,8 +81,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect private Workspace mWorkspace; private Hotseat mHotseat; - private AllAppsCaretController mCaretController; - private final Launcher mLauncher; private final SwipeDetector mDetector; private final boolean mIsDarkTheme; @@ -193,7 +191,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect @Override public void onDragStart(boolean start) { - mCaretController.onDragStart(); mLauncher.getStateManager().cancelAnimation(); cancelDiscoveryAnimation(); mShiftStart = mAppsView.getTranslationY(); @@ -349,7 +346,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect System.currentTimeMillis()); } - mCaretController.updateCaret(progress, mContainerVelocity, mDetector.isDraggingState()); updateLightStatusBar(shiftCurrent); } @@ -479,8 +475,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mHotseat = hotseat; mWorkspace = workspace; mHotseat.bringToFront(); - mCaretController = new AllAppsCaretController( - mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher); mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this); mSpringAnimationHandler = mAppsView.getSpringAnimationHandler(); mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling(); diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java deleted file mode 100644 index 5ade497280..0000000000 --- a/src/com/android/launcher3/pageindicators/CaretDrawable.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2016 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.pageindicators; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.drawable.Drawable; - -import com.android.launcher3.R; -import com.android.launcher3.util.Themes; - -public class CaretDrawable extends Drawable { - public static final float PROGRESS_CARET_POINTING_UP = -1f; - public static final float PROGRESS_CARET_POINTING_DOWN = 1f; - public static final float PROGRESS_CARET_NEUTRAL = 0; - - private float mCaretProgress = PROGRESS_CARET_NEUTRAL; - - private Paint mShadowPaint = new Paint(); - private Paint mCaretPaint = new Paint(); - private Path mPath = new Path(); - private final int mCaretSizePx; - private final boolean mUseShadow; - - public CaretDrawable(Context context) { - final Resources res = context.getResources(); - - final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width); - final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread); - - mCaretPaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor)); - mCaretPaint.setAntiAlias(true); - mCaretPaint.setStrokeWidth(strokeWidth); - mCaretPaint.setStyle(Paint.Style.STROKE); - mCaretPaint.setStrokeCap(Paint.Cap.ROUND); - mCaretPaint.setStrokeJoin(Paint.Join.ROUND); - - mShadowPaint.setColor(res.getColor(R.color.default_shadow_color_no_alpha)); - mShadowPaint.setAlpha(Themes.getAlpha(context, android.R.attr.spotShadowAlpha)); - mShadowPaint.setAntiAlias(true); - mShadowPaint.setStrokeWidth(strokeWidth + (shadowSpread * 2)); - mShadowPaint.setStyle(Paint.Style.STROKE); - mShadowPaint.setStrokeCap(Paint.Cap.ROUND); - mShadowPaint.setStrokeJoin(Paint.Join.ROUND); - - mUseShadow = !Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText); - mCaretSizePx = res.getDimensionPixelSize(R.dimen.all_apps_caret_size); - } - - @Override - public int getIntrinsicHeight() { - return mCaretSizePx; - } - - @Override - public int getIntrinsicWidth() { - return mCaretSizePx; - } - - @Override - public void draw(Canvas canvas) { - // Assumes caret paint is more important than shadow paint - if (Float.compare(mCaretPaint.getAlpha(), 0f) == 0) { - return; - } - - // Assumes shadow stroke width is larger - final float width = getBounds().width() - mShadowPaint.getStrokeWidth(); - final float height = getBounds().height() - mShadowPaint.getStrokeWidth(); - final float left = getBounds().left + (mShadowPaint.getStrokeWidth() / 2); - final float top = getBounds().top + (mShadowPaint.getStrokeWidth() / 2); - - // When the bounds are square, this will result in a caret with a right angle - final float verticalInset = (height / 4); - final float caretHeight = (height - (verticalInset * 2)); - - mPath.reset(); - mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress())); - mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress()); - mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress())); - if (mUseShadow) { - canvas.drawPath(mPath, mShadowPaint); - } - canvas.drawPath(mPath, mCaretPaint); - } - - /** - * Sets the caret progress - * - * @param progress The progress ({@value #PROGRESS_CARET_POINTING_UP} for pointing up, - * {@value #PROGRESS_CARET_POINTING_DOWN} for pointing down, {@value #PROGRESS_CARET_NEUTRAL} - * for neutral) - */ - public void setCaretProgress(float progress) { - mCaretProgress = progress; - invalidateSelf(); - } - - /** - * Returns the caret progress - * - * @return The progress - */ - public float getCaretProgress() { - return mCaretProgress; - } - - /** - * Returns the caret progress normalized to [0..1] - * - * @return The normalized progress - */ - public float getNormalizedCaretProgress() { - return (mCaretProgress - PROGRESS_CARET_POINTING_UP) / - (PROGRESS_CARET_POINTING_DOWN - PROGRESS_CARET_POINTING_UP); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { - mCaretPaint.setAlpha(alpha); - mShadowPaint.setAlpha(alpha); - invalidateSelf(); - } - - @Override - public void setColorFilter(ColorFilter cf) { - // no-op - } -} diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java index d6ef5b4c8d..be6bcc5825 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicator.java +++ b/src/com/android/launcher3/pageindicators/PageIndicator.java @@ -16,7 +16,6 @@ package com.android.launcher3.pageindicators; import android.content.Context; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.FrameLayout; @@ -24,7 +23,6 @@ import android.widget.FrameLayout; * Base class for a page indicator. */ public abstract class PageIndicator extends FrameLayout { - private CaretDrawable mCaretDrawable; protected int mNumPages = 1; @@ -52,28 +50,7 @@ public abstract class PageIndicator extends FrameLayout { onPageCountChanged(); } - public CaretDrawable getCaretDrawable() { - return mCaretDrawable; - } - - public void setCaretDrawable(CaretDrawable caretDrawable) { - if (mCaretDrawable != null) { - mCaretDrawable.setCallback(null); - } - - mCaretDrawable = caretDrawable; - - if (mCaretDrawable != null) { - mCaretDrawable.setCallback(this); - } - } - protected void onPageCountChanged() {} public void setShouldAutoHide(boolean shouldAutoHide) {} - - @Override - protected boolean verifyDrawable(Drawable who) { - return super.verifyDrawable(who) || who == getCaretDrawable(); - } } diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java similarity index 56% rename from src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java rename to src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java index 911be93fcd..7325235db4 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java @@ -16,49 +16,30 @@ package com.android.launcher3.pageindicators; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Rect; import android.util.AttributeSet; import com.android.launcher3.Launcher; -import com.android.launcher3.R; /** * Simply draws the caret drawable bottom-right aligned in the view. This ensures that we can have * a view with as large an area as we want (for touching) while maintaining a caret of size * all_apps_caret_size. Used only for the landscape layout. */ -public class PageIndicatorCaretLandscape extends PageIndicator { +public class PageIndicatorLandscape extends PageIndicator { // all apps pull up handle drawable. - public PageIndicatorCaretLandscape(Context context) { + public PageIndicatorLandscape(Context context) { this(context, null); } - public PageIndicatorCaretLandscape(Context context, AttributeSet attrs) { + public PageIndicatorLandscape(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public PageIndicatorCaretLandscape(Context context, AttributeSet attrs, int defStyle) { + public PageIndicatorLandscape(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - - int caretSize = context.getResources().getDimensionPixelSize(R.dimen.all_apps_caret_size); - CaretDrawable caretDrawable = new CaretDrawable(context); - caretDrawable.setBounds(0, 0, caretSize, caretSize); - setCaretDrawable(caretDrawable); - Launcher l = Launcher.getLauncher(context); setOnClickListener(l); setOnFocusChangeListener(l.mFocusHandler); } - - @Override - protected void onDraw(Canvas canvas) { - Rect drawableBounds = getCaretDrawable().getBounds(); - int count = canvas.save(); - canvas.translate((getWidth() - drawableBounds.width()) / 2, - getHeight() - drawableBounds.height()); - getCaretDrawable().draw(canvas); - canvas.restoreToCount(count); - } } diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java similarity index 84% rename from src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java rename to src/com/android/launcher3/pageindicators/PageIndicatorLine.java index 5eedd92fd0..09a06b0da6 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java @@ -26,7 +26,7 @@ import com.android.launcher3.dynamicui.WallpaperColorInfo; * * The fraction is 1 / number of pages and the position is based on the progress of the page scroll. */ -public class PageIndicatorLineCaret extends PageIndicator { +public class PageIndicatorLine extends PageIndicator { private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration(); private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay(); @@ -56,43 +56,43 @@ public class PageIndicatorLineCaret extends PageIndicator { private final int mLineHeight; private ImageView mAllAppsHandle; - private static final Property PAINT_ALPHA - = new Property(Integer.class, "paint_alpha") { + private static final Property PAINT_ALPHA + = new Property(Integer.class, "paint_alpha") { @Override - public Integer get(PageIndicatorLineCaret obj) { + public Integer get(PageIndicatorLine obj) { return obj.mLinePaint.getAlpha(); } @Override - public void set(PageIndicatorLineCaret obj, Integer alpha) { + public void set(PageIndicatorLine obj, Integer alpha) { obj.mLinePaint.setAlpha(alpha); obj.invalidate(); } }; - private static final Property NUM_PAGES - = new Property(Float.class, "num_pages") { + private static final Property NUM_PAGES + = new Property(Float.class, "num_pages") { @Override - public Float get(PageIndicatorLineCaret obj) { + public Float get(PageIndicatorLine obj) { return obj.mNumPagesFloat; } @Override - public void set(PageIndicatorLineCaret obj, Float numPages) { + public void set(PageIndicatorLine obj, Float numPages) { obj.mNumPagesFloat = numPages; obj.invalidate(); } }; - private static final Property TOTAL_SCROLL - = new Property(Integer.class, "total_scroll") { + private static final Property TOTAL_SCROLL + = new Property(Integer.class, "total_scroll") { @Override - public Integer get(PageIndicatorLineCaret obj) { + public Integer get(PageIndicatorLine obj) { return obj.mTotalScroll; } @Override - public void set(PageIndicatorLineCaret obj, Integer totalScroll) { + public void set(PageIndicatorLine obj, Integer totalScroll) { obj.mTotalScroll = totalScroll; obj.invalidate(); } @@ -105,15 +105,15 @@ public class PageIndicatorLineCaret extends PageIndicator { } }; - public PageIndicatorLineCaret(Context context) { + public PageIndicatorLine(Context context) { this(context, null); } - public PageIndicatorLineCaret(Context context, AttributeSet attrs) { + public PageIndicatorLine(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) { + public PageIndicatorLine(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); Resources res = context.getResources(); @@ -122,7 +122,6 @@ public class PageIndicatorLineCaret extends PageIndicator { mLauncher = Launcher.getLauncher(context); mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height); - setCaretDrawable(new CaretDrawable(context)); boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText(); mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA; @@ -132,8 +131,7 @@ public class PageIndicatorLineCaret extends PageIndicator { @Override protected void onFinishInflate() { super.onFinishInflate(); - mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle); - mAllAppsHandle.setImageDrawable(getCaretDrawable()); + mAllAppsHandle = findViewById(R.id.all_apps_handle); mAllAppsHandle.setOnClickListener(mLauncher); mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler); mLauncher.setAllAppsButton(mAllAppsHandle); From 0ab09fcf7b8471ae83f84d2e8a63207a4ea8a9b2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 30 Oct 2017 09:26:11 -0700 Subject: [PATCH 070/885] Deleting sample code demonstrating how to extend Launcher3 Launcher already includes Launcher3Go build flavour and we will be adding another build flavour for RecentsUI. There is no need to maintain another build which is not used anywhere. Change-Id: I9287f62691d57750460ccc9d6859c7fa11c99956 --- AndroidManifest.xml | 48 ---- res/layout/zzz_weight_watcher.xml | 4 - src/com/android/launcher3/Launcher.java | 5 - .../android/launcher3/LauncherAppState.java | 6 - .../launcher3/testing/LauncherExtension.java | 128 --------- .../launcher3/testing/MemoryDumpActivity.java | 183 ------------ .../launcher3/testing/MemoryTracker.java | 217 -------------- .../testing/ToggleWeightWatcher.java | 31 -- .../launcher3/testing/WeightWatcher.java | 267 ------------------ .../android/launcher3/util/TestingUtils.java | 51 ---- 10 files changed, 940 deletions(-) delete mode 100644 res/layout/zzz_weight_watcher.xml delete mode 100644 src/com/android/launcher3/testing/LauncherExtension.java delete mode 100644 src/com/android/launcher3/testing/MemoryDumpActivity.java delete mode 100644 src/com/android/launcher3/testing/MemoryTracker.java delete mode 100644 src/com/android/launcher3/testing/ToggleWeightWatcher.java delete mode 100644 src/com/android/launcher3/testing/WeightWatcher.java delete mode 100644 src/com/android/launcher3/util/TestingUtils.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 23bddf62f4..6ef78285f2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -112,53 +112,5 @@ android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS" android:readPermission="com.android.launcher3.permission.READ_SETTINGS" /> - - diff --git a/res/layout/zzz_weight_watcher.xml b/res/layout/zzz_weight_watcher.xml deleted file mode 100644 index 07fd39e91f..0000000000 --- a/res/layout/zzz_weight_watcher.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2593c49405..6b5ee5eafb 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -133,7 +133,6 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.util.RunnableWithId; import com.android.launcher3.util.SystemUiController; -import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TraceHelper; @@ -1095,10 +1094,6 @@ public class Launcher extends BaseActivity mDropTargetBar.setup(mDragController); mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace); - - if (TestingUtils.MEMORY_DUMP_ENABLED) { - TestingUtils.addWeightWatcher(this); - } } private void setupOverviewPanel() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index dfb30fd35c..0136c7087f 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -27,12 +27,10 @@ import android.util.Log; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.ConfigMonitor; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SettingsObserver; -import com.android.launcher3.util.TestingUtils; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -88,10 +86,6 @@ public class LauncherAppState { Preconditions.assertUIThread(); mContext = context; - if (TestingUtils.MEMORY_DUMP_ENABLED) { - TestingUtils.startTrackingMemory(mContext); - } - mInvariantDeviceProfile = new InvariantDeviceProfile(mContext); mIconCache = new IconCache(mContext, mInvariantDeviceProfile); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java deleted file mode 100644 index c40e1fbe4f..0000000000 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.android.launcher3.testing; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; - -import com.android.launcher3.AppInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherCallbacks; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; - -/** - * This class represents a very trivial LauncherExtension. It primarily serves as a simple - * class to exercise the LauncherOverlay interface. - */ -public class LauncherExtension extends Launcher { - - //------ Activity methods -------// - @Override - public void onCreate(Bundle savedInstanceState) { - setLauncherCallbacks(new LauncherExtensionCallbacks()); - super.onCreate(savedInstanceState); - } - - public class LauncherExtensionCallbacks implements LauncherCallbacks { - - @Override - public void preOnCreate() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - } - - @Override - public void onResume() { - } - - @Override - public void onStart() { - } - - @Override - public void onStop() { - } - - @Override - public void onPause() { - } - - @Override - public void onDestroy() { - } - - @Override - public void onSaveInstanceState(Bundle outState) { - } - - @Override - public void onNewIntent(Intent intent) { - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - } - - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, - int[] grantResults) { - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - return false; - } - - @Override - public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args) { - } - - @Override - public void onHomeIntent() { - } - - @Override - public boolean handleBackPressed() { - return false; - } - - @Override - public void onTrimMemory(int level) { - } - - @Override - public void onLauncherProviderChange() { - } - - @Override - public void bindAllApplications(ArrayList apps) { - } - - @Override - public boolean startSearch(String initialQuery, boolean selectInitialQuery, - Bundle appSearchData) { - return false; - } - - @Override - public boolean hasSettings() { - return false; - } - - @Override - public void onAttachedToWindow() { - } - - @Override - public void onDetachedFromWindow() { - } - } -} diff --git a/src/com/android/launcher3/testing/MemoryDumpActivity.java b/src/com/android/launcher3/testing/MemoryDumpActivity.java deleted file mode 100644 index 9bcf92b1b2..0000000000 --- a/src/com/android/launcher3/testing/MemoryDumpActivity.java +++ /dev/null @@ -1,183 +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.testing; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.IBinder; -import android.util.Log; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -public class MemoryDumpActivity extends Activity { - private static final String TAG = "MemoryDumpActivity"; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - public static String zipUp(ArrayList paths) { - final int BUFSIZ = 256 * 1024; // 256K - final byte[] buf = new byte[BUFSIZ]; - final String zipfilePath = String.format("%s/hprof-%d.zip", - Environment.getExternalStorageDirectory(), - System.currentTimeMillis()); - ZipOutputStream zos = null; - try { - OutputStream os = new FileOutputStream(zipfilePath); - zos = new ZipOutputStream(new BufferedOutputStream(os)); - for (String filename : paths) { - InputStream is = null; - try { - is = new BufferedInputStream(new FileInputStream(filename)); - ZipEntry entry = new ZipEntry(filename); - zos.putNextEntry(entry); - int len; - while ( 0 < (len = is.read(buf, 0, BUFSIZ)) ) { - zos.write(buf, 0, len); - } - zos.closeEntry(); - } finally { - is.close(); - } - } - } catch (IOException e) { - Log.e(TAG, "error zipping up profile data", e); - return null; - } finally { - if (zos != null) { - try { - zos.close(); - } catch (IOException e) { - // ugh, whatever - } - } - } - return zipfilePath; - } - - public static void dumpHprofAndShare(final Context context, MemoryTracker tracker) { - final StringBuilder body = new StringBuilder(); - - final ArrayList paths = new ArrayList(); - final int myPid = android.os.Process.myPid(); - - final int[] pids_orig = tracker.getTrackedProcesses(); - final int[] pids_copy = Arrays.copyOf(pids_orig, pids_orig.length); - for (int pid : pids_copy) { - MemoryTracker.ProcessMemInfo info = tracker.getMemInfo(pid); - if (info != null) { - body.append("pid ").append(pid).append(":") - .append(" up=").append(info.getUptime()) - .append(" pss=").append(info.currentPss) - .append(" uss=").append(info.currentUss) - .append("\n"); - } - if (pid == myPid) { - final String path = String.format("%s/launcher-memory-%d.ahprof", - Environment.getExternalStorageDirectory(), - pid); - Log.v(TAG, "Dumping memory info for process " + pid + " to " + path); - try { - android.os.Debug.dumpHprofData(path); // will block - } catch (IOException e) { - Log.e(TAG, "error dumping memory:", e); - } - paths.add(path); - } - } - - String zipfile = zipUp(paths); - - if (zipfile == null) return; - - Intent shareIntent = new Intent(Intent.ACTION_SEND); - shareIntent.setType("application/zip"); - - final PackageManager pm = context.getPackageManager(); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("Launcher memory dump (%d)", myPid)); - String appVersion; - try { - appVersion = pm.getPackageInfo(context.getPackageName(), 0).versionName; - } catch (PackageManager.NameNotFoundException e) { - appVersion = "?"; - } - - body.append("\nApp version: ").append(appVersion).append("\nBuild: ").append(Build.DISPLAY).append("\n"); - shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString()); - - final File pathFile = new File(zipfile); - final Uri pathUri = Uri.fromFile(pathFile); - - shareIntent.putExtra(Intent.EXTRA_STREAM, pathUri); - context.startActivity(shareIntent); - } - - @Override - public void onStart() { - super.onStart(); - - startDump(this, new Runnable() { - @Override - public void run() { - finish(); - } - }); - } - - public static void startDump(final Context context) { - startDump(context, null); - } - - public static void startDump(final Context context, final Runnable andThen) { - final ServiceConnection connection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.v(TAG, "service connected, dumping..."); - dumpHprofAndShare(context, - ((MemoryTracker.MemoryTrackerInterface) service).getService()); - context.unbindService(this); - if (andThen != null) andThen.run(); - } - - public void onServiceDisconnected(ComponentName className) { - } - }; - Log.v(TAG, "attempting to bind to memory tracker"); - context.bindService(new Intent(context, MemoryTracker.class), - connection, Context.BIND_AUTO_CREATE); - } -} diff --git a/src/com/android/launcher3/testing/MemoryTracker.java b/src/com/android/launcher3/testing/MemoryTracker.java deleted file mode 100644 index ed2a3122c0..0000000000 --- a/src/com/android/launcher3/testing/MemoryTracker.java +++ /dev/null @@ -1,217 +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.testing; - -import android.app.ActivityManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Binder; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.SystemClock; -import android.util.Log; -import android.util.LongSparseArray; - -import com.android.launcher3.util.TestingUtils; - -import java.util.ArrayList; -import java.util.List; - -public class MemoryTracker extends Service { - public static final String TAG = MemoryTracker.class.getSimpleName(); - - private static final long UPDATE_RATE = 5000; - - private static final int MSG_START = 1; - private static final int MSG_STOP = 2; - private static final int MSG_UPDATE = 3; - - public static class ProcessMemInfo { - public int pid; - public String name; - public long startTime; - public long currentPss, currentUss; - public long[] pss = new long[256]; - public long[] uss = new long[256]; - //= new Meminfo[(int) (30 * 60 / (UPDATE_RATE / 1000))]; // 30 minutes - public long max = 1; - public int head = 0; - public ProcessMemInfo(int pid, String name, long start) { - this.pid = pid; - this.name = name; - this.startTime = start; - } - public long getUptime() { - return System.currentTimeMillis() - startTime; - } - }; - public final LongSparseArray mData = new LongSparseArray(); - public final ArrayList mPids = new ArrayList(); - private int[] mPidsArray = new int[0]; - private final Object mLock = new Object(); - - Handler mHandler = new Handler() { - @Override - public void handleMessage(Message m) { - switch (m.what) { - case MSG_START: - mHandler.removeMessages(MSG_UPDATE); - mHandler.sendEmptyMessage(MSG_UPDATE); - break; - case MSG_STOP: - mHandler.removeMessages(MSG_UPDATE); - break; - case MSG_UPDATE: - update(); - mHandler.removeMessages(MSG_UPDATE); - mHandler.sendEmptyMessageDelayed(MSG_UPDATE, UPDATE_RATE); - break; - } - } - }; - - ActivityManager mAm; - - public ProcessMemInfo getMemInfo(int pid) { - return mData.get(pid); - } - - public int[] getTrackedProcesses() { - return mPidsArray; - } - - public void startTrackingProcess(int pid, String name, long start) { - synchronized (mLock) { - final Long lpid = Long.valueOf(pid); - - if (mPids.contains(lpid)) return; - - mPids.add(lpid); - updatePidsArrayL(); - - mData.put(pid, new ProcessMemInfo(pid, name, start)); - } - } - - void updatePidsArrayL() { - final int N = mPids.size(); - mPidsArray = new int[N]; - StringBuffer sb = new StringBuffer("Now tracking processes: "); - for (int i=0; i mPids.size()) { - Log.e(TAG, "update: unknown process info received: " + dinfo); - break; - } - final long pid = mPids.get(i).intValue(); - final ProcessMemInfo info = mData.get(pid); - info.head = (info.head+1) % info.pss.length; - info.pss[info.head] = info.currentPss = dinfo.getTotalPss(); - info.uss[info.head] = info.currentUss = dinfo.getTotalPrivateDirty(); - if (info.currentPss > info.max) info.max = info.currentPss; - if (info.currentUss > info.max) info.max = info.currentUss; - // Log.v(TAG, "update: pid " + pid + " pss=" + info.currentPss + " uss=" + info.currentUss); - if (info.currentPss == 0) { - Log.v(TAG, "update: pid " + pid + " has pss=0, it probably died"); - mData.remove(pid); - } - } - for (int i=mPids.size()-1; i>=0; i--) { - final long pid = mPids.get(i).intValue(); - if (mData.get(pid) == null) { - mPids.remove(i); - updatePidsArrayL(); - } - } - } - } - - @Override - public void onCreate() { - mAm = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - - // catch up in case we crashed but other processes are still running - List svcs = mAm.getRunningServices(256); - for (ActivityManager.RunningServiceInfo svc : svcs) { - if (svc.service.getPackageName().equals(getPackageName())) { - Log.v(TAG, "discovered running service: " + svc.process + " (" + svc.pid + ")"); - startTrackingProcess(svc.pid, svc.process, - System.currentTimeMillis() - (SystemClock.elapsedRealtime() - svc.activeSince)); - } - } - - List procs = mAm.getRunningAppProcesses(); - for (ActivityManager.RunningAppProcessInfo proc : procs) { - final String pname = proc.processName; - if (pname.startsWith(getPackageName())) { - Log.v(TAG, "discovered other running process: " + pname + " (" + proc.pid + ")"); - startTrackingProcess(proc.pid, pname, System.currentTimeMillis()); - } - } - } - - @Override - public void onDestroy() { - mHandler.sendEmptyMessage(MSG_STOP); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Log.v(TAG, "Received start id " + startId + ": " + intent); - - if (intent != null) { - if (TestingUtils.ACTION_START_TRACKING.equals(intent.getAction())) { - final int pid = intent.getIntExtra("pid", -1); - final String name = intent.getStringExtra("name"); - final long start = intent.getLongExtra("start", System.currentTimeMillis()); - startTrackingProcess(pid, name, start); - } - } - - mHandler.sendEmptyMessage(MSG_START); - - return START_STICKY; - } - - public class MemoryTrackerInterface extends Binder { - MemoryTracker getService() { - return MemoryTracker.this; - } - } - - private final IBinder mBinder = new MemoryTrackerInterface(); - - public IBinder onBind(Intent intent) { - mHandler.sendEmptyMessage(MSG_START); - - return mBinder; - } -} diff --git a/src/com/android/launcher3/testing/ToggleWeightWatcher.java b/src/com/android/launcher3/testing/ToggleWeightWatcher.java deleted file mode 100644 index f0c39202ba..0000000000 --- a/src/com/android/launcher3/testing/ToggleWeightWatcher.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.android.launcher3.testing; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.view.View; - -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.Utilities; -import com.android.launcher3.util.TestingUtils; - -public class ToggleWeightWatcher extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - SharedPreferences sp = Utilities.getPrefs(this); - boolean show = sp.getBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, true); - - show = !show; - sp.edit().putBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, show).apply(); - - Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback(); - if (launcher != null && launcher.mWeightWatcher != null) { - launcher.mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE); - } - finish(); - } -} diff --git a/src/com/android/launcher3/testing/WeightWatcher.java b/src/com/android/launcher3/testing/WeightWatcher.java deleted file mode 100644 index a26a2b6420..0000000000 --- a/src/com/android/launcher3/testing/WeightWatcher.java +++ /dev/null @@ -1,267 +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.testing; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.Gravity; -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; - private static final int TEXT_COLOR = 0xFFFFFFFF; - private static final int BACKGROUND_COLOR = 0xc0000000; - - private static final int UPDATE_RATE = 5000; - - private static final int MSG_START = 1; - private static final int MSG_STOP = 2; - private static final int MSG_UPDATE = 3; - - static int indexOf(int[] a, int x) { - for (int i=0; i 0) { - sec -= days * 86400; - sb.append(days); - sb.append("d"); - } - - long hours = sec / 3600; - if (hours > 0) { - sec -= hours * 3600; - sb.append(hours); - sb.append("h"); - } - - long mins = sec / 60; - if (mins > 0) { - sec -= mins * 60; - sb.append(mins); - sb.append("m"); - } - - sb.append(sec); - sb.append("s"); - return sb.toString(); - } - - public void update() { - //Log.v("WeightWatcher.ProcessWatcher", - // "MSG_UPDATE pss=" + mMemInfo.currentPss); - mText.setText("(" + mPid - + (mPid == android.os.Process.myPid() - ? "/A" // app - : "/S") // service - + ") up " + getUptimeString() - + " P=" + mMemInfo.currentPss - + " U=" + mMemInfo.currentUss - ); - mRamGraph.invalidate(); - } - - public class GraphView extends View { - Paint pssPaint, ussPaint, headPaint; - - public GraphView(Context context, AttributeSet attrs) { - super(context, attrs); - - pssPaint = new Paint(); - pssPaint.setColor(RAM_GRAPH_PSS_COLOR); - ussPaint = new Paint(); - ussPaint.setColor(RAM_GRAPH_RSS_COLOR); - headPaint = new Paint(); - headPaint.setColor(Color.WHITE); - } - - public GraphView(Context context) { - this(context, null); - } - - @Override - public void onDraw(Canvas c) { - int w = c.getWidth(); - int h = c.getHeight(); - - if (mMemInfo == null) return; - - final int N = mMemInfo.pss.length; - final float barStep = (float) w / N; - final float barWidth = Math.max(1, barStep); - final float scale = (float) h / mMemInfo.max; - - int i; - float x; - for (i=0; i Date: Mon, 30 Oct 2017 10:03:34 -0700 Subject: [PATCH 071/885] Setting up build rules and placeholder code for QuickStep Change-Id: Ib4ad5c6082b293fc9f9455d70e2b21462b4ab76d --- Android.mk | 50 ++++++++++++++++--- build.gradle | 36 +++++++++---- quickstep/AndroidManifest.xml | 43 ++++++++++++++++ quickstep/res/values/strings.xml | 23 +++++++++ .../quickstep/TouchInteractionService.java | 31 ++++++++++++ .../launcher3/config/FeatureFlags.java | 32 ++++++++++++ src/com/android/launcher3/LauncherState.java | 4 +- .../android/launcher3/config/BaseFlags.java | 7 +++ 8 files changed, 207 insertions(+), 19 deletions(-) create mode 100644 quickstep/AndroidManifest.xml create mode 100644 quickstep/res/values/strings.xml create mode 100644 quickstep/src/com/android/quickstep/TouchInteractionService.java create mode 100644 quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java diff --git a/Android.mk b/Android.mk index 4cc5e42395..1e4083560a 100644 --- a/Android.mk +++ b/Android.mk @@ -112,22 +112,58 @@ LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.* include $(BUILD_PACKAGE) # -# Launcher proto buffer jar used for development +# Build rule for Quickstep app. # include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-proto-files-under, protos) $(call all-proto-files-under, proto_overrides) +LOCAL_MODULE_TAGS := optional + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-recyclerview \ + android-support-dynamic-animation + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) \ + $(call all-java-files-under, src_config) \ + $(call all-java-files-under, quickstep/src) \ + $(call all-java-files-under, quickstep/src_flags) \ + $(call all-proto-files-under, protos) \ + $(call all-proto-files-under, proto_overrides) + +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/quickstep/res \ + $(LOCAL_PATH)/res \ + prebuilts/sdk/current/support/v7/recyclerview/res \ + +LOCAL_PROGUARD_FLAG_FILES := proguard.flags LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/ LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := launcher_proto_lib -LOCAL_IS_HOST_MODULE := true -LOCAL_STATIC_JAVA_LIBRARIES := host-libprotobuf-java-nano +LOCAL_AAPT_FLAGS := \ + --auto-add-overlay \ + --extra-packages android.support.v7.recyclerview \ + +LOCAL_SDK_VERSION := current +LOCAL_MIN_SDK_VERSION := 21 +LOCAL_PACKAGE_NAME := Launcher3QuickStep +LOCAL_PRIVILEGED_MODULE := true +LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 + +LOCAL_FULL_LIBS_MANIFEST_FILES := \ + $(LOCAL_PATH)/AndroidManifest.xml \ + $(LOCAL_PATH)/AndroidManifest-common.xml + +LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml + +LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.* + +include $(BUILD_PACKAGE) + + -include $(BUILD_HOST_JAVA_LIBRARY) # ================================================== include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/build.gradle b/build.gradle index a1b3a6037a..9b0989c922 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,20 @@ android { applicationId 'com.android.launcher3' testApplicationId 'com.android.launcher3.tests' } + + quickstep { + applicationId 'com.android.launcher3' + testApplicationId 'com.android.launcher3.tests' + } } + + // Disable release builds for now + android.variantFilter { variant -> + if (variant.buildType.name.endsWith('release')) { + variant.setIgnore(true); + } + } + sourceSets { main { res.srcDirs = ['res'] @@ -52,31 +65,34 @@ android { } } + debug { + manifest.srcFile "AndroidManifest.xml" + } + androidTest { res.srcDirs = ['tests/res'] java.srcDirs = ['tests/src'] manifest.srcFile "tests/AndroidManifest-common.xml" } - aosp { - java.srcDirs = ['src_flags'] - manifest.srcFile "AndroidManifest.xml" + androidTestDebug { + manifest.srcFile "tests/AndroidManifest.xml" } - aospAndroidTest { - manifest.srcFile "tests/AndroidManifest.xml" + aosp { + java.srcDirs = ['src_flags'] } l3go { res.srcDirs = ['go/res'] java.srcDirs = ['go/src_flags'] - // Note: we are using the Launcher3 manifest here because the gradle manifest-merger uses - // different attributes than the build system. - manifest.srcFile "AndroidManifest.xml" + manifest.srcFile "go/AndroidManifest.xml" } - l3goAndroidTest { - manifest.srcFile "tests/AndroidManifest.xml" + quickstep { + res.srcDirs = ['quickstep/res'] + java.srcDirs = ['quickstep/src_flags', 'quickstep/src'] + manifest.srcFile "quickstep/AndroidManifest.xml" } } } diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml new file mode 100644 index 0000000000..eb05864a04 --- /dev/null +++ b/quickstep/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml new file mode 100644 index 0000000000..ef612268f6 --- /dev/null +++ b/quickstep/res/values/strings.xml @@ -0,0 +1,23 @@ + + + + + + Quickstep + \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java new file mode 100644 index 0000000000..091ab54989 --- /dev/null +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +/** + * Service connected by system-UI for handling touch interaction. + */ +public class TouchInteractionService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java b/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java new file mode 100644 index 0000000000..1edf592b9b --- /dev/null +++ b/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.config; + +import com.android.launcher3.LauncherState; +import com.android.launcher3.states.OverviewState; + +/** + * Defines a set of flags used to control various launcher behaviors + */ +public final class FeatureFlags extends BaseFlags { + + private FeatureFlags() {} + + public static LauncherState createOverviewState(int id) { + return new OverviewState(id); + } +} diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index de3f441c23..661ba11411 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -21,8 +21,8 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CH import android.view.View; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.states.AllAppsState; -import com.android.launcher3.states.OverviewState; import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -50,7 +50,7 @@ public class LauncherState { public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); - public static final LauncherState OVERVIEW = new OverviewState(3); + public static final LauncherState OVERVIEW = FeatureFlags.createOverviewState(3); public final int ordinal; diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index f01923f7d2..4fbab390e5 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -16,6 +16,9 @@ package com.android.launcher3.config; +import com.android.launcher3.LauncherState; +import com.android.launcher3.states.OverviewState; + /** * Defines a set of flags used to control various launcher behaviors. * @@ -58,4 +61,8 @@ abstract class BaseFlags { // Features to control Launcher3Go behavior public static final boolean GO_DISABLE_WIDGETS = false; + + public static LauncherState createOverviewState(int id) { + return new OverviewState(id); + } } From 076839c321e26f055387346d594f20c88b54dfa7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 30 Oct 2017 13:52:20 -0700 Subject: [PATCH 072/885] Moving various runtime flags into the common base class. Caching the ststemApp status for workspace shortcuts. Change-Id: I25663e1f04a9768afcca000294adcbb00ea1db7b --- src/com/android/launcher3/AllAppsList.java | 2 +- src/com/android/launcher3/AppInfo.java | 28 +-------- .../android/launcher3/ItemInfoWithIcon.java | 59 +++++++++++++++++++ src/com/android/launcher3/Launcher.java | 19 +++--- src/com/android/launcher3/ShortcutInfo.java | 54 +---------------- .../launcher3/UninstallDropTarget.java | 11 ++-- .../discovery/AppDiscoveryAppInfo.java | 1 - .../android/launcher3/model/LoaderCursor.java | 13 +++- .../android/launcher3/model/LoaderTask.java | 15 ++--- .../launcher3/model/PackageUpdatedTask.java | 6 +- .../model/UserLockStateChangedTask.java | 6 +- 11 files changed, 110 insertions(+), 104 deletions(-) diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 8ac8570829..5eb6cc72dc 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -154,7 +154,7 @@ public class AllAppsList { for (int i = data.size() - 1; i >= 0; i--) { AppInfo info = data.get(i); if (matcher.matches(info, info.componentName)) { - info.isDisabled = op.apply(info.isDisabled); + info.runtimeStatusFlags = op.apply(info.runtimeStatusFlags); modified.add(info); } } diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 7d2f753852..a5422aa7be 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -32,10 +32,6 @@ import com.android.launcher3.util.PackageManagerHelper; */ public class AppInfo extends ItemInfoWithIcon { - public static final int FLAG_SYSTEM_UNKNOWN = 0; - public static final int FLAG_SYSTEM_YES = 1 << 0; - public static final int FLAG_SYSTEM_NO = 1 << 1; - /** * The intent used to start the application. */ @@ -43,16 +39,6 @@ public class AppInfo extends ItemInfoWithIcon { public ComponentName componentName; - /** - * {@see ShortcutInfo#isDisabled} - */ - public int isDisabled = ShortcutInfo.DEFAULT; - - /** - * Stores if the app is a system app or not. - */ - public int isSystemApp; - public AppInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; } @@ -74,15 +60,14 @@ public class AppInfo extends ItemInfoWithIcon { this.container = ItemInfo.NO_ID; this.user = user; if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) { - isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; } if (quietModeEnabled) { - isDisabled |= ShortcutInfo.FLAG_DISABLED_QUIET_USER; + runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_QUIET_USER; } intent = makeLaunchIntent(info); - - isSystemApp = (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0 + runtimeStatusFlags |= (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0 ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; } @@ -92,8 +77,6 @@ public class AppInfo extends ItemInfoWithIcon { componentName = info.componentName; title = Utilities.trim(info.title); intent = new Intent(info.intent); - isDisabled = info.isDisabled; - isSystemApp = info.isSystemApp; } @Override @@ -119,9 +102,4 @@ public class AppInfo extends ItemInfoWithIcon { .setComponent(cn) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); } - - @Override - public boolean isDisabled() { - return isDisabled != 0; - } } diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java index 1e020e2587..1c4e88b5b0 100644 --- a/src/com/android/launcher3/ItemInfoWithIcon.java +++ b/src/com/android/launcher3/ItemInfoWithIcon.java @@ -33,11 +33,70 @@ public abstract class ItemInfoWithIcon extends ItemInfo { */ public boolean usingLowResIcon; + /** + * Indicates that the icon is disabled due to safe mode restrictions. + */ + public static final int FLAG_DISABLED_SAFEMODE = 1 << 0; + + /** + * Indicates that the icon is disabled as the app is not available. + */ + public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1; + + /** + * Indicates that the icon is disabled as the app is suspended + */ + public static final int FLAG_DISABLED_SUSPENDED = 1 << 2; + + /** + * Indicates that the icon is disabled as the user is in quiet mode. + */ + public static final int FLAG_DISABLED_QUIET_USER = 1 << 3; + + /** + * Indicates that the icon is disabled as the publisher has disabled the actual shortcut. + */ + public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4; + + /** + * Indicates that the icon is disabled as the user partition is currently locked. + */ + public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5; + + public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE | + FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED | + FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER; + + + /** + * The item points to a system app. + */ + public static final int FLAG_SYSTEM_YES = 1 << 6; + + /** + * The item points to a non system app. + */ + public static final int FLAG_SYSTEM_NO = 1 << 7; + + public static final int FLAG_SYSTEM_MASK = FLAG_SYSTEM_YES | FLAG_SYSTEM_NO; + + /** + * Status associated with the system state of the underlying item. This is calculated every + * time a new info is created and not persisted on the disk. + */ + public int runtimeStatusFlags = 0; + protected ItemInfoWithIcon() { } protected ItemInfoWithIcon(ItemInfoWithIcon info) { super(info); iconBitmap = info.iconBitmap; usingLowResIcon = info.usingLowResIcon; + runtimeStatusFlags = info.runtimeStatusFlags; + } + + @Override + public boolean isDisabled() { + return (runtimeStatusFlags & FLAG_DISABLED_MASK) != 0; } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2593c49405..1542703c52 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -19,6 +19,11 @@ package com.android.launcher3; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; @@ -2053,10 +2058,10 @@ public class Launcher extends BaseActivity // Open shortcut final ShortcutInfo shortcut = (ShortcutInfo) tag; - if (shortcut.isDisabled != 0) { - if ((shortcut.isDisabled & - ~ShortcutInfo.FLAG_DISABLED_SUSPENDED & - ~ShortcutInfo.FLAG_DISABLED_QUIET_USER) == 0) { + if (shortcut.isDisabled()) { + if ((shortcut.runtimeStatusFlags & + ~FLAG_DISABLED_SUSPENDED & + ~FLAG_DISABLED_QUIET_USER) == 0) { // If the app is only disabled because of the above flags, launch activity anyway. // Framework will tell the user why the app is suspended. } else { @@ -2067,10 +2072,10 @@ public class Launcher extends BaseActivity } // Otherwise just use a generic error message. int error = R.string.activity_not_available; - if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) { + if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) { error = R.string.safemode_shortcut_error; - } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0 || - (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_LOCKED_USER) != 0) { + } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 || + (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) { error = R.string.shortcut_not_available; } Toast.makeText(this, error, Toast.LENGTH_SHORT).show(); diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index adf008bf44..ec608ca8de 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -64,13 +64,6 @@ public class ShortcutInfo extends ItemInfoWithIcon { */ public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000; - /** - * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}. - * Upto 15 different types supported. - */ - @Deprecated - public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000; - /** * The intent used to start the application. */ @@ -82,42 +75,6 @@ public class ShortcutInfo extends ItemInfoWithIcon { */ public Intent.ShortcutIconResource iconResource; - /** - * Indicates that the icon is disabled due to safe mode restrictions. - */ - public static final int FLAG_DISABLED_SAFEMODE = 1 << 0; - - /** - * Indicates that the icon is disabled as the app is not available. - */ - public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1; - - /** - * Indicates that the icon is disabled as the app is suspended - */ - public static final int FLAG_DISABLED_SUSPENDED = 1 << 2; - - /** - * Indicates that the icon is disabled as the user is in quiet mode. - */ - public static final int FLAG_DISABLED_QUIET_USER = 1 << 3; - - /** - * Indicates that the icon is disabled as the publisher has disabled the actual shortcut. - */ - public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4; - - /** - * Indicates that the icon is disabled as the user partition is currently locked. - */ - public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5; - - /** - * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when - * sd-card is not available). - */ - public int isDisabled = DEFAULT; - /** * A message to display when the user tries to start a disabled shortcut. * This is currently only used for deep shortcuts. @@ -142,7 +99,6 @@ public class ShortcutInfo extends ItemInfoWithIcon { iconResource = info.iconResource; status = info.status; mInstallProgress = info.mInstallProgress; - isDisabled = info.isDisabled; } /** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */ @@ -150,7 +106,6 @@ public class ShortcutInfo extends ItemInfoWithIcon { super(info); title = Utilities.trim(info.title); intent = new Intent(info.intent); - isDisabled = info.isDisabled; } /** @@ -219,9 +174,9 @@ public class ShortcutInfo extends ItemInfoWithIcon { contentDescription = UserManagerCompat.getInstance(context) .getBadgedLabelForUser(label, user); if (shortcutInfo.isEnabled()) { - isDisabled &= ~FLAG_DISABLED_BY_PUBLISHER; + runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER; } else { - isDisabled |= FLAG_DISABLED_BY_PUBLISHER; + runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER; } disabledMessage = shortcutInfo.getDisabledMessage(); } @@ -232,11 +187,6 @@ public class ShortcutInfo extends ItemInfoWithIcon { getIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null; } - @Override - public boolean isDisabled() { - return isDisabled != 0; - } - @Override public ComponentName getTargetComponent() { ComponentName cn = super.getTargetComponent(); diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 8e83a30c0a..68a441ad98 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -1,5 +1,8 @@ package com.android.launcher3; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -83,10 +86,10 @@ public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmList return false; } - if (info instanceof AppInfo) { - AppInfo appInfo = (AppInfo) info; - if (appInfo.isSystemApp != AppInfo.FLAG_SYSTEM_UNKNOWN) { - return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0; + if (info instanceof ItemInfoWithIcon) { + ItemInfoWithIcon iconInfo = (ItemInfoWithIcon) info; + if ((iconInfo.runtimeStatusFlags & FLAG_SYSTEM_MASK) != 0) { + return (iconInfo.runtimeStatusFlags & FLAG_SYSTEM_NO) != 0; } } return getUninstallTarget(info) != null; diff --git a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java index 06493b2b17..ed5cfeb7d6 100644 --- a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java +++ b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java @@ -41,7 +41,6 @@ public class AppDiscoveryAppInfo extends AppInfo { this.intent = item.isInstantApp ? item.launchIntent : item.installIntent; this.title = item.title; this.iconBitmap = item.bitmap; - this.isDisabled = ShortcutInfo.DEFAULT; this.usingLowResIcon = false; this.isInstantApp = item.isInstantApp; this.isRecent = item.isRecent; diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index bc7da9b120..47f370a175 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -16,11 +16,15 @@ package com.android.launcher3.model; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_YES; + import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; +import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.database.Cursor; import android.database.CursorWrapper; @@ -274,8 +278,13 @@ public class LoaderCursor extends CursorWrapper { info.iconBitmap = icon != null ? icon : info.iconBitmap; } - if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) { - info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED; + if (lai != null) { + ApplicationInfo appInfo = lai.getApplicationInfo(); + if (PackageManagerHelper.isAppSuspended(appInfo)) { + info.runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + } + info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 + ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; } // from the db diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 5386fb4a57..c2cfebb85f 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -27,8 +27,6 @@ import android.content.pm.PackageInstaller; import android.graphics.Bitmap; import android.os.Handler; import android.os.Process; -import android.os.SystemClock; -import android.os.Trace; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; @@ -76,6 +74,9 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CancellationException; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; /** @@ -466,13 +467,13 @@ public class LoaderTask implements Runnable { true /* badged */, fallbackIconProvider); if (pmHelper.isAppSuspended( pinnedShortcut.getPackage(), info.user)) { - info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; } intent = info.intent; } else { // Create a shortcut info in disabled mode for now. info = c.loadSimpleShortcut(); - info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; + info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; } } else { // item type == ITEM_TYPE_SHORTCUT info = c.loadSimpleShortcut(); @@ -480,7 +481,7 @@ public class LoaderTask implements Runnable { // Shortcuts are only available on the primary profile if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg, c.user)) { - disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; + disabledState |= FLAG_DISABLED_SUSPENDED; } // App shortcuts that used to be automatically added to Launcher @@ -503,9 +504,9 @@ public class LoaderTask implements Runnable { info.rank = c.getInt(rankIndex); info.spanX = 1; info.spanY = 1; - info.isDisabled |= disabledState; + info.runtimeStatusFlags |= disabledState; if (isSafeMode && !Utilities.isSystemApp(context, intent)) { - info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE; + info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; } if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 53a9862580..470dadf724 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -244,9 +244,9 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { infoUpdated = true; } - int oldDisabledFlags = si.isDisabled; - si.isDisabled = flagOp.apply(si.isDisabled); - if (si.isDisabled != oldDisabledFlags) { + int oldRuntimeFlags = si.runtimeStatusFlags; + si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags); + if (si.runtimeStatusFlags != oldRuntimeFlags) { shortcutUpdated = true; } } diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index 8170f9a67f..2e9ac72243 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.model; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; + import android.content.Context; import android.os.UserHandle; @@ -87,12 +89,12 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { removedKeys.add(key); continue; } - si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER; + si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER; si.updateFromDeepShortcutInfo(shortcut, context); si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context, si.iconBitmap); } else { - si.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; + si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; } updatedShortcutInfos.add(si); } From 031022029b1cc5731d85327f5bd4e183a72cfb3c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 27 Oct 2017 11:05:26 -0700 Subject: [PATCH 073/885] Using state animation to control all-apps transition > Separating all-apps transtions control and vertical shift touch handling > Creating separate spring handler for search box (to avoid adding and removing spring) > Driving all-apps vertical shift using state AnimatorSet Bug: 67678570 Change-Id: I3b6a4d1f43275a5f485b399444742b6b9a8c4bb9 --- src/com/android/launcher3/LauncherState.java | 3 - .../launcher3/LauncherStateManager.java | 41 ++- .../launcher3/PinchToOverviewListener.java | 20 +- .../launcher3/VerticalSwipeController.java | 282 ++++++++++++++++++ src/com/android/launcher3/Workspace.java | 1 + .../allapps/AllAppsTransitionController.java | 259 +--------------- .../anim/AnimationSuccessListener.java | 2 +- .../AnimatorPlaybackController.java} | 84 +++--- .../anim/SpringAnimationHandler.java | 4 + .../launcher3/dragndrop/DragLayer.java | 7 +- .../launcher3/states/AllAppsState.java | 2 +- 11 files changed, 391 insertions(+), 314 deletions(-) create mode 100644 src/com/android/launcher3/VerticalSwipeController.java rename src/com/android/launcher3/{compat/AnimatorSetCompat.java => anim/AnimatorPlaybackController.java} (75%) diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 661ba11411..63c232d3f6 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -39,7 +39,6 @@ public class LauncherState { protected static final int FLAG_HIDE_HOTSEAT = 1 << 2; protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; protected static final int FLAG_DO_NOT_RESTORE = 1 << 4; - protected static final int FLAG_HAS_SPRING = 1 << 5; private static final LauncherState[] sAllStates = new LauncherState[4]; @@ -90,7 +89,6 @@ public class LauncherState { * @see com.android.launcher3.allapps.AllAppsTransitionController */ public final float verticalProgress; - public final boolean hasVerticalSpring; public LauncherState(int id, int containerType, int transitionDuration, float verticalProgress, int flags) { @@ -106,7 +104,6 @@ public class LauncherState { this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0; this.verticalProgress = verticalProgress; - this.hasVerticalSpring = (flags & FLAG_HAS_SPRING) != 0; this.ordinal = id; sAllStates[id] = this; diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 863cae7329..fd940677aa 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -28,6 +28,7 @@ import android.view.View; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; /** * TODO: figure out what kind of tests we can write for this @@ -133,8 +134,7 @@ public class LauncherStateManager { private void goToState(LauncherState state, boolean animated, long delay, Runnable onCompleteRunnable) { - if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null - && !mAllAppsController.isTransitioning()) { + if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null) { // Run any queued runnable if (onCompleteRunnable != null) { @@ -159,27 +159,42 @@ public class LauncherStateManager { return; } - AnimatorSet animation = createAnimationToNewWorkspace(state, onCompleteRunnable); + // Since state NORMAL can be reached from multiple states, just assume that the + // transition plays in reverse and use the same duration as previous state. + mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration; + + AnimatorSet animation = createAnimationToNewWorkspaceInternal(state, onCompleteRunnable); Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher)); if (delay > 0) { mUiHandler.postDelayed(runnable, delay); - } else if (mConfig.shouldPost) { - mUiHandler.post(runnable); } else { - runnable.run(); + mUiHandler.post(runnable); } } - protected AnimatorSet createAnimationToNewWorkspace(final LauncherState state, - final Runnable onCompleteRunnable) { + /** + * Creates a {@link AnimatorPlaybackController} that can be used for a controlled + * state transition. + * @param state the final state for the transition. + * @param duration intended duration for normal playback. Use higher duration for better + * accuracy. + */ + protected AnimatorPlaybackController createAnimationToNewWorkspace( + LauncherState state, long duration) { mConfig.reset(); - mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration; + mConfig.userControlled = true; + mConfig.duration = duration; + return AnimatorPlaybackController.wrap( + createAnimationToNewWorkspaceInternal(state, null), duration); + } + + protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state, + final Runnable onCompleteRunnable) { final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final AnimationLayerSet layerViews = new AnimationLayerSet(); - mAllAppsController.animateToFinalProgress(state.verticalProgress, - state.hasVerticalSpring, animation, mConfig); + mAllAppsController.animateToFinalProgress(state.verticalProgress, animation, mConfig); mLauncher.getWorkspace().setStateWithAnimation(state, layerViews, animation, mConfig); @@ -242,14 +257,14 @@ public class LauncherStateManager { } public static class AnimationConfig extends AnimatorListenerAdapter { - public boolean shouldPost; public long duration; + public boolean userControlled; private AnimatorSet mCurrentAnimation; public void reset() { - shouldPost = true; duration = 0; + userControlled = false; if (mCurrentAnimation != null) { mCurrentAnimation.setDuration(0); diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index d1a253893a..27edaf69a1 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -22,18 +22,19 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; -import com.android.launcher3.compat.AnimatorSetCompat; +import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.util.TouchController; /** * Detects pinches and animates the Workspace to/from overview mode. */ -public class PinchToOverviewListener - implements TouchController, OnScaleGestureListener, Runnable { +public class PinchToOverviewListener extends AnimatorListenerAdapter + implements TouchController, OnScaleGestureListener { private static final float ACCEPT_THRESHOLD = 0.65f; /** @@ -47,7 +48,7 @@ public class PinchToOverviewListener private Workspace mWorkspace = null; private boolean mPinchStarted = false; - private AnimatorSetCompat mCurrentAnimation; + private AnimatorPlaybackController mCurrentAnimation; private float mCurrentScale; private boolean mShouldGoToFinalState; @@ -100,8 +101,9 @@ public class PinchToOverviewListener } mToState = mLauncher.isInState(OVERVIEW) ? NORMAL : OVERVIEW; - mCurrentAnimation = AnimatorSetCompat.wrap(mLauncher.getStateManager() - .createAnimationToNewWorkspace(mToState, this), OVERVIEW_TRANSITION_MS); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, OVERVIEW_TRANSITION_MS); + mCurrentAnimation.getTarget().addListener(this); mPinchStarted = true; mCurrentScale = 1; mShouldGoToFinalState = false; @@ -111,7 +113,7 @@ public class PinchToOverviewListener } @Override - public void run() { + public void onAnimationEnd(Animator animation) { mCurrentAnimation = null; mPinchStarted = false; } @@ -121,9 +123,9 @@ public class PinchToOverviewListener if (mShouldGoToFinalState) { mCurrentAnimation.start(); } else { - mCurrentAnimation.addListener(new AnimatorListenerAdapter() { + mCurrentAnimation.setEndAction(new Runnable() { @Override - public void onAnimationEnd(Animator animation) { + public void run() { mLauncher.getStateManager().goToState( mToState == OVERVIEW ? NORMAL : OVERVIEW, false); } diff --git a/src/com/android/launcher3/VerticalSwipeController.java b/src/com/android/launcher3/VerticalSwipeController.java new file mode 100644 index 0000000000..12c6916764 --- /dev/null +++ b/src/com/android/launcher3/VerticalSwipeController.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.support.animation.SpringAnimation; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.SpringAnimationHandler; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.util.TouchController; + +import java.util.ArrayList; + +/** + * Handles vertical touch gesture on the DragLayer + */ +public class VerticalSwipeController extends AnimatorListenerAdapter + implements TouchController, SwipeDetector.Listener { + + private static final String TAG = "VerticalSwipeController"; + + private static final float RECATCH_REJECTION_FRACTION = .0875f; + private static final int SINGLE_FRAME_MS = 16; + + // Progress after which the transition is assumed to be a success in case user does not fling + private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; + + private final Launcher mLauncher; + private final SwipeDetector mDetector; + + private boolean mNoIntercept; + private int mStartContainerType; + + private AnimatorPlaybackController mCurrentAnimation; + private LauncherState mToState; + + private float mStartProgress; + // Ratio of transition process [0, 1] to drag displacement (px) + private float mProgressMultiplier; + + private SpringAnimationHandler[] mSpringHandlers; + + public VerticalSwipeController(Launcher l) { + mLauncher = l; + mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL); + } + + private boolean canInterceptTouch(MotionEvent ev) { + if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) { + // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. + return false; + } + if (mCurrentAnimation != null) { + // If we are already animating from a previous state, we can intercept. + return true; + } + if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) { + return false; + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } + + return true; + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) { + Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); + mDetector.finishedScrolling(); + mCurrentAnimation = null; + } + } + + private void initSprings() { + AllAppsContainerView appsView = mLauncher.getAppsView(); + + SpringAnimationHandler handler = appsView.getSpringAnimationHandler(); + if (handler == null) { + mSpringHandlers = new SpringAnimationHandler[0]; + return; + } + + ArrayList handlers = new ArrayList<>(); + handlers.add(handler); + + SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling(); + if (searchSpring != null) { + SpringAnimationHandler searchHandler = + new SpringAnimationHandler(Y_DIRECTION, handler.getFactory()); + searchHandler.add(searchSpring, true /* setDefaultValues */); + handlers.add(searchHandler); + } + + mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]); + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mNoIntercept = !canInterceptTouch(ev); + if (mNoIntercept) { + return false; + } + + // Now figure out which direction scroll events the controller will start + // calling the callbacks. + final int directionsToDetectScroll; + boolean ignoreSlopWhenSettling = false; + + if (mCurrentAnimation != null) { + if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) { + directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; + } else { + directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; + ignoreSlopWhenSettling = true; + } + } else { + if (mLauncher.isInState(ALL_APPS)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; + mStartContainerType = ContainerType.ALLAPPS; + } else { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ? + ContainerType.HOTSEAT : ContainerType.WORKSPACE; + } + } + + mDetector.setDetectableScrollConditions( + directionsToDetectScroll, ignoreSlopWhenSettling); + + if (mSpringHandlers == null) { + initSprings(); + } + } + + if (mNoIntercept) { + return false; + } + + onControllerTouchEvent(ev); + return mDetector.isDraggingOrSettling(); + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + for (SpringAnimationHandler h : mSpringHandlers) { + h.addMovement(ev); + } + return mDetector.onTouchEvent(ev); + } + + @Override + public void onDragStart(boolean start) { + if (mCurrentAnimation == null) { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + + // Build current animation + mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS; + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + mCurrentAnimation.getTarget().addListener(this); + mStartProgress = 0; + mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range; + mCurrentAnimation.dispatchOnStart(); + } else { + mCurrentAnimation.pause(); + mStartProgress = mCurrentAnimation.getProgressFraction(); + } + + for (SpringAnimationHandler h : mSpringHandlers) { + h.skipToEnd(); + } + } + + private float getShiftRange() { + return mLauncher.mAllAppsController.getShiftRange(); + } + + @Override + public boolean onDrag(float displacement, float velocity) { + float deltaProgress = mProgressMultiplier * displacement; + mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress); + return true; + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + final long animationDuration; + final int logAction; + final LauncherState targetState; + final float progress = mCurrentAnimation.getProgressFraction(); + + if (fling) { + logAction = Touch.FLING; + if (velocity < 0) { + targetState = ALL_APPS; + animationDuration = SwipeDetector.calculateDuration(velocity, + mToState == ALL_APPS ? (1 - progress) : progress); + } else { + targetState = NORMAL; + animationDuration = SwipeDetector.calculateDuration(velocity, + mToState == ALL_APPS ? progress : (1 - progress)); + } + // snap to top or bottom using the release velocity + } else { + logAction = Touch.SWIPE; + if (progress > SUCCESS_TRANSITION_PROGRESS) { + targetState = mToState; + animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress); + } else { + targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS; + animationDuration = SwipeDetector.calculateDuration(velocity, progress); + } + } + + if (fling && targetState == ALL_APPS) { + for (SpringAnimationHandler h : mSpringHandlers) { + // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) + h.animateToFinalPosition(0 /* pos */, 1 /* startValue */); + } + } + + mCurrentAnimation.setEndAction(new Runnable() { + @Override + public void run() { + if (targetState == mToState) { + // Transition complete. log the action + mLauncher.getUserEventDispatcher().logActionOnContainer(logAction, + mToState == ALL_APPS ? Direction.UP : Direction.DOWN, + mStartContainerType, mLauncher.getWorkspace().getCurrentPage()); + } else { + mLauncher.getStateManager().goToState( + mToState == ALL_APPS ? NORMAL : ALL_APPS, false); + } + mDetector.finishedScrolling(); + mCurrentAnimation = null; + } + }); + + float nextFrameProgress = Utilities.boundToRange( + progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f); + + ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); + anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f); + anim.setDuration(animationDuration); + anim.setInterpolator(scrollInterpolatorForVelocity(velocity)); + anim.start(); + } +} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index e72f54e014..24c470408a 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1582,6 +1582,7 @@ public class Workspace extends PagedView ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1); stepAnimator.addUpdateListener(listener); + stepAnimator.setDuration(config.duration); anim.play(stepAnimator); anim.addListener(listener); } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index a84172ddf5..9b64043426 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -1,25 +1,19 @@ package com.android.launcher3.allapps; -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.support.animation.SpringAnimation; import android.util.Property; -import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; -import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; @@ -27,16 +21,9 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.anim.SpringAnimationHandler; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GradientView; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; -import com.android.launcher3.util.TouchController; /** * Handles AllApps view transition. @@ -48,8 +35,7 @@ import com.android.launcher3.util.TouchController; * If release velocity < THRES1, snap according to either top or bottom depending on whether it's * closer to top or closer to the page indicator. */ -public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener, - SearchUiManager.OnScrollRangeChangeListener { +public class AllAppsTransitionController implements SearchUiManager.OnScrollRangeChangeListener { private static final Property PROGRESS = new Property(Float.class, "progress") { @@ -65,24 +51,16 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } }; - // Spring values used when the user has not flung all apps. - private static final float SPRING_MAX_RELEASE_VELOCITY = 10000; - // The delay (as a % of the animation duration) to start the springs. - private static final float SPRING_DELAY = 0.3f; - private final Interpolator mWorkspaceAccelnterpolator = Interpolators.ACCEL_2; private final Interpolator mHotseatAccelInterpolator = Interpolators.ACCEL_1_5; - private final Interpolator mFastOutSlowInInterpolator = Interpolators.FAST_OUT_SLOW_IN; private static final float PARALLAX_COEFFICIENT = .125f; - private static final int SINGLE_FRAME_MS = 16; private AllAppsContainerView mAppsView; private Workspace mWorkspace; private Hotseat mHotseat; private final Launcher mLauncher; - private final SwipeDetector mDetector; private final boolean mIsDarkTheme; // Animation in this class is controlled by a single variable {@link mProgress}. @@ -91,190 +69,25 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect // When {@link mProgress} is 0, all apps container is pulled up. // When {@link mProgress} is 1, all apps container is pulled down. - private float mShiftStart; // [0, mShiftRange] private float mShiftRange; // changes depending on the orientation private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent - // Velocity of the container. Unit is in px/ms. - private float mContainerVelocity; - private static final float DEFAULT_SHIFT_RANGE = 10; - private static final float RECATCH_REJECTION_FRACTION = .0875f; - - private long mAnimationDuration; - - private boolean mNoIntercept; - private boolean mTouchEventStartedOnHotseat; - - // Used in discovery bounce animation to provide the transition without workspace changing. private boolean mIsTranslateWithoutWorkspace = false; private Animator mDiscoBounceAnimation; private GradientView mGradientView; - private SpringAnimation mSearchSpring; - private SpringAnimationHandler mSpringAnimationHandler; - public AllAppsTransitionController(Launcher l) { mLauncher = l; - mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL); mShiftRange = DEFAULT_SHIFT_RANGE; mProgress = 1f; mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); } - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mNoIntercept = false; - mTouchEventStartedOnHotseat = mLauncher.getDragLayer().isEventOverHotseat(ev); - if (!mLauncher.isInState(ALL_APPS) && !mLauncher.isInState(NORMAL)) { - mNoIntercept = true; - } else if (mLauncher.isInState(ALL_APPS) && - !mAppsView.shouldContainerScroll(ev)) { - mNoIntercept = true; - } else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { - mNoIntercept = true; - } else { - // Now figure out which direction scroll events the controller will start - // calling the callbacks. - int directionsToDetectScroll = 0; - boolean ignoreSlopWhenSettling = false; - - if (mDetector.isIdleState()) { - if (mLauncher.isInState(ALL_APPS)) { - directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE; - } else { - directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE; - } - } else { - if (isInDisallowRecatchBottomZone()) { - directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE; - } else if (isInDisallowRecatchTopZone()) { - directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE; - } else { - directionsToDetectScroll |= SwipeDetector.DIRECTION_BOTH; - ignoreSlopWhenSettling = true; - } - } - mDetector.setDetectableScrollConditions(directionsToDetectScroll, - ignoreSlopWhenSettling); - } - } - - if (mNoIntercept) { - return false; - } - mDetector.onTouchEvent(ev); - if (mDetector.isSettlingState() && (isInDisallowRecatchBottomZone() || isInDisallowRecatchTopZone())) { - return false; - } - return mDetector.isDraggingOrSettling(); - } - - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.addMovement(ev); - } - return mDetector.onTouchEvent(ev); - } - - private boolean isInDisallowRecatchTopZone() { - return mProgress < RECATCH_REJECTION_FRACTION; - } - - private boolean isInDisallowRecatchBottomZone() { - return mProgress > 1 - RECATCH_REJECTION_FRACTION; - } - - @Override - public void onDragStart(boolean start) { - mLauncher.getStateManager().cancelAnimation(); - cancelDiscoveryAnimation(); - mShiftStart = mAppsView.getTranslationY(); - onProgressAnimationStart(); - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.skipToEnd(); - } - } - - @Override - public boolean onDrag(float displacement, float velocity) { - if (mAppsView == null) { - return false; // early termination. - } - - mContainerVelocity = velocity; - - float shift = Math.min(Math.max(0, mShiftStart + displacement), mShiftRange); - setProgress(shift / mShiftRange); - - return true; - } - - @Override - public void onDragEnd(float velocity, boolean fling) { - if (mAppsView == null) { - return; // early termination. - } - - final int containerType = mTouchEventStartedOnHotseat - ? ContainerType.HOTSEAT : ContainerType.WORKSPACE; - if (fling) { - if (velocity < 0) { - calculateDuration(velocity, mAppsView.getTranslationY()); - if (!mLauncher.isInState(ALL_APPS)) { - logSwipeOnContainer(Touch.FLING, Direction.UP, containerType); - } - mLauncher.getStateManager().goToState(ALL_APPS); - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */); - // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) - mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */); - } - } else { - calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); - if (mLauncher.isInState(ALL_APPS)) { - logSwipeOnContainer(Touch.FLING, Direction.DOWN, ContainerType.ALLAPPS); - } - mLauncher.getStateManager().goToState(NORMAL); - } - // snap to top or bottom using the release velocity - } else { - if (mAppsView.getTranslationY() > mShiftRange / 2) { - calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY())); - if (mLauncher.isInState(ALL_APPS)) { - logSwipeOnContainer(Touch.SWIPE, Direction.DOWN, ContainerType.ALLAPPS); - } - mLauncher.getStateManager().goToState(NORMAL); - } else { - calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); - if (!mLauncher.isInState(ALL_APPS)) { - logSwipeOnContainer(Touch.SWIPE, Direction.UP, containerType); - } - mLauncher.getStateManager().goToState(ALL_APPS); - } - } - } - - /** - * Important, make sure that this method is called only when actual launcher state transition - * happen and not when user swipes in one direction only to cancel that swipe seconds later. - * - * @param touchType Swipe or Fling - * @param direction Up or Down - * @param containerType Workspace or Allapps - */ - private void logSwipeOnContainer(int touchType, int direction, int containerType) { - mLauncher.getUserEventDispatcher().logActionOnContainer( - touchType, direction, containerType, - mLauncher.getWorkspace().getCurrentPage()); - } - - public boolean isTransitioning() { - return mDetector.isDraggingOrSettling(); + public float getShiftRange() { + return mShiftRange; } private void onProgressAnimationStart() { @@ -310,10 +123,9 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace * * @see #setFinalProgress(float) - * @see #animateToFinalProgress(float, boolean, AnimatorSet, AnimationConfig) + * @see #animateToFinalProgress(float, AnimatorSet, AnimationConfig) */ public void setProgress(float progress) { - float shiftPrevious = mProgress * mShiftRange; mProgress = progress; float shiftCurrent = progress * mShiftRange; @@ -341,11 +153,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mWorkspace.setWorkspaceYTranslationAndAlpha( PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), workspaceAlpha); - if (!mDetector.isDraggingState()) { - mContainerVelocity = mDetector.computeVelocity(shiftCurrent - shiftPrevious, - System.currentTimeMillis()); - } - updateLightStatusBar(shiftCurrent); } @@ -353,10 +160,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect return mProgress; } - private void calculateDuration(float velocity, float disp) { - mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange); - } - /** * Sets the vertical transition progress to {@param progress} and updates all the dependent UI * accordingly. @@ -371,34 +174,20 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect * dependent UI using various animation events * * @param progress the final vertical progress at the end of the animation - * @param addSpring should there be an addition spring animation for the sub-views * @param animationOut the target AnimatorSet where this animation should be added * @param outConfig an in/out configuration which can be shared with other animations */ - public void animateToFinalProgress(float progress, boolean addSpring, - AnimatorSet animationOut, AnimationConfig outConfig) { + public void animateToFinalProgress( + float progress, AnimatorSet animationOut, AnimationConfig outConfig) { if (Float.compare(mProgress, progress) == 0) { // Fail fast onProgressAnimationEnd(); return; } - outConfig.shouldPost = true; - Interpolator interpolator; - if (mDetector.isIdleState()) { - mAnimationDuration = LauncherAnimUtils.ALL_APPS_TRANSITION_MS; - mShiftStart = mAppsView.getTranslationY(); - interpolator = mFastOutSlowInInterpolator; - } else { - interpolator = scrollInterpolatorForVelocity(mContainerVelocity); - mProgress = Utilities.boundToRange( - mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange, 0f, 1f); - outConfig.shouldPost = false; - } - - outConfig.duration = mAnimationDuration; + Interpolator interpolator = outConfig.userControlled ? LINEAR : FAST_OUT_SLOW_IN; ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, progress); - anim.setDuration(mAnimationDuration); + anim.setDuration(outConfig.duration); anim.setInterpolator(interpolator); anim.addListener(new AnimationSuccessListener() { @Override @@ -413,20 +202,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect }); animationOut.play(anim); - if (addSpring) { - ValueAnimator springAnim = ValueAnimator.ofFloat(0, 1); - springAnim.setDuration((long) (mAnimationDuration * SPRING_DELAY)); - springAnim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - if (!mSpringAnimationHandler.isRunning()) { - float velocity = mProgress * SPRING_MAX_RELEASE_VELOCITY; - mSpringAnimationHandler.animateToPositionWithVelocity(0, 1, velocity); - } - } - }); - animationOut.play(anim); - } } public void showDiscoveryBounce() { @@ -476,12 +251,6 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mWorkspace = workspace; mHotseat.bringToFront(); mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this); - mSpringAnimationHandler = mAppsView.getSpringAnimationHandler(); - mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling(); - } - - private boolean hasSpringAnimationHandler() { - return FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null; } @Override @@ -503,13 +272,5 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mHotseat.setVisibility(View.INVISIBLE); mAppsView.setVisibility(View.VISIBLE); } - if (hasSpringAnimationHandler()) { - mSpringAnimationHandler.remove(mSearchSpring); - mSpringAnimationHandler.reset(); - } - - // TODO: This call should no longer be needed once caret stops animating. - setProgress(mProgress); - mDetector.finishedScrolling(); } } diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java index feebc6c5de..9448632ac5 100644 --- a/src/com/android/launcher3/anim/AnimationSuccessListener.java +++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java @@ -24,7 +24,7 @@ import android.animation.AnimatorListenerAdapter; */ public abstract class AnimationSuccessListener extends AnimatorListenerAdapter { - private boolean mCancelled = false; + protected boolean mCancelled = false; @Override public void onAnimationCancel(Animator animation) { diff --git a/src/com/android/launcher3/compat/AnimatorSetCompat.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java similarity index 75% rename from src/com/android/launcher3/compat/AnimatorSetCompat.java rename to src/com/android/launcher3/anim/AnimatorPlaybackController.java index 6676725930..826a20e7a2 100644 --- a/src/com/android/launcher3/compat/AnimatorSetCompat.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -13,37 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.compat; +package com.android.launcher3.anim; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorSet; import android.animation.ValueAnimator; -import android.annotation.TargetApi; -import android.os.Build; - -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.Interpolators; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** - * Compat implementation for various new APIs in {@link AnimatorSet} + * Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators + * and durations. * - * Note: The compat implementation does not support start delays on child animations or + * Note: The implementation does not support start delays on child animations or * sequential playbacks. */ -public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateListener { +public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener { - public static AnimatorSetCompat wrap(AnimatorSet anim, int duration) { - if (Utilities.ATLEAST_OREO) { - return new AnimatorSetCompatVO(anim, duration); - } else { - return new AnimatorSetCompatVL(anim, duration); - } + public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) { + + /** + * TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed. + */ + return new AnimatorPlaybackControllerVL(anim, duration); } private final ValueAnimator mAnimationPlayer; @@ -52,23 +47,28 @@ public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateL protected final AnimatorSet mAnim; protected float mCurrentFraction; + private Runnable mEndAction; - protected AnimatorSetCompat(AnimatorSet anim, int duration) { + protected AnimatorPlaybackController(AnimatorSet anim, long duration) { mAnim = anim; mDuration = duration; mAnimationPlayer = ValueAnimator.ofFloat(0, 1); mAnimationPlayer.setInterpolator(Interpolators.LINEAR); + mAnimationPlayer.addListener(new OnAnimationEndDispatcher()); mAnimationPlayer.addUpdateListener(this); } + public AnimatorSet getTarget() { + return mAnim; + } + /** * Starts playing the animation forward from current position. */ public void start() { mAnimationPlayer.setFloatValues(mCurrentFraction, 1); mAnimationPlayer.setDuration(clampDuration(1 - mCurrentFraction)); - mAnimationPlayer.addListener(new OnAnimationEndDispatcher()); mAnimationPlayer.start(); } @@ -78,20 +78,38 @@ public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateL public void reverse() { mAnimationPlayer.setFloatValues(mCurrentFraction, 0); mAnimationPlayer.setDuration(clampDuration(mCurrentFraction)); - mAnimationPlayer.addListener(new OnAnimationEndDispatcher()); mAnimationPlayer.start(); } + /** + * Pauses the currently playing animation. + */ + public void pause() { + mAnimationPlayer.cancel(); + } + + /** + * Returns the underlying animation used for controlling the set. + */ + public ValueAnimator getAnimationPlayer() { + return mAnimationPlayer; + } + /** * Sets the current animation position and updates all the child animators accordingly. */ public abstract void setPlayFraction(float fraction); + public float getProgressFraction() { + return mCurrentFraction; + } + /** - * @see Animator#addListener(AnimatorListener) + * Sets the action to be called when the animation is completed. Also clears any + * previously set action. */ - public void addListener(Animator.AnimatorListener listener) { - mAnimationPlayer.addListener(listener); + public void setEndAction(Runnable runnable) { + mEndAction = runnable; } @Override @@ -124,11 +142,11 @@ public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateL } } - public static class AnimatorSetCompatVL extends AnimatorSetCompat { + public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController { private final ValueAnimator[] mChildAnimations; - private AnimatorSetCompatVL(AnimatorSet anim, int duration) { + private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration) { super(anim, duration); // Build animation list @@ -164,25 +182,19 @@ public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateL } - @TargetApi(Build.VERSION_CODES.O) - private static class AnimatorSetCompatVO extends AnimatorSetCompat { - - private AnimatorSetCompatVO(AnimatorSet anim, int duration) { - super(anim, duration); - } + private class OnAnimationEndDispatcher extends AnimationSuccessListener { @Override - public void setPlayFraction(float fraction) { - mCurrentFraction = fraction; - mAnim.setCurrentPlayTime(clampDuration(fraction)); + public void onAnimationStart(Animator animation) { + mCancelled = false; } - } - - private class OnAnimationEndDispatcher extends AnimationSuccessListener { @Override public void onAnimationSuccess(Animator animator) { dispatchOnEndRecursively(mAnim); + if (mEndAction != null) { + mEndAction.run(); + } } private void dispatchOnEndRecursively(Animator animator) { diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java index eec3a48ee3..29a2430a56 100644 --- a/src/com/android/launcher3/anim/SpringAnimationHandler.java +++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java @@ -83,6 +83,10 @@ public class SpringAnimationHandler { mAnimations.add(spring); } + public AnimationFactory getFactory() { + return mAnimationFactory; + } + /** * Adds a new or recycled animation to the list of springs handled by this class. * diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 5b1a4dc6cf..0f0b20d2bf 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -46,6 +46,7 @@ import com.android.launcher3.PinchToOverviewListener; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; +import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; @@ -96,6 +97,7 @@ public class DragLayer extends InsettableFrameLayout { // Handles all apps pull up interaction private AllAppsTransitionController mAllAppsController; + private VerticalSwipeController mVerticalSwipeController; private TouchController mActiveController; /** @@ -121,6 +123,7 @@ public class DragLayer extends InsettableFrameLayout { mLauncher = launcher; mDragController = dragController; mAllAppsController = allAppsTransitionController; + mVerticalSwipeController = new VerticalSwipeController(mLauncher); boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService( Context.ACCESSIBILITY_SERVICE)).isEnabled(); @@ -191,8 +194,8 @@ public class DragLayer extends InsettableFrameLayout { return true; } - if (mAllAppsController.onControllerInterceptTouchEvent(ev)) { - mActiveController = mAllAppsController; + if (mVerticalSwipeController.onControllerInterceptTouchEvent(ev)) { + mActiveController = mVerticalSwipeController; return true; } diff --git a/src/com/android/launcher3/states/AllAppsState.java b/src/com/android/launcher3/states/AllAppsState.java index ee35b4d7c3..ed3023ab13 100644 --- a/src/com/android/launcher3/states/AllAppsState.java +++ b/src/com/android/launcher3/states/AllAppsState.java @@ -32,7 +32,7 @@ public class AllAppsState extends LauncherState { public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown"; - private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_HAS_SPRING; + private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY; public AllAppsState(int id) { super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS); From 48043ee0d6ad9bee4d10118e5c67fef6c2d0cb5e Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 1 Nov 2017 15:40:22 -0700 Subject: [PATCH 074/885] Also reset mBadgeScale to 0 when recycling view. When left as 1f, the drawBadgeIfNecessary thinks that there is still a badge -- which results in a NPE when trying to draw the badge. Bug: 68324671 Change-Id: I4eedb5825b3a6429c99de0466a77d368a39b1951 --- src/com/android/launcher3/BubbleTextView.java | 1 + src/com/android/launcher3/badge/BadgeRenderer.java | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f20baea039..5bdc4ed2a7 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -183,6 +183,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver { public void reset() { mBadgeInfo = null; mBadgePalette = null; + mBadgeScale = 0f; mForceHideBadge = false; } diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java index c2cc215ce5..6ce334ef35 100644 --- a/src/com/android/launcher3/badge/BadgeRenderer.java +++ b/src/com/android/launcher3/badge/BadgeRenderer.java @@ -26,6 +26,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Shader; import android.support.annotation.Nullable; +import android.util.Log; import android.util.SparseArray; import com.android.launcher3.R; @@ -38,6 +39,8 @@ import com.android.launcher3.graphics.ShadowGenerator; */ public class BadgeRenderer { + private static final String TAG = "BadgeRenderer"; + private static final boolean DOTS_ONLY = true; // The badge sizes are defined as percentages of the app icon size. @@ -95,6 +98,10 @@ public class BadgeRenderer { */ public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo, Rect iconBounds, float badgeScale, Point spaceForOffset) { + if (palette == null || iconBounds == null || spaceForOffset == null) { + Log.e(TAG, "Invalid null argument(s) passed in call to draw."); + return; + } mTextPaint.setColor(palette.textColor); IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge() ? mLargeIconDrawer : mSmallIconDrawer; From 73133391e5b67db7d057468e516327a5b942a612 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 2 Nov 2017 11:04:49 -0700 Subject: [PATCH 075/885] Adding a placeholder recents activity Change-Id: Idde86b4008559cde8bcf13fba90a8c72c3f1f591 --- Android.mk | 18 +++++-- build.gradle | 2 + quickstep/AndroidManifest.xml | 6 ++- quickstep/libs/sysui_shared.jar | Bin 0 -> 71118 bytes .../android/quickstep/RecentsActivity.java | 45 ++++++++++++++++++ 5 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 quickstep/libs/sysui_shared.jar create mode 100644 quickstep/src/com/android/quickstep/RecentsActivity.java diff --git a/Android.mk b/Android.mk index 1e4083560a..282b878dd9 100644 --- a/Android.mk +++ b/Android.mk @@ -16,6 +16,14 @@ LOCAL_PATH := $(call my-dir) +# +# Prebuilt Java Libraries +# +include $(CLEAR_VARS) +LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \ + libSharedSystemUI:quickstep/libs/sysui_shared.jar +include $(BUILD_MULTI_PREBUILT) + # # Build rule for Launcher3 app. # @@ -121,7 +129,8 @@ LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-v4 \ android-support-v7-recyclerview \ - android-support-dynamic-animation + android-support-dynamic-animation \ + libSharedSystemUI LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ @@ -136,7 +145,7 @@ LOCAL_RESOURCE_DIR := \ $(LOCAL_PATH)/res \ prebuilts/sdk/current/support/v7/recyclerview/res \ -LOCAL_PROGUARD_FLAG_FILES := proguard.flags +LOCAL_PROGUARD_ENABLED := disabled LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/ @@ -146,8 +155,8 @@ LOCAL_AAPT_FLAGS := \ --auto-add-overlay \ --extra-packages android.support.v7.recyclerview \ -LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 21 +LOCAL_SDK_VERSION := system_current +LOCAL_MIN_SDK_VERSION := 26 LOCAL_PACKAGE_NAME := Launcher3QuickStep LOCAL_PRIVILEGED_MODULE := true LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 @@ -157,7 +166,6 @@ LOCAL_FULL_LIBS_MANIFEST_FILES := \ $(LOCAL_PATH)/AndroidManifest-common.xml LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml - LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.* include $(BUILD_PACKAGE) diff --git a/build.gradle b/build.gradle index 9b0989c922..f357e5ca81 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,8 @@ dependencies { compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}" compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7' + quickstepCompile fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar') + testCompile 'junit:junit:4.12' androidTestCompile "org.mockito:mockito-core:1.9.5" androidTestCompile 'com.google.dexmaker:dexmaker:1.2' diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index eb05864a04..08c740c4f0 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -36,8 +36,12 @@ android:supportsRtl="true" > + android:exported="true" /> + + diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar new file mode 100644 index 0000000000000000000000000000000000000000..2f6c8810f63e1e2f4ad9757762c56618a28051ae GIT binary patch literal 71118 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�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 h-#c`!|IritHX mAdapter; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(this); + plan.preloadPlan(new RecentsTaskLoader(this, 1, 1, 0), -1); + + mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); + mAdapter.addAll(plan.getTaskStack().getStackTasks()); + setListAdapter(mAdapter); + } +} From ce4fa5f33b29ac6083cab61ab8c4cd7314f6c934 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 Nov 2017 11:43:37 -0700 Subject: [PATCH 076/885] Fixing wrong packageName being used for loading icons Bug: 68796720 Change-Id: I9b0da050ead0fe5bf40de0e0d87949bda50be699 --- src/com/android/launcher3/graphics/LauncherIcons.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java index d471af6e74..1eddb2cf04 100644 --- a/src/com/android/launcher3/graphics/LauncherIcons.java +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -72,7 +72,7 @@ public class LauncherIcons { PackageManager packageManager = context.getPackageManager(); // the resource try { - Resources resources = packageManager.getResourcesForApplication(iconRes.resourceName); + Resources resources = packageManager.getResourcesForApplication(iconRes.packageName); if (resources != null) { final int id = resources.getIdentifier(iconRes.resourceName, null, null); // do not stamp old legacy shortcuts as the app may have already forgotten about it From 416fc0b3281f1bb336e126dcd553fd96b4e28242 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 Nov 2017 11:16:53 -0700 Subject: [PATCH 077/885] Updating the systemUI lib Change-Id: Ie945325ea5c436188c79a8f79082d5098357de6d --- quickstep/libs/sysui_shared.jar | Bin 71118 -> 75304 bytes .../android/quickstep/RecentsActivity.java | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar index 2f6c8810f63e1e2f4ad9757762c56618a28051ae..f9ce6e0c9ec99a23523e2038dd1083efe112de93 100644 GIT binary patch delta 19624 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 RRumjj@U;Wy2ZS2@`Y-ZWnS=lU delta 15487 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 zzstpo(this, android.R.layout.simple_list_item_1); mAdapter.addAll(plan.getTaskStack().getStackTasks()); From 85525173ce063305cc2b787fcfcb5f58a14410d4 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 6 Nov 2017 13:00:42 -0800 Subject: [PATCH 078/885] Rearranging the code structure to allow replacing state logic. Change-Id: I6f83d0f77045ba189f02dd465bf70ffc2a239aa1 --- Android.mk | 6 +- .../launcher3/uioverrides}/OverviewState.java | 2 +- .../launcher3/uioverrides/UiFactory.java | 16 ++-- src/com/android/launcher3/Launcher.java | 13 --- .../android/launcher3/LauncherProvider.java | 3 +- src/com/android/launcher3/LauncherState.java | 4 +- .../launcher3/LauncherStateManager.java | 2 +- .../launcher3/VerticalSwipeController.java | 2 +- src/com/android/launcher3/Workspace.java | 3 +- .../android/launcher3/config/BaseFlags.java | 8 +- .../launcher3/dragndrop/DragLayer.java | 34 ++------ .../launcher3/uioverrides/OverviewState.java | 83 +++++++++++++++++++ .../uioverrides}/PinchToOverviewListener.java | 16 ++-- .../launcher3/uioverrides/UiFactory.java | 19 ++--- 14 files changed, 130 insertions(+), 81 deletions(-) rename {src/com/android/launcher3/states => quickstep/src/com/android/launcher3/uioverrides}/OverviewState.java (98%) rename src_config/com/android/launcher3/BuildConfig.java => quickstep/src/com/android/launcher3/uioverrides/UiFactory.java (62%) create mode 100644 src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java rename {src/com/android/launcher3 => src_ui_overrides/com/android/launcher3/uioverrides}/PinchToOverviewListener.java (91%) rename quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java => src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java (59%) diff --git a/Android.mk b/Android.mk index 282b878dd9..13540f4bfe 100644 --- a/Android.mk +++ b/Android.mk @@ -38,7 +38,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ - $(call all-java-files-under, src_config) \ + $(call all-java-files-under, src_ui_overrides) \ $(call all-java-files-under, src_flags) \ $(call all-proto-files-under, protos) \ $(call all-proto-files-under, proto_overrides) @@ -83,7 +83,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ - $(call all-java-files-under, src_config) \ + $(call all-java-files-under, src_ui_overrides) \ $(call all-java-files-under, go/src_flags) \ $(call all-proto-files-under, protos) \ $(call all-proto-files-under, proto_overrides) @@ -134,9 +134,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ - $(call all-java-files-under, src_config) \ $(call all-java-files-under, quickstep/src) \ - $(call all-java-files-under, quickstep/src_flags) \ $(call all-proto-files-under, protos) \ $(call all-proto-files-under, proto_overrides) diff --git a/src/com/android/launcher3/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java similarity index 98% rename from src/com/android/launcher3/states/OverviewState.java rename to quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 344a4f95fd..0e7035a465 100644 --- a/src/com/android/launcher3/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.states; +package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.Utilities.isAccessibilityEnabled; diff --git a/src_config/com/android/launcher3/BuildConfig.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java similarity index 62% rename from src_config/com/android/launcher3/BuildConfig.java rename to quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 4df75a1a12..e43023a13c 100644 --- a/src_config/com/android/launcher3/BuildConfig.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -14,11 +14,15 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.uioverrides; -/** - * Config file used by Make. This file is automatically generated when using gradle. - */ -public class BuildConfig { - public static final String APPLICATION_ID = "com.android.launcher3"; +import com.android.launcher3.Launcher; +import com.android.launcher3.VerticalSwipeController; +import com.android.launcher3.util.TouchController; + +public class UiFactory { + + public static TouchController[] createTouchControllers(Launcher launcher) { + return new TouchController[] {new VerticalSwipeController(launcher)}; + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1542703c52..ca75603c74 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -167,7 +167,6 @@ import java.util.concurrent.Executor; public class Launcher extends BaseActivity implements LauncherExterns, View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener, - AccessibilityManager.AccessibilityStateChangeListener, WallpaperColorInfo.OnThemeChangeListener { public static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -361,9 +360,6 @@ public class Launcher extends BaseActivity mPopupDataProvider = new PopupDataProvider(this); - ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE)) - .addAccessibilityStateChangeListener(this); - restoreState(savedInstanceState); // We only load the page synchronously if the user rotates (or triggers a @@ -1552,10 +1548,6 @@ public class Launcher extends BaseActivity mAppWidgetHost = null; TextKeyListener.getInstance().release(); - - ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE)) - .removeAccessibilityStateChangeListener(this); - WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null); LauncherAnimUtils.onDestroyActivity(); @@ -2190,11 +2182,6 @@ public class Launcher extends BaseActivity startActivity(intent, getActivityLaunchOptions(v)); } - @Override - public void onAccessibilityStateChanged(boolean enabled) { - mDragLayer.onAccessibilityStateChanged(enabled); - } - private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { try { StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 25a698b159..98568e4363 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -44,7 +44,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Process; -import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; @@ -86,7 +85,7 @@ public class LauncherProvider extends ContentProvider { */ public static final int SCHEMA_VERSION = 27; - public static final String AUTHORITY = (BuildConfig.APPLICATION_ID + ".settings").intern(); + public static final String AUTHORITY = FeatureFlags.AUTHORITY; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 63c232d3f6..bb09a9f860 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -21,9 +21,9 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CH import android.view.View; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.states.AllAppsState; import com.android.launcher3.states.SpringLoadedState; +import com.android.launcher3.uioverrides.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import java.util.Arrays; @@ -49,7 +49,7 @@ public class LauncherState { public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); - public static final LauncherState OVERVIEW = FeatureFlags.createOverviewState(3); + public static final LauncherState OVERVIEW = new OverviewState(3); public final int ordinal; diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index fd940677aa..b99df717ac 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -179,7 +179,7 @@ public class LauncherStateManager { * @param duration intended duration for normal playback. Use higher duration for better * accuracy. */ - protected AnimatorPlaybackController createAnimationToNewWorkspace( + public AnimatorPlaybackController createAnimationToNewWorkspace( LauncherState state, long duration) { mConfig.reset(); mConfig.userControlled = true; diff --git a/src/com/android/launcher3/VerticalSwipeController.java b/src/com/android/launcher3/VerticalSwipeController.java index 12c6916764..b3dc176b70 100644 --- a/src/com/android/launcher3/VerticalSwipeController.java +++ b/src/com/android/launcher3/VerticalSwipeController.java @@ -75,7 +75,7 @@ public class VerticalSwipeController extends AnimatorListenerAdapter private boolean canInterceptTouch(MotionEvent ev) { if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) { - // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. + // Don't listen for the swipe gesture if we are already in some other state. return false; } if (mCurrentAnimation != null) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 24c470408a..32f96df61f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -54,7 +54,6 @@ import android.util.SparseArray; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.Toast; @@ -82,7 +81,7 @@ import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; -import com.android.launcher3.states.OverviewState; +import com.android.launcher3.uioverrides.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 4fbab390e5..a03dabb2bb 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -16,9 +16,6 @@ package com.android.launcher3.config; -import com.android.launcher3.LauncherState; -import com.android.launcher3.states.OverviewState; - /** * Defines a set of flags used to control various launcher behaviors. * @@ -32,10 +29,10 @@ abstract class BaseFlags { BaseFlags() {} public static final boolean IS_DOGFOOD_BUILD = false; + public static final String AUTHORITY = "com.android.launcher3.settings".intern(); // Custom flags go below this public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false; - public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false; // When enabled allows to use any point on the fast scrollbar to start dragging. public static final boolean LAUNCHER3_DIRECT_SCROLL = true; // When enabled the promise icon is visible in all apps while installation an app. @@ -62,7 +59,4 @@ abstract class BaseFlags { // Features to control Launcher3Go behavior public static final boolean GO_DISABLE_WIDGETS = false; - public static LauncherState createOverviewState(int id) { - return new OverviewState(id); - } } diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 0f0b20d2bf..8189b236e9 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -42,17 +42,15 @@ import com.android.launcher3.CellLayout; import com.android.launcher3.DropTargetBar; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; -import com.android.launcher3.PinchToOverviewListener; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; -import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.keyboard.ViewGroupFocusHelper; +import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TouchController; @@ -92,13 +90,10 @@ public class DragLayer extends InsettableFrameLayout { private final ViewGroupFocusHelper mFocusIndicatorHelper; private final PageCutOutScrimDrawable mPageCutOutScrim; - // Related to pinch-to-go-to-overview gesture. - private PinchToOverviewListener mPinchListener = null; - // Handles all apps pull up interaction private AllAppsTransitionController mAllAppsController; - private VerticalSwipeController mVerticalSwipeController; + protected TouchController[] mControllers; private TouchController mActiveController; /** * Used to create a new DragLayer from XML. @@ -123,11 +118,7 @@ public class DragLayer extends InsettableFrameLayout { mLauncher = launcher; mDragController = dragController; mAllAppsController = allAppsTransitionController; - mVerticalSwipeController = new VerticalSwipeController(mLauncher); - - boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService( - Context.ACCESSIBILITY_SERVICE)).isEnabled(); - onAccessibilityStateChanged(isAccessibilityEnabled); + mControllers = UiFactory.createTouchControllers(mLauncher); } public ViewGroupFocusHelper getFocusIndicatorHelper() { @@ -144,11 +135,6 @@ public class DragLayer extends InsettableFrameLayout { return super.verifyDrawable(who) || who == mPageCutOutScrim; } - public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) { - mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled - ? null : new PinchToOverviewListener(mLauncher); - } - public boolean isEventOverHotseat(MotionEvent ev) { return isEventOverView(mLauncher.getHotseat(), ev); } @@ -194,15 +180,11 @@ public class DragLayer extends InsettableFrameLayout { return true; } - if (mVerticalSwipeController.onControllerInterceptTouchEvent(ev)) { - mActiveController = mVerticalSwipeController; - return true; - } - - if (mPinchListener != null && mPinchListener.onControllerInterceptTouchEvent(ev)) { - // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.) - mActiveController = mPinchListener; - return true; + for (TouchController controller : mControllers) { + if (controller.onControllerInterceptTouchEvent(ev)) { + mActiveController = controller; + return true; + } } return false; } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java new file mode 100644 index 0000000000..0e7035a465 --- /dev/null +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.Utilities.isAccessibilityEnabled; + +import android.graphics.Rect; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Workspace; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +/** + * Definition for overview state + */ +public class OverviewState extends LauncherState { + + // The percent to shrink the workspace during overview mode + public static final float SCALE_FACTOR = 0.7f; + + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT; + + public OverviewState(int id) { + super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS); + } + + @Override + public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { + DeviceProfile grid = launcher.getDeviceProfile(); + Workspace ws = launcher.getWorkspace(); + Rect insets = launcher.getDragLayer().getInsets(); + + int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight(); + int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight()); + Rect workspacePadding = grid.getWorkspacePadding(null); + int workspaceTop = insets.top + workspacePadding.top; + int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom; + int overviewTop = insets.top; + int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight; + int workspaceOffsetTopEdge = + workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2; + int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2; + return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge }; + } + + @Override + public void onStateEnabled(Launcher launcher) { + launcher.getWorkspace().setPageRearrangeEnabled(true); + + if (isAccessibilityEnabled(launcher)) { + launcher.getOverviewPanel().getChildAt(0).performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + } + } + + @Override + public void onStateDisabled(Launcher launcher) { + launcher.getWorkspace().setPageRearrangeEnabled(false); + } + + @Override + public View getFinalFocus(Launcher launcher) { + return launcher.getOverviewPanel(); + } +} diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java similarity index 91% rename from src/com/android/launcher3/PinchToOverviewListener.java rename to src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java index 27edaf69a1..40bf057286 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java @@ -14,19 +14,23 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.Utilities.isAccessibilityEnabled; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Workspace; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.util.TouchController; @@ -70,8 +74,10 @@ public class PinchToOverviewListener extends AnimatorListenerAdapter @Override public boolean onScaleBegin(ScaleGestureDetector detector) { - if (!mLauncher.isInState(NORMAL) - && !mLauncher.isInState(OVERVIEW)) { + if (isAccessibilityEnabled(mLauncher)) { + return false; + } + if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(OVERVIEW)) { // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc. return false; } @@ -86,7 +92,7 @@ public class PinchToOverviewListener extends AnimatorListenerAdapter if (mWorkspace == null) { mWorkspace = mLauncher.getWorkspace(); } - if (mWorkspace.isSwitchingState() || mWorkspace.mScrollInteractionBegan) { + if (mWorkspace.isSwitchingState()) { // Don't listen for the pinch gesture while switching state, as it will cause a jump // once the state switching animation is complete. return false; diff --git a/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java similarity index 59% rename from quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java rename to src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index 1edf592b9b..e217f708ea 100644 --- a/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -14,19 +14,16 @@ * limitations under the License. */ -package com.android.launcher3.config; +package com.android.launcher3.uioverrides; -import com.android.launcher3.LauncherState; -import com.android.launcher3.states.OverviewState; +import com.android.launcher3.Launcher; +import com.android.launcher3.VerticalSwipeController; +import com.android.launcher3.util.TouchController; -/** - * Defines a set of flags used to control various launcher behaviors - */ -public final class FeatureFlags extends BaseFlags { +public class UiFactory { - private FeatureFlags() {} - - public static LauncherState createOverviewState(int id) { - return new OverviewState(id); + public static TouchController[] createTouchControllers(Launcher launcher) { + return new TouchController[] { + new VerticalSwipeController(launcher), new PinchToOverviewListener(launcher)}; } } From 8b0a647a1144ca9fe600014d36a25e13012a99f5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 6 Nov 2017 21:09:23 -0800 Subject: [PATCH 079/885] Removing dependency on deprecated android.test.** package Change-Id: Ib1065e26fff3c193d12531c8bca944693ea6137c --- build.gradle | 9 +- .../launcher3/logging/FileLogTest.java | 31 +++++-- .../model/AddWorkspaceItemsTaskTest.java | 30 +++--- .../model/BaseModelUpdateTaskTestCase.java | 33 ++++--- .../model/CacheDataUpdatedTaskTest.java | 22 ++++- .../model/GridSizeMigrationTaskTest.java | 91 +++++++++++++------ .../PackageInstallStateChangedTaskTest.java | 17 +++- .../launcher3/provider/RestoreDbTaskTest.java | 20 +++- .../launcher3/util/FocusLogicTest.java | 32 +++---- .../launcher3/util/GridOccupancyTest.java | 15 ++- 10 files changed, 203 insertions(+), 97 deletions(-) diff --git a/build.gradle b/build.gradle index f357e5ca81..61c05e5625 100644 --- a/build.gradle +++ b/build.gradle @@ -80,18 +80,18 @@ android { } aosp { - java.srcDirs = ['src_flags'] + java.srcDirs = ['src_flags', "src_ui_overrides"] } l3go { res.srcDirs = ['go/res'] - java.srcDirs = ['go/src_flags'] + java.srcDirs = ['go/src_flags', "src_ui_overrides"] manifest.srcFile "go/AndroidManifest.xml" } quickstep { res.srcDirs = ['quickstep/res'] - java.srcDirs = ['quickstep/src_flags', 'quickstep/src'] + java.srcDirs = ['src_flags', 'quickstep/src'] manifest.srcFile "quickstep/AndroidManifest.xml" } } @@ -115,7 +115,8 @@ dependencies { androidTestCompile "org.mockito:mockito-core:1.9.5" androidTestCompile 'com.google.dexmaker:dexmaker:1.2' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' - androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:runner:1.0.0' + androidTestCompile 'com.android.support.test:rules:1.0.0' androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' androidTestCompile "com.android.support:support-annotations:${SUPPORT_LIBS_VERSION}" } diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java index 7048c2868c..9c7cb8f61c 100644 --- a/tests/src/com/android/launcher3/logging/FileLogTest.java +++ b/tests/src/com/android/launcher3/logging/FileLogTest.java @@ -1,41 +1,51 @@ package com.android.launcher3.logging; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Calendar; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + /** * Tests for {@link FileLog} */ @SmallTest -public class FileLogTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class FileLogTest { private File mTempDir; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { int count = 0; do { - mTempDir = new File(getContext().getCacheDir(), "log-test-" + (count++)); + mTempDir = new File(InstrumentationRegistry.getTargetContext().getCacheDir(), + "log-test-" + (count++)); } while(!mTempDir.mkdir()); FileLog.setDir(mTempDir); } - @Override - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { // Clear existing logs new File(mTempDir, "log-0").delete(); new File(mTempDir, "log-1").delete(); mTempDir.delete(); - super.tearDown(); } + @Test public void testPrintLog() throws Exception { if (!FileLog.ENABLED) { return; @@ -56,6 +66,7 @@ public class FileLogTest extends AndroidTestCase { assertTrue(writer.toString().contains("hoolalala")); } + @Test public void testOldFileTruncated() throws Exception { if (!FileLog.ENABLED) { return; diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java index a486cebc25..401711d072 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.net.Uri; +import android.support.test.runner.AndroidJUnit4; import android.util.Pair; import com.android.launcher3.ItemInfo; @@ -15,21 +16,25 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.LongArrayMap; -import com.android.launcher3.util.Provider; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import java.util.ArrayList; import java.util.List; -import static org.mockito.Matchers.isNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * Tests for {@link AddWorkspaceItemsTask} */ +@RunWith(AndroidJUnit4.class) public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { private final ComponentName mComponent1 = new ComponentName("a", "b"); @@ -39,9 +44,8 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { private ArrayList newScreens; private LongArrayMap screenOccupancy; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void initData() throws Exception { existingScreens = new ArrayList<>(); screenOccupancy = new LongArrayMap<>(); newScreens = new ArrayList<>(); @@ -62,6 +66,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { }; } + @Test public void testFindSpaceForItem_prefers_second() { // First screen has only one hole of size 1 int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); @@ -83,13 +88,12 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 2, 3)); } + @Test public void testFindSpaceForItem_adds_new_screen() throws Exception { // First screen has 2 holes of sizes 3x2 and 2x3 setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5)); commitScreensToDb(); - when(appState.getContext()).thenReturn(getMockContext()); - ArrayList oldScreens = new ArrayList<>(existingScreens); Pair spaceFound = newTask() .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 3, 3); @@ -97,6 +101,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { assertTrue(newScreens.contains(spaceFound.first)); } + @Test public void testAddItem_existing_item_ignored() throws Exception { ShortcutInfo info = new ShortcutInfo(); info.intent = new Intent().setComponent(mComponent1); @@ -105,12 +110,11 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); commitScreensToDb(); - when(appState.getContext()).thenReturn(getMockContext()); - // Nothing was added assertTrue(executeTaskForTest(newTask(info)).isEmpty()); } + @Test public void testAddItem_some_items_added() throws Exception { ShortcutInfo info = new ShortcutInfo(); info.intent = new Intent().setComponent(mComponent1); @@ -122,8 +126,6 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); commitScreensToDb(); - when(appState.getContext()).thenReturn(getMockContext()); - executeTaskForTest(newTask(info, info2)).get(0).run(); ArgumentCaptor notAnimated = ArgumentCaptor.forClass(ArrayList.class); ArgumentCaptor animated = ArgumentCaptor.forClass(ArrayList.class); @@ -168,7 +170,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { } private void commitScreensToDb() throws Exception { - LauncherSettings.Settings.call(getMockContentResolver(), + LauncherSettings.Settings.call(mProviderRule.getResolver(), LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; @@ -183,6 +185,6 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase { v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build()); } - getMockContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops); + mProviderRule.getResolver().applyBatch(LauncherProvider.AUTHORITY, ops); } } diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java index 3d03507eab..bbb6772d3f 100644 --- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java +++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java @@ -1,7 +1,9 @@ package com.android.launcher3.model; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.LauncherActivityInfo; import android.content.res.Resources; @@ -11,7 +13,8 @@ import android.os.Process; import android.os.UserHandle; import android.support.annotation.NonNull; import android.support.test.InstrumentationRegistry; -import android.test.ProviderTestCase2; +import android.support.test.rule.provider.ProviderTestRule; +import android.support.test.runner.AndroidJUnit4; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppFilter; @@ -28,6 +31,8 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Provider; import com.android.launcher3.util.TestLauncherProvider; +import org.junit.Before; +import org.junit.Rule; import org.mockito.ArgumentCaptor; import java.io.BufferedReader; @@ -46,7 +51,12 @@ import static org.mockito.Mockito.when; /** * Base class for writing tests for Model update tasks. */ -public class BaseModelUpdateTaskTestCase extends ProviderTestCase2 { +public class BaseModelUpdateTaskTestCase { + + @Rule + public ProviderTestRule mProviderRule = + new ProviderTestRule.Builder(TestLauncherProvider.class, LauncherProvider.AUTHORITY) + .build(); public final HashMap> fieldCache = new HashMap<>(); @@ -63,14 +73,8 @@ public class BaseModelUpdateTaskTestCase extends ProviderTestCase2(Arrays.asList(pkg))); } + @Test public void testCacheUpdate_update_apps() throws Exception { // Clear all icons from apps list so that its easy to check what was updated for (AppInfo info : allAppsList.data) { @@ -52,6 +64,7 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase { } } + @Test public void testSessionUpdate_ignores_normal_apps() throws Exception { executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1")); @@ -59,6 +72,7 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase { verifyUpdate(); } + @Test public void testSessionUpdate_updates_pending_apps() throws Exception { executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3")); diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java index fd62d36440..b92f61205c 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java @@ -1,11 +1,16 @@ package com.android.launcher3.model; +import android.content.ContentResolver; import android.content.ContentValues; +import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; import android.database.Cursor; import android.graphics.Point; -import android.test.ProviderTestCase2; -import android.test.suitebuilder.annotation.MediumTest; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.MediumTest; +import android.support.test.rule.provider.ProviderTestRule; +import android.support.test.runner.AndroidJUnit4; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherModel; @@ -15,15 +20,29 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask; import com.android.launcher3.util.TestLauncherProvider; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * Unit tests for {@link GridSizeMigrationTask} */ @MediumTest -public class GridSizeMigrationTaskTest extends ProviderTestCase2 { +@RunWith(AndroidJUnit4.class) +public class GridSizeMigrationTaskTest { + + @Rule + public ProviderTestRule mProviderRule = + new ProviderTestRule.Builder(TestLauncherProvider.class, LauncherProvider.AUTHORITY) + .build(); private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP; private static final long HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT; @@ -37,20 +56,25 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2 mValidPackages; private InvariantDeviceProfile mIdp; + private Context mContext; - public GridSizeMigrationTaskTest() { - super(TestLauncherProvider.class, LauncherProvider.AUTHORITY); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mValidPackages = new HashSet<>(); mValidPackages.add(TEST_PACKAGE); mIdp = new InvariantDeviceProfile(); + + mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) { + + @Override + public ContentResolver getContentResolver() { + return mProviderRule.getResolver(); + } + }; } + @Test public void testHotseatMigration_apps_dropped() throws Exception { long[] hotseatItems = { addItem(APPLICATION, 0, HOTSEAT, 0, 0), @@ -61,7 +85,7 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2 allScreens = LauncherModel.loadWorkspaceScreensDb(getMockContext()); + ArrayList allScreens = LauncherModel.loadWorkspaceScreensDb(mContext); assertEquals(ids.length, allScreens.size()); int total = 0; @@ -330,7 +361,8 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2 Date: Mon, 6 Nov 2017 16:00:34 -0800 Subject: [PATCH 080/885] Separing the overview states for normal and quickstep builds In QuickStep, adding a placeholder ScrollView and changing the state logic appropriately to handle that Change-Id: I10223c0692788d6e5dbf8c408c01cafb4e39bd2c --- quickstep/res/layout/overview_panel.xml | 48 +++++ .../launcher3/uioverrides/OverviewState.java | 27 +-- .../launcher3/uioverrides/UiFactory.java | 6 + .../com/android/quickstep/RecentsView.java | 41 +++++ res/layout/overview_panel.xml | 6 +- res/values/config.xml | 4 - src/com/android/launcher3/DeviceProfile.java | 47 ----- src/com/android/launcher3/Launcher.java | 96 +--------- src/com/android/launcher3/Workspace.java | 4 +- .../OverviewAccessibilityDelegate.java | 7 +- .../launcher3/uioverrides/OverviewPanel.java | 170 ++++++++++++++++++ .../launcher3/uioverrides/OverviewState.java | 2 +- .../launcher3/uioverrides/UiFactory.java | 6 + 13 files changed, 287 insertions(+), 177 deletions(-) create mode 100644 quickstep/res/layout/overview_panel.xml create mode 100644 quickstep/src/com/android/quickstep/RecentsView.java rename {src/com/android/launcher3/accessibility => src_ui_overrides/com/android/launcher3/uioverrides}/OverviewAccessibilityDelegate.java (92%) create mode 100644 src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml new file mode 100644 index 0000000000..466470f3b4 --- /dev/null +++ b/quickstep/res/layout/overview_panel.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 0e7035a465..51a8a5e837 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -27,6 +27,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.quickstep.RecentsView; /** * Definition for overview state @@ -44,36 +45,18 @@ public class OverviewState extends LauncherState { @Override public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { - DeviceProfile grid = launcher.getDeviceProfile(); - Workspace ws = launcher.getWorkspace(); - Rect insets = launcher.getDragLayer().getInsets(); - - int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight(); - int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight()); - Rect workspacePadding = grid.getWorkspacePadding(null); - int workspaceTop = insets.top + workspacePadding.top; - int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom; - int overviewTop = insets.top; - int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight; - int workspaceOffsetTopEdge = - workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2; - int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2; - return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge }; + // TODO: Find a better transition + return new float[] {SCALE_FACTOR, 0}; } @Override public void onStateEnabled(Launcher launcher) { - launcher.getWorkspace().setPageRearrangeEnabled(true); - - if (isAccessibilityEnabled(launcher)) { - launcher.getOverviewPanel().getChildAt(0).performAccessibilityAction( - AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } + ((RecentsView) launcher.getOverviewPanel()).setViewVisible(true); } @Override public void onStateDisabled(Launcher launcher) { - launcher.getWorkspace().setPageRearrangeEnabled(false); + ((RecentsView) launcher.getOverviewPanel()).setViewVisible(false); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index e43023a13c..540b20041d 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -16,6 +16,8 @@ package com.android.launcher3.uioverrides; +import android.view.View.AccessibilityDelegate; + import com.android.launcher3.Launcher; import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.util.TouchController; @@ -25,4 +27,8 @@ public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { return new TouchController[] {new VerticalSwipeController(launcher)}; } + + public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { + return null; + } } diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java new file mode 100644 index 0000000000..e474ecba12 --- /dev/null +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.HorizontalScrollView; + +/** + * A placeholder view for recents + */ +public class RecentsView extends HorizontalScrollView { + public RecentsView(Context context) { + this(context, null); + } + + public RecentsView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setAlpha(0); + } + + public void setViewVisible(boolean isVisible) { } +} diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml index d1ac56c505..c795b81fee 100644 --- a/res/layout/overview_panel.xml +++ b/res/layout/overview_panel.xml @@ -14,11 +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/values/config.xml b/res/values/config.xml index 7a33ae6535..54328e1486 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -1,8 +1,4 @@ - - - 22 - false false diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 8aaad1367e..6030e08865 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -65,12 +65,6 @@ public class DeviceProfile { private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f; - // Overview mode - private final int overviewModeMinIconZoneHeightPx; - private final int overviewModeMaxIconZoneHeightPx; - private final int overviewModeBarItemWidthPx; - private final int overviewModeBarSpacerWidthPx; - private final float overviewModeIconZoneRatio; // Workspace private final int desiredWorkspaceLeftRightMarginPx; @@ -196,16 +190,6 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing); topWorkspacePadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding); - overviewModeMinIconZoneHeightPx = - res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); - overviewModeMaxIconZoneHeightPx = - res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height); - overviewModeBarItemWidthPx = - res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width); - overviewModeBarSpacerWidthPx = - res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width); - overviewModeIconZoneRatio = - res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f; iconDrawablePaddingOriginalPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size); @@ -570,13 +554,6 @@ public class DeviceProfile { } } - public int getOverviewModeButtonBarHeight() { - int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); - return Utilities.boundToRange(zoneHeight, - overviewModeMinIconZoneHeightPx, - overviewModeMaxIconZoneHeightPx); - } - public static int calculateCellWidth(int width, int countX) { return width / countX; } @@ -597,16 +574,6 @@ public class DeviceProfile { return isVerticalBarLayout() || isLargeTablet; } - private int getVisibleChildCount(ViewGroup parent) { - int visibleChildren = 0; - for (int i = 0; i < parent.getChildCount(); i++) { - if (parent.getChildAt(i).getVisibility() != View.GONE) { - visibleChildren++; - } - } - return visibleChildren; - } - public void layout(Launcher launcher, boolean notifyListeners) { FrameLayout.LayoutParams lp; boolean hasVerticalBarLayout = isVerticalBarLayout(); @@ -701,20 +668,6 @@ public class DeviceProfile { pageIndicator.setLayoutParams(lp); } - // Layout the Overview Mode - ViewGroup overviewMode = launcher.getOverviewPanel(); - if (overviewMode != null) { - int visibleChildCount = getVisibleChildCount(overviewMode); - int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx; - int maxWidth = totalItemWidth + (visibleChildCount - 1) * overviewModeBarSpacerWidthPx; - - lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); - lp.width = Math.min(availableWidthPx, maxWidth); - lp.height = getOverviewModeButtonBarHeight(); - lp.bottomMargin = mInsets.bottom; - overviewMode.setLayoutParams(lp); - } - // Layout the AllAppsRecyclerView View view = launcher.findViewById(R.id.apps_list_view); int paddingLeftRight = desiredWorkspaceLeftRightMarginPx + cellLayoutPaddingLeftRightPx; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 67c0578958..75968ae2bc 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -88,7 +88,6 @@ import android.view.View; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; import android.view.animation.OvershootInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; @@ -910,7 +909,7 @@ public class Launcher extends BaseActivity } } - protected boolean hasSettings() { + public boolean hasSettings() { if (mLauncherCallbacks != null) { return mLauncherCallbacks.hasSettings(); } else { @@ -976,32 +975,6 @@ public class Launcher extends BaseActivity return handled; } - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - // Ignore the menu key if we are currently dragging or are on the custom content screen - if (!mDragController.isDragging()) { - // Close any open floating view - AbstractFloatingView.closeAllOpenViews(this); - - // Show the overview mode if we are on the workspace - if (isInState(NORMAL) && !mWorkspace.isSwitchingState()) { - mStateManager.goToState(OVERVIEW, true /* animate */, new Runnable() { - @Override - public void run() { - // Hitting the menu button when in touch mode does not trigger touch - // mode to be disabled, so if requested, force focus on one of the - // overview panel buttons. - mOverviewPanel.requestFocusFromTouch(); - } - }); - } - } - return true; - } - return super.onKeyUp(keyCode, event); - } - private String getTypedText() { return mDefaultKeySsb.toString(); } @@ -1056,6 +1029,7 @@ public class Launcher extends BaseActivity mFocusHandler = mDragLayer.getFocusIndicatorHelper(); mWorkspace = mDragLayer.findViewById(R.id.workspace); mWorkspace.initParentViews(mDragLayer); + mOverviewPanel = findViewById(R.id.overview_panel); mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION @@ -1070,9 +1044,6 @@ public class Launcher extends BaseActivity mHotseat.setOnLongClickListener(this); } - // Setup the overview panel - setupOverviewPanel(); - // Setup the workspace mWorkspace.setHapticFeedbackEnabled(false); mWorkspace.setOnLongClickListener(this); @@ -1097,43 +1068,6 @@ public class Launcher extends BaseActivity mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace); } - private void setupOverviewPanel() { - mOverviewPanel = findViewById(R.id.overview_panel); - - // Bind wallpaper button actions - View wallpaperButton = findViewById(R.id.wallpaper_button); - new OverviewButtonClickListener(ControlType.WALLPAPER_BUTTON) { - @Override - public void handleViewClick(View view) { - onClickWallpaperPicker(view); - } - }.attachTo(wallpaperButton); - - // Bind widget button actions - new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) { - @Override - public void handleViewClick(View view) { - onClickAddWidgetButton(view); - } - }.attachTo(findViewById(R.id.widget_button)); - - // Bind settings actions - View settingsButton = findViewById(R.id.settings_button); - boolean hasSettings = hasSettings(); - if (hasSettings) { - new OverviewButtonClickListener(ControlType.SETTINGS_BUTTON) { - @Override - public void handleViewClick(View view) { - onClickSettingsButton(view); - } - }.attachTo(settingsButton); - } else { - settingsButton.setVisibility(View.GONE); - } - - mOverviewPanel.setAlpha(0f); - } - /** * Sets the all apps button. This method is called from {@link Hotseat}. * TODO: Get rid of this. @@ -2118,19 +2052,6 @@ public class Launcher extends BaseActivity } } - /** - * Event handler for the (Add) Widgets button that appears after a long press - * on the home screen. - */ - public void onClickAddWidgetButton(View view) { - if (LOGD) Log.d(TAG, "onClickAddWidgetButton"); - if (mIsSafeModeEnabled) { - Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); - } else { - WidgetsFullSheet.show(this, true /* animated */); - } - } - /** * Event handler for the wallpaper picker button that appears after a long press * on the home screen. @@ -2164,19 +2085,6 @@ public class Launcher extends BaseActivity } } - /** - * Event handler for a click on the settings button that appears after a long press - * on the home screen. - */ - public void onClickSettingsButton(View v) { - if (LOGD) Log.d(TAG, "onClickSettingsButton"); - Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES) - .setPackage(getPackageName()); - intent.setSourceBounds(getViewBounds(v)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent, getActivityLaunchOptions(v)); - } - private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { try { StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 32f96df61f..daa9bd0119 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -61,7 +61,6 @@ import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; -import com.android.launcher3.accessibility.OverviewAccessibilityDelegate; import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.AnimationLayerSet; @@ -82,6 +81,7 @@ import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.uioverrides.OverviewState; +import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -454,7 +454,7 @@ public class Workspace extends PagedView @Override public void initParentViews(View parent) { super.initParentViews(parent); - mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate()); + mPageIndicator.setAccessibilityDelegate(UiFactory.newPageIndicatorAccessibilityDelegate()); } private void setupLayoutTransition() { diff --git a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java similarity index 92% rename from src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java rename to src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java index 771353e77b..88a1e10d84 100644 --- a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3.accessibility; +package com.android.launcher3.uioverrides; import android.content.Context; import android.os.Bundle; @@ -55,6 +55,7 @@ public class OverviewAccessibilityDelegate extends AccessibilityDelegate { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { Launcher launcher = Launcher.getLauncher(host.getContext()); + OverviewPanel overviewPanel = launcher.findViewById(R.id.overview_panel); if (action == OVERVIEW) { launcher.getStateManager().goToState(LauncherState.OVERVIEW); return true; @@ -62,10 +63,10 @@ public class OverviewAccessibilityDelegate extends AccessibilityDelegate { launcher.onClickWallpaperPicker(host); return true; } else if (action == WIDGETS) { - launcher.onClickAddWidgetButton(host); + overviewPanel.onClickAddWidgetButton(); return true; } else if (action == SETTINGS) { - launcher.onClickSettingsButton(host); + overviewPanel.onClickSettingsButton(host); return true; } return super.performAccessibilityAction(host, action, args); diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java new file mode 100644 index 0000000000..1fb56e74fe --- /dev/null +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.Toast; + +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action; +import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; +import com.android.launcher3.widget.WidgetsFullSheet; + +public class OverviewPanel extends LinearLayout implements Insettable, View.OnClickListener, + View.OnLongClickListener { + + // Out of 100, the percent of space the overview bar should try and take vertically. + private static final float OVERVIEW_ICON_ZONE_RATIO = 0.22f; + + private final Launcher mLauncher; + + public OverviewPanel(Context context) { + this(context, null); + } + + public OverviewPanel(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public OverviewPanel(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = Launcher.getLauncher(context); + setAlpha(0); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + int visibleChildCount = 3; + // Attach buttons. + attachListeners(findViewById(R.id.wallpaper_button)); + attachListeners(findViewById(R.id.widget_button)); + + View settingsButton = findViewById(R.id.settings_button); + if (mLauncher.hasSettings()) { + attachListeners(settingsButton); + } else { + settingsButton.setVisibility(GONE); + visibleChildCount--; + } + + // Init UI + Resources res = getResources(); + int itemWidthPx = + res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width); + int spacerWidthPx = + res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width); + + int totalItemWidth = visibleChildCount * itemWidthPx; + int maxWidth = totalItemWidth + (visibleChildCount - 1) * spacerWidthPx; + + getLayoutParams().width = Math.min(mLauncher.getDeviceProfile().availableWidthPx, maxWidth); + getLayoutParams().height = getButtonBarHeight(mLauncher); + } + + private void attachListeners(View view) { + view.setOnClickListener(this); + view.setOnLongClickListener(this); + } + + @Override + public void setInsets(Rect insets) { + ((FrameLayout.LayoutParams) getLayoutParams()).bottomMargin = insets.bottom; + } + + @Override + public void onClick(View view) { + handleViewClick(view, Action.Touch.TAP); + } + + @Override + public boolean onLongClick(View view) { + return handleViewClick(view, Action.Touch.LONGPRESS); + } + + private boolean handleViewClick(View view, int action) { + if (mLauncher.getWorkspace().isSwitchingState()) { + return false; + } + + final int controlType; + if (view.getId() == R.id.wallpaper_button) { + mLauncher.onClickWallpaperPicker(view); + controlType = ControlType.WALLPAPER_BUTTON; + } else if (view.getId() == R.id.widget_button) { + onClickAddWidgetButton(); + controlType = ControlType.WIDGETS_BUTTON; + } else if (view.getId() == R.id.settings_button) { + onClickSettingsButton(view); + controlType = ControlType.SETTINGS_BUTTON; + } else { + return false; + } + + mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType); + return true; + } + + /** + * Event handler for the (Add) Widgets button that appears after a long press + * on the home screen. + */ + public void onClickAddWidgetButton() { + if (getContext().getPackageManager().isSafeMode()) { + Toast.makeText(mLauncher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); + } else { + WidgetsFullSheet.show(mLauncher, true /* animated */); + } + } + + /** + * Event handler for a click on the settings button that appears after a long press + * on the home screen. + */ + public void onClickSettingsButton(View v) { + Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES) + .setPackage(getContext().getPackageName()); + intent.setSourceBounds(mLauncher.getViewBounds(v)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getContext().startActivity(intent, mLauncher.getActivityLaunchOptions(v)); + } + + + public static int getButtonBarHeight(Launcher launcher) { + int zoneHeight = (int) (OVERVIEW_ICON_ZONE_RATIO * + launcher.getDeviceProfile().availableWidthPx); + Resources res = launcher.getResources(); + int overviewModeMinIconZoneHeightPx = + res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); + int overviewModeMaxIconZoneHeightPx = + res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height); + return Utilities.boundToRange(zoneHeight, + overviewModeMinIconZoneHeightPx, + overviewModeMaxIconZoneHeightPx); + } +} diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java index 0e7035a465..9e2ad982f4 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java @@ -48,7 +48,7 @@ public class OverviewState extends LauncherState { Workspace ws = launcher.getWorkspace(); Rect insets = launcher.getDragLayer().getInsets(); - int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight(); + int overviewButtonBarHeight = OverviewPanel.getButtonBarHeight(launcher); int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight()); Rect workspacePadding = grid.getWorkspacePadding(null); int workspaceTop = insets.top + workspacePadding.top; diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index e217f708ea..6776150c9a 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -16,6 +16,8 @@ package com.android.launcher3.uioverrides; +import android.view.View.AccessibilityDelegate; + import com.android.launcher3.Launcher; import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.util.TouchController; @@ -26,4 +28,8 @@ public class UiFactory { return new TouchController[] { new VerticalSwipeController(launcher), new PinchToOverviewListener(launcher)}; } + + public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { + return new OverviewAccessibilityDelegate(); + } } From c4fa8c312b98401f456a44067f87eff511162e2a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 7 Nov 2017 12:23:58 -0800 Subject: [PATCH 081/885] Changing the state UI logic for normal build and quickStep build > Creating ShareHandlers for managing UI > In normal build, hotseat is hidden in overview, while in QuickStepBuild, it is visible Change-Id: I5f8d35c75b861d912d93fce186b5dd74106184c3 --- .../launcher3/uioverrides/OverviewState.java | 9 +--- .../RecentsViewStateController.java | 53 +++++++++++++++++++ .../launcher3/uioverrides/UiFactory.java | 7 +++ .../com/android/quickstep/RecentsView.java | 24 ++++++++- src/com/android/launcher3/Launcher.java | 6 ++- src/com/android/launcher3/LauncherState.java | 7 +-- .../launcher3/LauncherStateManager.java | 41 ++++++++++---- src/com/android/launcher3/Workspace.java | 4 +- .../WorkspaceStateTransitionAnimation.java | 17 ++---- .../allapps/AllAppsTransitionController.java | 34 ++++++------ .../launcher3/uioverrides/OverviewPanel.java | 34 ++++++++++-- .../launcher3/uioverrides/OverviewState.java | 2 +- .../launcher3/uioverrides/UiFactory.java | 7 +++ 13 files changed, 187 insertions(+), 58 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 51a8a5e837..9bdd7a3849 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -16,16 +16,11 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; -import static com.android.launcher3.Utilities.isAccessibilityEnabled; -import android.graphics.Rect; import android.view.View; -import android.view.accessibility.AccessibilityNodeInfo; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.quickstep.RecentsView; @@ -37,7 +32,7 @@ public class OverviewState extends LauncherState { // The percent to shrink the workspace during overview mode public static final float SCALE_FACTOR = 0.7f; - private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT; + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE; public OverviewState(int id) { super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS); @@ -46,7 +41,7 @@ public class OverviewState extends LauncherState { @Override public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { // TODO: Find a better transition - return new float[] {SCALE_FACTOR, 0}; + return new float[] {0f, 0}; } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java new file mode 100644 index 0000000000..da1eff9741 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER; + +import android.animation.AnimatorSet; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager.AnimationConfig; +import com.android.launcher3.LauncherStateManager.StateHandler; +import com.android.launcher3.WorkspaceStateTransitionAnimation.AnimatedPropertySetter; +import com.android.launcher3.WorkspaceStateTransitionAnimation.PropertySetter; +import com.android.launcher3.anim.AnimationLayerSet; + +public class RecentsViewStateController implements StateHandler { + + private final Launcher mLauncher; + + public RecentsViewStateController(Launcher launcher) { + mLauncher = launcher; + } + + @Override + public void setState(LauncherState state) { + setState(state, NO_ANIM_PROPERTY_SETTER); + } + + @Override + public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, + AnimatorSet anim, AnimationConfig config) { + setState(toState, new AnimatedPropertySetter(config.duration, layerViews, anim)); + } + + private void setState(LauncherState state, PropertySetter setter) { + setter.setViewAlpha(null, mLauncher.getOverviewPanel(), + state == LauncherState.OVERVIEW ? 1 : 0); + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 540b20041d..c490c3fa7f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -19,6 +19,7 @@ package com.android.launcher3.uioverrides; import android.view.View.AccessibilityDelegate; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.util.TouchController; @@ -31,4 +32,10 @@ public class UiFactory { public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { return null; } + + public static StateHandler[] getStateHandler(Launcher launcher) { + return new StateHandler[] { + launcher.getAllAppsController(), launcher.getWorkspace(), + new RecentsViewStateController(launcher)}; + } } diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java index e474ecba12..d85de8f405 100644 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -17,13 +17,19 @@ package com.android.quickstep; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.widget.HorizontalScrollView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; + /** * A placeholder view for recents */ -public class RecentsView extends HorizontalScrollView { +public class RecentsView extends HorizontalScrollView implements Insettable { public RecentsView(Context context) { this(context, null); } @@ -35,7 +41,23 @@ public class RecentsView extends HorizontalScrollView { public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setAlpha(0); + setVisibility(INVISIBLE); } public void setViewVisible(boolean isVisible) { } + + @Override + public void setInsets(Rect insets) { + MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); + lp.topMargin = insets.top; + lp.bottomMargin = insets.bottom; + lp.leftMargin = insets.left; + lp.rightMargin = insets.right; + + DeviceProfile dp = Launcher.getLauncher(getContext()).getDeviceProfile(); + if (!dp.isVerticalBarLayout()) { + lp.bottomMargin += dp.hotseatBarSizePx + getResources().getDimensionPixelSize( + R.dimen.dynamic_grid_min_page_indicator_size); + } + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 75968ae2bc..fa4a1c843e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -339,7 +339,7 @@ public class Launcher extends BaseActivity mDragController = new DragController(this); mAllAppsController = new AllAppsTransitionController(this); - mStateManager = new LauncherStateManager(this, mAllAppsController); + mStateManager = new LauncherStateManager(this); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); @@ -1287,6 +1287,10 @@ public class Launcher extends BaseActivity } } + public AllAppsTransitionController getAllAppsController() { + return mAllAppsController; + } + public DragLayer getDragLayer() { return mDragLayer; } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index bb09a9f860..d6cd8a35c9 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -36,9 +36,8 @@ public class LauncherState { protected static final int FLAG_SHOW_SCRIM = 1 << 0; protected static final int FLAG_MULTI_PAGE = 1 << 1; - protected static final int FLAG_HIDE_HOTSEAT = 1 << 2; - protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 3; - protected static final int FLAG_DO_NOT_RESTORE = 1 << 4; + protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 2; + protected static final int FLAG_DO_NOT_RESTORE = 1 << 3; private static final LauncherState[] sAllStates = new LauncherState[4]; @@ -80,7 +79,6 @@ public class LauncherState { * @see WorkspaceStateTransitionAnimation */ public final boolean hasScrim; - public final boolean hideHotseat; public final int transitionDuration; /** @@ -97,7 +95,6 @@ public class LauncherState { this.hasScrim = (flags & FLAG_SHOW_SCRIM) != 0; this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0; - this.hideHotseat = (flags & FLAG_HIDE_HOTSEAT) != 0; this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : IMPORTANT_FOR_ACCESSIBILITY_AUTO; diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index b99df717ac..f016e8d2e5 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -29,6 +29,7 @@ import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.uioverrides.UiFactory; /** * TODO: figure out what kind of tests we can write for this @@ -78,21 +79,26 @@ public class LauncherStateManager { private final AnimationConfig mConfig = new AnimationConfig(); private final Handler mUiHandler; private final Launcher mLauncher; - private final AllAppsTransitionController mAllAppsController; + private StateHandler[] mStateHandlers; private LauncherState mState = NORMAL; - public LauncherStateManager( - Launcher l, AllAppsTransitionController allAppsController) { + public LauncherStateManager(Launcher l) { mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; - mAllAppsController = allAppsController; } public LauncherState getState() { return mState; } + private StateHandler[] getStateHandlers() { + if (mStateHandlers == null) { + mStateHandlers = UiFactory.getStateHandler(mLauncher); + } + return mStateHandlers; + } + /** * @see #goToState(LauncherState, boolean, Runnable) */ @@ -148,8 +154,9 @@ public class LauncherStateManager { if (!animated) { setState(state); - mAllAppsController.setFinalProgress(state.verticalProgress); - mLauncher.getWorkspace().setState(state); + for (StateHandler handler : getStateHandlers()) { + handler.setState(state); + } mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); // Run any queued runnable @@ -190,14 +197,12 @@ public class LauncherStateManager { protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state, final Runnable onCompleteRunnable) { - final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final AnimationLayerSet layerViews = new AnimationLayerSet(); - mAllAppsController.animateToFinalProgress(state.verticalProgress, animation, mConfig); - mLauncher.getWorkspace().setStateWithAnimation(state, - layerViews, animation, mConfig); - + for (StateHandler handler : getStateHandlers()) { + handler.setStateWithAnimation(state, layerViews, animation, mConfig); + } animation.addListener(layerViews); animation.addListener(new AnimationSuccessListener() { @@ -285,4 +290,18 @@ public class LauncherStateManager { mCurrentAnimation.addListener(this); } } + + public interface StateHandler { + + /** + * Updates the UI to {@param state} without any animations + */ + void setState(LauncherState state); + + /** + * Sets the UI to {@param state} by animating any changes. + */ + void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, + AnimatorSet anim, AnimationConfig config); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index daa9bd0119..685dcee4f2 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -105,7 +105,7 @@ import java.util.Set; public class Workspace extends PagedView implements DropTarget, DragSource, View.OnTouchListener, DragController.DragListener, ViewGroup.OnHierarchyChangeListener, - Insettable { + Insettable, LauncherStateManager.StateHandler { private static final String TAG = "Launcher.Workspace"; /** The value that {@link #mTransitionProgress} must be greater than for @@ -1558,6 +1558,7 @@ public class Workspace extends PagedView /** * Sets the current workspace {@link LauncherState} and updates the UI without any animations */ + @Override public void setState(LauncherState toState) { onStartStateTransition(toState); mStateTransitionAnimation.setState(toState); @@ -1567,6 +1568,7 @@ public class Workspace extends PagedView /** * Sets the current workspace {@link LauncherState}, then animates the UI */ + @Override public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, AnimatorSet anim, AnimationConfig config) { StateTransitionListener listener = new StateTransitionListener(toState); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index e14461e85d..8edec40d24 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -90,7 +90,7 @@ class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimat */ public class WorkspaceStateTransitionAnimation { - private static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); + public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); public final int mWorkspaceScrimAlpha; @@ -141,14 +141,6 @@ public class WorkspaceStateTransitionAnimation { propertySetter); } - float finalHotseatAlpha = state.hideHotseat ? 0f : 1f; - - // This is true when transitioning between: - // - Overview <-> Workspace - propertySetter.setViewAlpha(null, mLauncher.getOverviewPanel(), 1 - finalHotseatAlpha); - propertySetter.setViewAlpha(mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha), - mLauncher.getHotseat(), finalHotseatAlpha); - propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN); propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y, finalWorkspaceTranslationY, Interpolators.ZOOM_IN); @@ -176,7 +168,7 @@ public class WorkspaceStateTransitionAnimation { } } - private static class PropertySetter { + public static class PropertySetter { public void setViewAlpha(Animator anim, View view, float alpha) { if (anim != null) { @@ -204,13 +196,14 @@ public class WorkspaceStateTransitionAnimation { } } - private static class AnimatedPropertySetter extends PropertySetter { + public static class AnimatedPropertySetter extends PropertySetter { private final long mDuration; private final AnimationLayerSet mLayerViews; private final AnimatorSet mStateAnimator; - AnimatedPropertySetter(long duration, AnimationLayerSet layerView, AnimatorSet anim) { + public AnimatedPropertySetter( + long duration, AnimationLayerSet layerView, AnimatorSet anim) { mDuration = duration; mLayerViews = layerView; mStateAnimator = anim; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 9b64043426..eb2670426c 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -15,10 +15,12 @@ import android.view.animation.Interpolator; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.graphics.GradientView; @@ -35,7 +37,8 @@ import com.android.launcher3.util.Themes; * If release velocity < THRES1, snap according to either top or bottom depending on whether it's * closer to top or closer to the page indicator. */ -public class AllAppsTransitionController implements SearchUiManager.OnScrollRangeChangeListener { +public class AllAppsTransitionController + implements SearchUiManager.OnScrollRangeChangeListener, LauncherStateManager.StateHandler { private static final Property PROGRESS = new Property(Float.class, "progress") { @@ -122,8 +125,8 @@ public class AllAppsTransitionController implements SearchUiManager.OnScrollRang * * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace * - * @see #setFinalProgress(float) - * @see #animateToFinalProgress(float, AnimatorSet, AnimationConfig) + * @see #setState(LauncherState) + * @see #setStateWithAnimation(LauncherState, AnimationLayerSet, AnimatorSet, AnimationConfig) */ public void setProgress(float progress) { mProgress = progress; @@ -161,33 +164,32 @@ public class AllAppsTransitionController implements SearchUiManager.OnScrollRang } /** - * Sets the vertical transition progress to {@param progress} and updates all the dependent UI + * Sets the vertical transition progress to {@param state} and updates all the dependent UI * accordingly. */ - public void setFinalProgress(float progress) { - setProgress(progress); + @Override + public void setState(LauncherState state) { + setProgress(state.verticalProgress); onProgressAnimationEnd(); } /** * Creates an animation which updates the vertical transition progress and updates all the * dependent UI using various animation events - * - * @param progress the final vertical progress at the end of the animation - * @param animationOut the target AnimatorSet where this animation should be added - * @param outConfig an in/out configuration which can be shared with other animations */ - public void animateToFinalProgress( - float progress, AnimatorSet animationOut, AnimationConfig outConfig) { - if (Float.compare(mProgress, progress) == 0) { + @Override + public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, + AnimatorSet animationOut, AnimationConfig config) { + if (Float.compare(mProgress, toState.verticalProgress) == 0) { // Fail fast onProgressAnimationEnd(); return; } - Interpolator interpolator = outConfig.userControlled ? LINEAR : FAST_OUT_SLOW_IN; - ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, progress); - anim.setDuration(outConfig.duration); + Interpolator interpolator = config.userControlled ? LINEAR : FAST_OUT_SLOW_IN; + ObjectAnimator anim = ObjectAnimator.ofFloat( + this, PROGRESS, mProgress, toState.verticalProgress); + anim.setDuration(config.duration); anim.setInterpolator(interpolator); anim.addListener(new AnimationSuccessListener() { @Override diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java index 1fb56e74fe..3ce101414a 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java @@ -15,12 +15,14 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER; + +import android.animation.AnimatorSet; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -28,14 +30,20 @@ import android.widget.Toast; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.WorkspaceStateTransitionAnimation.AnimatedPropertySetter; +import com.android.launcher3.WorkspaceStateTransitionAnimation.PropertySetter; +import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.widget.WidgetsFullSheet; public class OverviewPanel extends LinearLayout implements Insettable, View.OnClickListener, - View.OnLongClickListener { + View.OnLongClickListener, LauncherStateManager.StateHandler { // Out of 100, the percent of space the overview bar should try and take vertically. private static final float OVERVIEW_ICON_ZONE_RATIO = 0.22f; @@ -154,10 +162,30 @@ public class OverviewPanel extends LinearLayout implements Insettable, View.OnCl getContext().startActivity(intent, mLauncher.getActivityLaunchOptions(v)); } + @Override + public void setState(LauncherState state) { + setState(state, NO_ANIM_PROPERTY_SETTER); + } + + @Override + public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, + AnimatorSet anim, AnimationConfig config) { + setState(toState, new AnimatedPropertySetter(config.duration, layerViews, anim)); + } + + private void setState(LauncherState state, PropertySetter setter) { + boolean isOverview = state == LauncherState.OVERVIEW; + float finalHotseatAlpha = isOverview ? 0 : 1; + + setter.setViewAlpha(null, this, isOverview ? 1 : 0); + setter.setViewAlpha( + mLauncher.getWorkspace().createHotseatAlphaAnimator(finalHotseatAlpha), + mLauncher.getHotseat(), finalHotseatAlpha); + } public static int getButtonBarHeight(Launcher launcher) { int zoneHeight = (int) (OVERVIEW_ICON_ZONE_RATIO * - launcher.getDeviceProfile().availableWidthPx); + launcher.getDeviceProfile().availableHeightPx); Resources res = launcher.getResources(); int overviewModeMinIconZoneHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java index 9e2ad982f4..c339634f26 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java @@ -36,7 +36,7 @@ public class OverviewState extends LauncherState { // The percent to shrink the workspace during overview mode public static final float SCALE_FACTOR = 0.7f; - private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT; + private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE; public OverviewState(int id) { super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS); diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index 6776150c9a..8521334fc7 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -19,6 +19,7 @@ package com.android.launcher3.uioverrides; import android.view.View.AccessibilityDelegate; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.VerticalSwipeController; import com.android.launcher3.util.TouchController; @@ -32,4 +33,10 @@ public class UiFactory { public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() { return new OverviewAccessibilityDelegate(); } + + public static StateHandler[] getStateHandler(Launcher launcher) { + return new StateHandler[] { + (OverviewPanel) launcher.getOverviewPanel(), + launcher.getAllAppsController(), launcher.getWorkspace() }; + } } From 3cf2d5093f17ef896d85f0d22a56dbbd59649501 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 7 Nov 2017 15:40:42 -0800 Subject: [PATCH 082/885] Fixing wrong xml refenrece when the packageName is changed Change-Id: I276d22e82dbef65eaa0f6c3331d7990292a62bc9 --- res/xml/device_profiles.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml index c582fc557c..30c1c54c0b 100644 --- a/res/xml/device_profiles.xml +++ b/res/xml/device_profiles.xml @@ -15,7 +15,7 @@ limitations under the License. --> - + Date: Tue, 7 Nov 2017 16:30:06 -0800 Subject: [PATCH 083/885] Fixing quickstep build Change-Id: I1db1ce636c52cb02dd248dd843de8c886ad34604 --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index 13540f4bfe..0dc7ff1113 100644 --- a/Android.mk +++ b/Android.mk @@ -135,6 +135,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ $(call all-java-files-under, quickstep/src) \ + $(call all-java-files-under, src_flags) \ $(call all-proto-files-under, protos) \ $(call all-proto-files-under, proto_overrides) From ea529083bd45bae8edcb86d0be056ff90921d0c1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 31 Oct 2017 13:33:03 -0700 Subject: [PATCH 084/885] Using view elevation for shadow during click feedback instead of creating a shadow bitmap Change-Id: I331186664c3c448596af3172e0e080921a6a1908 --- res/layout/deep_shortcut.xml | 1 + res/layout/system_shortcut.xml | 1 + res/values/dimens.xml | 5 +- src/com/android/launcher3/AppInfo.java | 33 +++-- src/com/android/launcher3/CellLayout.java | 11 +- .../android/launcher3/ClickShadowView.java | 129 ++++++++++++++---- .../android/launcher3/ItemInfoWithIcon.java | 7 +- .../android/launcher3/LauncherAnimUtils.java | 13 ++ .../allapps/AllAppsContainerView.java | 9 +- .../graphics/HolographicOutlineHelper.java | 16 ++- .../android/launcher3/model/LoaderCursor.java | 14 +- .../android/launcher3/model/LoaderTask.java | 15 +- 12 files changed, 186 insertions(+), 68 deletions(-) diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml index 4a2ad42254..4a3db1f136 100644 --- a/res/layout/deep_shortcut.xml +++ b/res/layout/deep_shortcut.xml @@ -35,6 +35,7 @@ android:textColor="?android:attr/textColorPrimary" android:fontFamily="sans-serif" launcher:layoutHorizontal="true" + launcher:deferShadowGeneration="true" launcher:iconDisplay="shortcut_popup" launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" /> diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml index 04f3d027dc..1888e2254d 100644 --- a/res/layout/system_shortcut.xml +++ b/res/layout/system_shortcut.xml @@ -34,6 +34,7 @@ android:fontFamily="sans-serif" launcher:iconDisplay="shortcut_popup" launcher:layoutHorizontal="true" + launcher:deferShadowGeneration="true" android:focusable="false" /> - + + 4dp + + 8dp 32dp 1dp diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index a5422aa7be..9796d18f1f 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -21,9 +21,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; +import android.os.Build; +import android.os.Process; import android.os.UserHandle; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageManagerHelper; @@ -59,17 +62,12 @@ public class AppInfo extends ItemInfoWithIcon { this.componentName = info.getComponentName(); this.container = ItemInfo.NO_ID; this.user = user; - if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) { - runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; - } - if (quietModeEnabled) { - runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_QUIET_USER; - } - intent = makeLaunchIntent(info); - runtimeStatusFlags |= (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0 - ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; + if (quietModeEnabled) { + runtimeStatusFlags |= FLAG_DISABLED_QUIET_USER; + } + updateRuntimeFlagsForActivityTarget(this, info); } public AppInfo(AppInfo info) { @@ -102,4 +100,21 @@ public class AppInfo extends ItemInfoWithIcon { .setComponent(cn) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); } + + public static void updateRuntimeFlagsForActivityTarget( + ItemInfoWithIcon info, LauncherActivityInfo lai) { + ApplicationInfo appInfo = lai.getApplicationInfo(); + if (PackageManagerHelper.isAppSuspended(appInfo)) { + info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; + } + info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 + ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; + + if (FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO + && appInfo.targetSdkVersion >= Build.VERSION_CODES.O + && Process.myUserHandle().equals(lai.getUser())) { + // The icon for a non-primary user is badged, hence it's not exactly an adaptive icon. + info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON; + } + } } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 3162286c1c..79a34a0988 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -385,16 +385,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { @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, mShortcutsAndWidgets, - null /* clipAgainstView */); - mTouchFeedbackView.animateShadow(); - } - } + mTouchFeedbackView.setPressedIcon(icon, background); } void setIsDragOverlapping(boolean isDragOverlapping) { diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java index aad1112984..5391b4d465 100644 --- a/src/com/android/launcher3/ClickShadowView.java +++ b/src/com/android/launcher3/ClickShadowView.java @@ -16,15 +16,28 @@ package com.android.launcher3; +import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_DURATION; +import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR; +import static com.android.launcher3.LauncherAnimUtils.ELEVATION; +import static com.android.launcher3.graphics.HolographicOutlineHelper.ADAPTIVE_ICON_SHADOW_BITMAP; + +import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.Property; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; public class ClickShadowView extends View { @@ -32,6 +45,8 @@ public class ClickShadowView extends View { private static final int SHADOW_LOW_ALPHA = 30; private static final int SHADOW_HIGH_ALPHA = 60; + private static float sAdaptiveIconScaleFactor = 1f; + private final Paint mPaint; @ViewDebug.ExportedProperty(category = "launcher") @@ -40,6 +55,10 @@ public class ClickShadowView extends View { private final float mShadowPadding; private Bitmap mBitmap; + private ObjectAnimator mAnim; + + private Drawable mAdaptiveIcon; + private ViewOutlineProvider mOutlineProvider; public ClickShadowView(Context context) { super(context); @@ -50,6 +69,10 @@ public class ClickShadowView extends View { mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift); } + public static void setAdaptiveIconScaleFactor(float factor) { + sAdaptiveIconScaleFactor = factor; + } + /** * @return extra space required by the view to show the shadow. */ @@ -57,11 +80,64 @@ public class ClickShadowView extends View { return (int) (SHADOW_SIZE_FACTOR * mShadowPadding); } + public void setPressedIcon(BubbleTextView icon, Bitmap background) { + if (icon == null) { + setBitmap(null); + cancelAnim(); + return; + } + if (background == null) { + if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) { + // clear animation shadow + } + setBitmap(null); + cancelAnim(); + icon.setOutlineProvider(null); + } else if (setBitmap(background)) { + if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) { + setupAdaptiveShadow(icon); + cancelAnim(); + startAnim(icon, ELEVATION, + getResources().getDimension(R.dimen.click_shadow_elevation)); + } else { + alignWithIconView(icon); + startAnim(this, ALPHA, 1); + } + } + } + + @TargetApi(Build.VERSION_CODES.O) + private void setupAdaptiveShadow(final BubbleTextView view) { + if (mAdaptiveIcon == null) { + mAdaptiveIcon = new AdaptiveIconDrawable(null, null); + mOutlineProvider = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + mAdaptiveIcon.getOutline(outline); + } + }; + } + + int iconWidth = view.getRight() - view.getLeft(); + int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft(); + int drawableWidth = view.getIcon().getBounds().width(); + + Rect bounds = new Rect(); + bounds.left = view.getCompoundPaddingLeft() + (iconHSpace - drawableWidth) / 2; + bounds.right = bounds.left + drawableWidth; + bounds.top = view.getPaddingTop(); + bounds.bottom = bounds.top + view.getIcon().getBounds().height(); + Utilities.scaleRectAboutCenter(bounds, sAdaptiveIconScaleFactor); + + mAdaptiveIcon.setBounds(bounds); + view.setOutlineProvider(mOutlineProvider); + } + /** * Applies the new bitmap. * @return true if the view was invalidated. */ - public boolean setBitmap(Bitmap b) { + private boolean setBitmap(Bitmap b) { if (b != mBitmap){ mBitmap = b; invalidate(); @@ -80,48 +156,51 @@ public class ClickShadowView extends View { } } - public void animateShadow() { - setAlpha(0); - animate().alpha(1) - .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION) - .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR) - .start(); + private void cancelAnim() { + if (mAnim != null) { + mAnim.cancel(); + mAnim.setCurrentPlayTime(0); + mAnim = null; + } + } + + private void startAnim(View target, Property property, float endValue) { + cancelAnim(); + property.set(target, 0f); + mAnim = ObjectAnimator.ofFloat(target, property, endValue); + mAnim.setDuration(CLICK_FEEDBACK_DURATION) + .setInterpolator(CLICK_FEEDBACK_INTERPOLATOR); + mAnim.start(); } /** * Aligns the shadow with {@param view} - * @param viewParent immediate parent of {@param view}. It must be a sibling of this view. + * Note: {@param view} must be a descendant of my parent. */ - public void alignWithIconView(BubbleTextView view, ViewGroup viewParent, View clipAgainstView) { - float leftShift = view.getLeft() + viewParent.getLeft() - getLeft(); - float topShift = view.getTop() + viewParent.getTop() - getTop(); + private void alignWithIconView(BubbleTextView view) { + int[] coords = new int[] {0, 0}; + Utilities.getDescendantCoordRelativeToAncestor( + (ViewGroup) view.getParent(), (View) getParent(), coords, false); + + float leftShift = view.getLeft() + coords[0] - getLeft(); + float topShift = view.getTop() + coords[1] - getTop(); int iconWidth = view.getRight() - view.getLeft(); int iconHeight = view.getBottom() - view.getTop(); int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft(); float drawableWidth = view.getIcon().getBounds().width(); - if (clipAgainstView != null) { - // Set the bounds to clip against - int[] coords = new int[] {0, 0}; - Utilities.getDescendantCoordRelativeToAncestor(clipAgainstView, (View) getParent(), - coords, false); - int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding); - int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ; - setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight)); - } else { - // Reset the clip bounds - setClipBounds(null); - } + // Set the bounds to clip against + int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding); + int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ; + setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight)); 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 */ diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java index 1c4e88b5b0..fea4ddae8e 100644 --- a/src/com/android/launcher3/ItemInfoWithIcon.java +++ b/src/com/android/launcher3/ItemInfoWithIcon.java @@ -67,7 +67,6 @@ public abstract class ItemInfoWithIcon extends ItemInfo { FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED | FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER; - /** * The item points to a system app. */ @@ -80,6 +79,12 @@ public abstract class ItemInfoWithIcon extends ItemInfo { public static final int FLAG_SYSTEM_MASK = FLAG_SYSTEM_YES | FLAG_SYSTEM_NO; + /** + * Flag indicating that the icon is an {@link android.graphics.drawable.AdaptiveIconDrawable} + * that can be optimized in various way. + */ + public static final int FLAG_ADAPTIVE_ICON = 1 << 8; + /** * Status associated with the system state of the underlying item. This is calculated every * time a new info is created and not persisted on the disk. diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index dfe51af868..9869fdf7e2 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -164,4 +164,17 @@ public class LauncherAnimUtils { view.setScaleY(scale); } }; + + public static final Property ELEVATION = + new Property(Float.class, "elevation") { + @Override + public Float get(View view) { + return view.getElevation(); + } + + @Override + public void set(View view, Float elevation) { + view.setElevation(elevation); + } + }; } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index a1f37ba222..81f58423f9 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -157,14 +157,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource, @Override public void setPressedIcon(BubbleTextView icon, Bitmap background) { - if (icon == null || background == null) { - mTouchFeedbackView.setBitmap(null); - mTouchFeedbackView.animate().cancel(); - } else if (mTouchFeedbackView.setBitmap(background)) { - View rv = findViewById(R.id.apps_list_view); - mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv); - mTouchFeedbackView.animateShadow(); - } + mTouchFeedbackView.setPressedIcon(icon, background); } /** diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java index 9e67f56e3f..fdf2d679a9 100644 --- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java +++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java @@ -16,6 +16,8 @@ package com.android.launcher3.graphics; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_ADAPTIVE_ICON; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; @@ -29,6 +31,7 @@ import android.graphics.drawable.Drawable; import android.util.SparseArray; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.R; /** @@ -37,6 +40,12 @@ import com.android.launcher3.R; */ public class HolographicOutlineHelper { + /** + * Bitmap used as shadow for Adaptive icons + */ + public static final Bitmap ADAPTIVE_ICON_SHADOW_BITMAP = + Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); + private static HolographicOutlineHelper sInstance; private final Canvas mCanvas = new Canvas(); @@ -63,6 +72,11 @@ public class HolographicOutlineHelper { } public Bitmap createMediumDropShadow(BubbleTextView view) { + if (view.getTag() instanceof ItemInfoWithIcon && + ((((ItemInfoWithIcon) view.getTag()).runtimeStatusFlags & FLAG_ADAPTIVE_ICON) + != 0)) { + return ADAPTIVE_ICON_SHADOW_BITMAP; + } Drawable drawable = view.getIcon(); if (drawable == null) { return null; @@ -119,7 +133,7 @@ public class HolographicOutlineHelper { } public void recycleShadowBitmap(Bitmap bitmap) { - if (bitmap != null) { + if (bitmap != null && bitmap != ADAPTIVE_ICON_SHADOW_BITMAP) { mBitmapCache.put((bitmap.getWidth() << 16) | bitmap.getHeight(), bitmap); } } diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 47f370a175..ccef9b7c7d 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -16,15 +16,11 @@ package com.android.launcher3.model; -import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO; -import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_YES; - import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.database.Cursor; import android.database.CursorWrapper; @@ -36,6 +32,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.LongSparseArray; +import com.android.launcher3.AppInfo; import com.android.launcher3.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; @@ -52,7 +49,6 @@ import com.android.launcher3.logging.FileLog; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.LongArrayMap; -import com.android.launcher3.util.PackageManagerHelper; import java.net.URISyntaxException; import java.security.InvalidParameterException; @@ -206,7 +202,6 @@ public class LoaderCursor extends CursorWrapper { return TextUtils.isEmpty(title) ? "" : Utilities.trim(title); } - /** * Make an ShortcutInfo object for a restored application or shortcut item that points * to a package that is not yet installed on the system. @@ -279,12 +274,7 @@ public class LoaderCursor extends CursorWrapper { } if (lai != null) { - ApplicationInfo appInfo = lai.getApplicationInfo(); - if (PackageManagerHelper.isAppSuspended(appInfo)) { - info.runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; - } - info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 - ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; + AppInfo.updateRuntimeFlagsForActivityTarget(info, lai); } // from the db diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index c2cfebb85f..310416f0b8 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageInstaller; import android.graphics.Bitmap; +import android.graphics.drawable.AdaptiveIconDrawable; import android.os.Handler; import android.os.Process; import android.os.UserHandle; @@ -35,6 +36,7 @@ import android.util.MutableInt; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; +import com.android.launcher3.ClickShadowView; import com.android.launcher3.FolderInfo; import com.android.launcher3.IconCache; import com.android.launcher3.InstallShortcutReceiver; @@ -52,6 +54,7 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIconPreviewVerifier; +import com.android.launcher3.graphics.IconNormalizer; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.logging.FileLog; import com.android.launcher3.provider.ImportDataTask; @@ -145,7 +148,9 @@ public class LoaderTask implements Runnable { TraceHelper.beginSection(TAG); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { - TraceHelper.partitionSection(TAG, "step 1.1: loading workspace"); + TraceHelper.partitionSection(TAG, "step 1.1: loading UI resources"); + loadUiResources(); + TraceHelper.partitionSection(TAG, "step 1.2: loading workspace"); loadWorkspace(); verifyNotStopped(); @@ -208,6 +213,14 @@ public class LoaderTask implements Runnable { this.notify(); } + public void loadUiResources() { + if (Utilities.ATLEAST_OREO) { + ClickShadowView.setAdaptiveIconScaleFactor( + IconNormalizer.getInstance(mApp.getContext()).getScale( + new AdaptiveIconDrawable(null, null), null, null, null)); + } + } + private void loadWorkspace() { final Context context = mApp.getContext(); final ContentResolver contentResolver = context.getContentResolver(); From 758d5047be84894698669c6bbc42822ff9e2cf08 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 8 Nov 2017 14:38:23 -0800 Subject: [PATCH 085/885] Refactor InsettableFrameLayout/DragLayer and add StateListener interface. Bug: 65387919 Change-Id: I5eb94c0f3962d44f63af3efc92d852e019bba711 --- .../launcher3/InsettableFrameLayout.java | 12 ++----- .../launcher3/LauncherStateManager.java | 32 ++++++++++++++++++- .../launcher3/dragndrop/DragLayer.java | 7 ++-- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java index be7649013c..60f5ca2925 100644 --- a/src/com/android/launcher3/InsettableFrameLayout.java +++ b/src/com/android/launcher3/InsettableFrameLayout.java @@ -9,8 +9,7 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; -public class InsettableFrameLayout extends FrameLayout implements - ViewGroup.OnHierarchyChangeListener, Insettable { +public class InsettableFrameLayout extends FrameLayout implements Insettable { @ViewDebug.ExportedProperty(category = "launcher") protected Rect mInsets = new Rect(); @@ -21,7 +20,6 @@ public class InsettableFrameLayout extends FrameLayout implements public InsettableFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); - setOnHierarchyChangeListener(this); } public void setFrameLayoutChildInsets(View child, Rect newInsets, Rect oldInsets) { @@ -95,12 +93,8 @@ public class InsettableFrameLayout extends FrameLayout implements } @Override - public void onChildViewAdded(View parent, View child) { + public void onViewAdded(View child) { + super.onViewAdded(child); setFrameLayoutChildInsets(child, mInsets, new Rect()); } - - @Override - public void onChildViewRemoved(View parent, View child) { - } - } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index f016e8d2e5..3b58ed8e04 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -25,7 +25,6 @@ import android.os.Handler; import android.os.Looper; import android.view.View; -import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationLayerSet; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; @@ -83,6 +82,8 @@ public class LauncherStateManager { private StateHandler[] mStateHandlers; private LauncherState mState = NORMAL; + private StateListener mStateListener; + public LauncherStateManager(Launcher l) { mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; @@ -99,6 +100,10 @@ public class LauncherStateManager { return mStateHandlers; } + public void setStateListener(StateListener stateListener) { + mStateListener = stateListener; + } + /** * @see #goToState(LauncherState, boolean, Runnable) */ @@ -157,6 +162,9 @@ public class LauncherStateManager { for (StateHandler handler : getStateHandlers()) { handler.setState(state); } + if (mStateListener != null) { + mStateListener.onStateSetImmediately(state); + } mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); // Run any queued runnable @@ -210,6 +218,17 @@ public class LauncherStateManager { public void onAnimationStart(Animator animation) { // Change the internal state only when the transition actually starts setState(state); + if (mStateListener != null) { + mStateListener.onStateTransitionStart(state); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (mStateListener != null) { + mStateListener.onStateTransitionComplete(mState); + } } @Override @@ -304,4 +323,15 @@ public class LauncherStateManager { void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews, AnimatorSet anim, AnimationConfig config); } + + public interface StateListener { + + /** + * Called when the state is set without an animation. + */ + void onStateSetImmediately(LauncherState state); + + void onStateTransitionStart(LauncherState toState); + void onStateTransitionComplete(LauncherState finalState); + } } diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 8189b236e9..9f9822c4c6 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -703,13 +703,14 @@ public class DragLayer extends InsettableFrameLayout { } @Override - public void onChildViewAdded(View parent, View child) { - super.onChildViewAdded(parent, child); + public void onViewAdded(View child) { + super.onViewAdded(child); updateChildIndices(); } @Override - public void onChildViewRemoved(View parent, View child) { + public void onViewRemoved(View child) { + super.onViewRemoved(child); updateChildIndices(); } From 8ae4198a448ab308aed95756e82da82ce42735a7 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 10 Nov 2017 11:42:13 -0800 Subject: [PATCH 086/885] Adding some task views. Bug: 69166452 Test: Build quickstep Change-Id: Iee4cb0b9ac32716f588082a197a90ec1b2655047 --- quickstep/libs/sysui_shared.jar | Bin 75304 -> 86425 bytes .../drawable/task_thumbnail_background.xml | 19 ++ quickstep/res/layout/task.xml | 35 ++++ .../android/quickstep/RecentsActivity.java | 2 +- .../android/quickstep/TaskThumbnailView.java | 185 ++++++++++++++++++ .../src/com/android/quickstep/TaskView.java | 88 +++++++++ src/com/android/launcher3/Launcher.java | 10 +- src/com/android/launcher3/Workspace.java | 1 - 8 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 quickstep/res/drawable/task_thumbnail_background.xml create mode 100644 quickstep/res/layout/task.xml create mode 100644 quickstep/src/com/android/quickstep/TaskThumbnailView.java create mode 100644 quickstep/src/com/android/quickstep/TaskView.java diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar index f9ce6e0c9ec99a23523e2038dd1083efe112de93..a76f4f9e0e2e8c39a8bd4e280217643e9d7f4657 100644 GIT binary patch delta 35327 zcmZs?1CVA-vo+eb?VdKK@w9DC+qP|Mo@v{*ZQHhObK02po%j9Df8%`ry&F*(wX14J zRAj8om6cgK36WL+iKrkA4uJsz0|Nt6rDqj~$PNBaBNg^fQ@2b8`cIo6$I6yKOHB+c zQqxw)SH<|0V>X-zK$0Y?*)*vw3JMo5Z=X^uDa|(SkxHHosdi9~S>oWCIhg&ri|}Z& zrqLo^Ea88c`Z}opX-)~7r=VW--Fn(|W_#!F{gycy(F6QI?A7$CIPih{Xmx<`ZA~3C zflG1FL{#^3#Dr{WFFuISBj``Dat7)mD${KCHb&zT>Yx4564cjCtg!nA87!5o{H)Vq z*WRt!X~*Mwri*pjX2j#BfWn#1^~2CcQZU2m0^*c$Ejm+Mze=jcNHrr7A$c=>NvEDy z`?t0*y+*UI+>t>StpNt1gqO1H#4|-v803s^J%vM+inIDuvjA`c`S85m_7ymhOm$IA zWjzmzGx#3-z;4rqs})5%IOkBd|D@F!pG-VN3&_OfNj-EBffA#N-6pG`wExj_yk_ku z9YLa1sezxo)|eTriNw2MN$s$r*bIivY>#EZ<>R;!ov$n@t=Q6p&H2VoJ-Iy4j;R55 zW~T<9tezm-&8LG@xqhH&O%Igu(YVi#&bWjnY{BV$NJ!U#w^_M$O)S0QZm3?Dv!BiA z4cqdnz{t>L6aj<9J&Wqpi;^3^;vPt=Q$1SnzDQCJGlko!pNE=HWdr;Y@?#ksZ2{cb zM*0NC*&7}{8v)Rvjsf-tpgRB-sO=GGC@iMI?yVhisPR&q)+!x_Jz#Wn2h`PGckj~I zQYqN=W%G)UtJt!C=oB!}N4bEHkQ`83W3N%P^^0~Qy8}0CJ&s}ObojI}Mv%7vFMp&8 zDtYR6T-QkDLhPCB43f!7rt0Ph^WrwJA7fLnRMzi;bsd@$*CY8rn^tm7<`#qsIyBt4 zGJYDtW(gzCga<}X2$+`d}x^p8WxPTe3L>n5;u~j25$K9ou-tigw~4uspzt2H!h}P=o|tE?^bo zUY%LKOAG=}dmjV)+far>F|+{wem_^4vn_B(pqtcXu{&fAfP@Rme?qm!ALPNTGO2(< zM(iA=zjLWle38iB5ZZg$S?dA{!zCwRf8#2_ZjSxVb;mE`Vv~RWOmx5-zSTSxN1pYY;rYFsr9TQEevx>Ul%1a&mXn&$5<$LgH#n#F|7SOsrdw(=vKVGcdzvT#g zA{gND&>jopjM!qXfo{U~#k`OJ=t@20hu%mKzG3(4`g{Y1bP`~RX!O@(=)@g%2}R}t z0!gK4ucV*K8=v#SV{j7lH>g=GxE3(nnuiG`I1&6XH@w~hNnFgkQ|S-Yq$VJP;sc%o7}8N*nyVCg)sh^Z(kD%-cKGpj zOqlKixw|#Tt`*tGW<$-9QtO`kt@+!4`Dv&YF$!DwtWB=7&AaB>7R0&(p{P+-luVMV zLmBne3mvKe_kx#Z+z3{397A)XkXrfT4>ivO!%AR|*g7jh!k)*B85K&}asI?(NQBQs zK>0rC0Em0~IRg5e2C#u-f^KcorS*!LJipDZ4Abr+KlyB6e2BjoP2<4r5pCe1B9Gn+ z|2c|%eJolxEx=_(a-lfid!$^YbpluXEl}{&wobJG4WV+yV=1qng7rSewT=Q2RhA?; zTn!jdh>hU+r=SZ~eoU)~do2eG@x&q9@Hkei-yw)qnSM$|n`LQHrioR#D;oj%mm82l5aWiKiJ)Du%A(8FWsdTqK+!aw#h0UgSwl*vT&kCSwW9b1=UV#ps zl?MgQs?(>9Fw(BH$uN4`?JA=>H^dm)egq7kj+>`HVZeM5Pw`;(>J3`m2{UQI@m)lD zLa$+Q!Icz!`$6n67yaCGSJB0pp(^#;XoOv7j#aAk&69M>(6Cy&a29j1mfj;sl&)dj zB`^QBoNdi{NsH~JhkHrj+OBr1#XLWR=^IG}8u8vEo@ex_jp5H}Z?Q!>+cqrBu{Gew zvmSfP59x0OMj3k#J>v?bu5D=W&8qgq<3koX!DtuU=|^|Pv!=w{^Y;*O4X1s&()nIZ z6&ndr+Z{I?U`GU90|yV06|D06YH$w+xYJ7gC9DVhl@ z)*Xz?<&p_aNF}YIVFc5^p^z*6fqsC-(BdySPY3=u^vvzxPjupgji3(?mGB!FNlSaTc%E6=}o2GX*UECtW1pBj$~PtzgEg-nqn{01sv27a61SHih&_7+C` ziD$fH%=tnv;^W~Lx7N!6Q=fcTT8}S~l!>RFZD`Yykfb@zOWg~zmpqyPaSI1JIm;=r zXT}sjP`ytine0di#&L_%qEG@Pgsl_QcgKB|Kd|+06jv*`MXCxMN%#)7CiP2a%EOgZ z{W@MXrzO28X-C`CLj9JRNF0zam+1+c6K)MuK-HrvrmMg&bXk~~rRSDOvkXd9m7xwe zoR<&zF2E2Zfo+&aMDPy(4-~p+@R@HwfPj?4{TCFX=vhgF{I82M0hO5wC@Jq|>f~l= z>aO5q@8QW{Y-8x`oTS(%H6V!Mt88p+T(NX6c5|Gkr-PCQZ7&rhSY$N!#*XVM-TuA9 z(grvcK)fpvmXR#gd1i>ao$MINwH#Zh$Sx*1$N||LW zQHt+dvAP|K#0U z(ZT&K>;gkyU@r^%7Sw_g-4WnwmG?(hCH?!?=%h_Q2d%L%6%m$=^p;cankZJh!)M9T zPcGQs^WT}va)umu_Xca6TBiYeL>HOA6J*{Ox4-V?)lPWJ$+zzpe;mF$M{)Zp+}jTT zCFs8euHKdIDehVyrrEMqM5?z)(Yq-;v)$>8*+WIy|FgG_;8?AgpdcVif8YN$Gk?8N zSV?71XBSgjRm&6u3{b4U21nEWAIEffBQi^Jozl_2Lm@fQERWsRuUQg1ofTB1J+}2s zoF?JcmbZ$}*lbv~pY&D%3pNZ}PY_esKO|Qw3C>^G6C~SXmjiR$y@rsA#Ezo3eunRP zvTbMW`cF*H&ritS1`p+tI1Ki^AaeHoQolKjVF#IbM@dR04^$vy6G=?U(G#X5Wx{Fi zEqi9KBw#=cd;u5>uC~=;;nFAp7*>{ME41XvtH~(47Z))A2K{NXu=g(Wb4!4 z_C)|d0SWstCTy+YyyH-qmZ{nwNb6=9$&*5J?IYX?_rq?I$RG-;U1q{%GqzOE70geK zH8?OK%lE`fp@Eb64yijJF-){5fxDwRP{8@lkK-J=BDdq#aNo4fTV z>K=EzO+Ji`!XwVG9%U9m$-&i2l02q!N%b-S-mWd;6I2?y&K5%!#&VbWSSHayOkzzGQbzR}yxhEhfD19? zQ=P+lwRJv#%Dkp_^YPCoMPab)1zZ`)R(*~}Iwmk9DE@kCrNsT7Ht=<7j;(D>}5Fy|jmUd)@1i%oU7F<{{vaw^-5 zjgKDSEG18ZJ~!T?_8fa?QXYzZsjfj#k-ft>L^UEBGDpiSssRG|sA$p>y z2ejG3%VqgcWc+w?7IE`SBs7tpg73z$3n9$n4@i*|tW@;VcH)PY~&Nx;Bx&Wac zY+pB*rpjA3>%P(HEP&5JB*K|{e8%|A6vATToam4A*TozX;e5hYsul2O&3iR&osjFi z06ZK;+nT*^AN+;s=YVFXEtv}^&gQ{GAOq6(56-?#uCT+Me^7D-GEd|Z0v!Y-1@`}j z$-mw>@c)I$e{BxAvWu(He+*BS+J-W^I?h*?NdlrF4IC6CMw1cZ9vY??q&<$1F{%Yx z*}R%gKUl;V!S7hw2>VI=75EBEI0efY!+A7Qic4gtp{E^l)+2ItS~o}5 zyt0-~O^4+KOQt8EJh1osZ5Xkr>N0C5!krez^ld*=zykx_;gW?E_~*M=<-hT;Mckur}>%`tnpOwoFYa zBf93;o2B}(i#)bPwIL}gp>bO;Ey5J`17yn5q;#%-URucp+4KEyy?Ons3Y|k%lQafC zn`siO{iIAzr8d^$@gK|8kPk4b?YeY`Xr=lZ(zO=KHTC4niXTt5iHo%`$^&S2DMMc+ zMt&^U7PjVMA3%N#95F;O4Llnzx`Sva=}!3z ziKk}oyHC{~j0WpQMe6c6m)bp+%^^K_J#R}Xsb4egRit7gpK%xMs-jB_wpATmCsp^* z(Z=SgiMds`GIJ*MPwaBAv*?xG?K`FG$}PQ6J>U!zAFu$S*JDwbVugJmy#+Uz7F=ay z_N4U*-y;1&#mr>Ye%2~nB75rQYRf0b#EAPl0;|o8Hf|{=qxo>DG4{%x1GU~kvYYA} zA5({l9sb<;Fp#-h%vb^C&Rgcfbr z7u0p;KvkHh=E69E8A zHa>;zby8e7t-LpR74lQ1uy-V3PhQ>$*EakvBp$SgJPK9-i6<+{lf)7+*{6njoMI9c zP{AKu5!k>#hZi1XY(#E?`H_LRMy zdWTR~a@MDi$oGFkgQwZwBfpDO8Y>QNU_wEptVGmzt z*!{4=|ZDKf?|z{==Ah!(0B8bQkLDBVBgw%^hN=q`;X_ zM$FIGSDUOKK)hVX%EgEd25!r*)(_p}*-`IywYTSdau2 zE3qm@nQp8&3diAx*CG=s@3kd$I=xU=BEXnvuPj7riV+ zeBH-%ySe-c;LKkad;QeK2F)92<{su5h7jt7svxQe^c`=L?RL*a)_tNGNxpq7dsTQ zVs%6PD~J)6!2LU6GM6nORtU2}mihBPE6A~`1AP*gP`{8n##%aFoKWsSCyGD=33~Gd ze?g$YzD;;`SI1e$I4&bcG=!y0hY&pYHGySC~N*b(cr?nJ=f z0Dr|ov-zwA@bO!$R_u`&`ik)rE!WP3yg!Uf_;_H%yv&BP<2Jq35<}nJa3aBR74E?@ z2Jcts@rKpB2y#~L(d_ZkAL5eCz$f{@G-=)NR zuANnW=S>yB;53=u*Q&DC{WI0MIJ$;)dP)d?K1f?zLq;nev9AZ-@<8?5Q z-z&f6P(@tiUl;{?@-5Gtxh?w}%x{}eWw4k>U3Yhw5lPQPv64^iiuXsp8qlGgu4Y-t zkyrf~M1&rjYjI1hcZI}FOh6M z-aL8lna4NrUM!f1)%L7elo9rO&6qIK-faM)vrgq>C@x8!w5JR(I_(CU*;to9;%KZ^ zPH{;v+|6(TsDz0*w^{iaKmwNTZ6r!Cl95fiDz6xOIXX;4Q%)mP3MV1nX-Pl&{9MLWW%hJ#LFAh}6KCE)Oko&j zd3XaKa4O8GdR0shjjDHpaYv7&!D62d0!cIThdZ1GO+C_NCx116;E~6q17|`)WF8tv-ok8LoOf+j|l`9yV>Y4Blt)o-86z z-SeFoZ`(*fQ@hfD*0Si}>2)DgI}X6FjAKs$pvXz#iB;I z7Ac|eM;bPEW7jEiB2@QtbyFT^5;G^XzL}{s{lLH?Bp$RPl&*^eROZz8YuQaE8W4W< z_rQ|LD`dOUjfio72DHG6yb9JwBO3$-f1P_kkFa)D9B^&`6GEXZ59h~Zx$ut83t<;T z^n{1JXWqD9sO8CYOMGZ&%VJp|%jM{ME>r?3v!ImgWf5vh9Q z$SfM=j874=rZJSHj9u|04qUk-C{J1AC>=`ARK$F(JQmowQ%El2|26 z##qHlKx8q2EHWfAxT}0F-1vrB)2K@&b1sU$!E&ms34q@ix#h+bYG=8+e$FQb?(~7cki0;E59Awbv{$HzjS%_ zZ)RhPeW+a6jTc$FB$vF^3wfP$E%EYKGo=4WSDbUYl0>_L4O)8mVZf*6 zNP5_ioUYyO6v2BI@1~FzwG#(OHR=)XargT^xB$D66hOeZ~a0fjnDye%r0rAIXe~DNh>~7|0 zlsUB{XLXrFuW#-0#_hHi{73MJKf><1+Y^}9yq1cq=6~n=@(4dNdVdOd<&bMu(E#mj z6XSg2Dxb~I6YcBFrhaY5^Q|*U>JqB=jMkAAFU+ON(&MM(R zf!QBDwucm53^weJSPzbRwas+;CF>3xR88DVjEwb#D6UVTlPBtR-?R50VwKfe#Em>< zNHJqZGEMUROsVkwHv=V7iu)ybvaB>%c@kL6xc3ezjBlg0AC^XzhDmQV5+x=kVLB4S z<%oY#{hFkCYj)3kJYHO?+WUe!JlVEv0Cw;t_UW2YlLwW!uc4lJpsA^oTx!lN{*+ff z2{$x7`d}TYjsgqpx1fz*+P;n6+v3Vni!0M$-K@-Zrc4P@IIeE)l<2%$0gqQiQ)2eW z?5Zj7ooL)o0tetoIf9;p9Oi1QQxaiHj|}t#6gKRI*ahI}Pk!BJXihoD6uF^r08LW> zA1J`8fCzOEb13;x>@F)_q}KeC&i=k(}XT z3_ONFF$jBi=YCV$iR1y04MN{}fzfDJMNYb^>3O(q?hAN^c5bYqQa`J-DcldIgBp=bWu zTEf!RuV)v=$^F3e6N|=pgQ>c`_QKf5H7>6uw=m$qy<^=GD3`B!hHw@PIUY zpapykeF_8Pm$dc_a}csnv93bty$MUu6!c{X`e%DI<;7VHgi8Qg4sU1Eb7*lJ2fWhnF#i`)!5==?ilGK7Miu zuLe`vjClE=vJ>@+Uy9(^hVFt?_c}Y=ptV@nd<$Bs#kKb{M$x80R-gB+ z#9Ntn+x*e8&%7R%ams(08{*g?yqytQ26go$T)W$M#F|yNHHc>iBnyz=0wHcr2$?}7 zi*fY`kw+3Sm(`O*(2x&~9Hbb-@ z!s0iEx$p!}_f+X$AFfL}!mYGMtUjDmSY8c?*)6~c=5nFoAHHA}0!l>QSm0Ohug0Q2 z*P{vXwx>%lcvx}(`TeX{uZ9}oSN6H1yVe?USMF^X_{TO}x6`sa{7&ptwrRM7Y4G@q z4Bwaz(YqqN5OXLIIi^b7cggY~Wnv5gEbas%f&)|U=H0>C(Z-Yrh&gjNn)!_gFRC4q z;ym-#o@K$>VNF<;rY@Q2@g*nxyS7R8{2?XO5>oTTnHr&N#{yY(2~2Z3J6)19h5f;htH0XGxEfTpTXy&=VL)xtZ8iA6^Kop51m*z;(GAm zi*RV|(4fYeQNBH~7wi)<+o*zn z%J9WTI@tgyeiL=Bk;0TC8s?oy`<$qv1Ub!2$V+o>UsY3ecpQ9d<*V$ zcq+R_n{(=(liqp=kI(}?DiL+<=OKAbi2<)ELbBg0VN|}T7l83+8G5{*n#J~G2l^(j zHT>&RUz8hPy35ouK! zUb|!8YXGl;Kc+_yDnq83{4g7M?`ntHZ;U%;y8;L=W6P9@7?6Qg1}%}iPKO)JcmC17 zcYO|y-6_fQ-|KaD_k}ex_lJzjQzAUlQ%V|$A$SKSSUi(?!GyoEBh0in)LYw>%vA46v0m}w-bt}n+C7+* z72N?#J4r}SPc1;Gg1Ib@lGMm%58h0hC@AGJKymX`GGeaH4*Dj3WfaswhgHY?*bg6d zaOI!Yq}fu?2WPf@rv^AY#@haGfu`Q%NoIE!d;w=a8k;H75Rk^0n?2h}JNEk?4A8XM zBPAAAw=+VQpbqj(NYTn=5sT*cRt*Z-n|M+YLaY+@wjF@HS>;g_!h04~1S_)$)sRd@ zqocRzd-eQivLSVuYluIaH7?|eqtG$L#%c{JHT!1QaKa?!r*?Jp>Y~QB!)0ZHe#d#V zIs=+nGB1&?B7=t(!?q1vv;nwrZEY6v*U`PW-zmwErrDMDT#_RgzYFMFg;9ET0TMZI zqbZ((g93pxT#BEFIiY&_1U$@eo7!u(VQl1QNjP9mth81k7?_l6eV z&jx>Syv2vDu8Z1#1v^MVbLx*JzE)1v0v08CTq*cXxp7gOgzF7fzl$g!Z^|FIVXg!zAy|v0N{V^Nwy0`mG&@LZrq^>AA z86&tLrM2x!Je$3uyY4M{81C={nHpK#luy`KHp!IV1@ zeVxAx4-@g@NGkj0kZhk4tz;3tCUb+|lis#~HCKGEI7xROe$PPswJJZf&@~~|(|kXm zSC?StF_gL;t6mfp{Z-ud@&88Ha%SVf_14#2_+B@kqWbPqCx!-P`aD=byO*p3r}S`qI;z_lS!N6d_3p6^h5$PyCZ#gu@zFq^Bo{89uxG$+ zvyS}>fjZ*svJ6LgeKiOD_Z_Rid?Y+cfm!T z*?(I1F>l(>q?OTs()CI?Vi}Pd2A@XQb}0Wb z6%AaY{`E%KU!E>t6=sG{A@}0S=ySUVzoif;y$4!Teo&dY@?(ebLp`FQS*~*eM{Q?S zUD%XA4(<7&bfs576;{a6ECbCQa>+8cvtWwgvO106J|Jqi0~n8!p!EA6p&%_wgqZMH zTmA46?fL_*3f>Uyj#Zg;ZD&!vdUvs2+xx6rlg{$P)@F8_I;V5B&vJzrXP*)EX5E`Z+6Y(qg?(F{OS`taLwr6#Li(6x z&FBw&aE@n+aW7J@#X|Z4g%3Du6vR6SQTNqz5uZ zhYyH6kan3k_!s)eP`6s1WIHe>FX!a&(B7G|l1FpXfzMG8!=9$u;75_`5$#?asb>}H zfO>fkfSWzHIrVRq$ceN8qxYQ(sS;WV({vylH}fR`X!5eDqkbE4&g^X!#b8=gUG9%OeMWS;tXX!^Lp_2`U&J_#ivpPhRYK zgh{;Nhds(>rGicb?EVSB32IXNnW*QbXMJ@EGkh!al#7Xjq7d)xnDM&Sf=z%g0CmP6 zt{%`0OPfV@me=f|?_bvRZoFuPU-2wh$(|9WlY01zvr>&k4xFnQnt2_txQ;v~SFCk*cib+~IyAu+MVad{%!!KJ_@pCTj4R z@^zr;cX@=^2-YS1Q!%=vQ1t5PAr{qb=rjG-g|y>8S$ag(1;M=g7{W{RaJPK;?DLg*Aq|G$ZtqHKRKB_n-@fndhRU z*u1{+fkU@yT|JzfFous3CQ9@hv<s4S~YcJ>HH2|urSEci@QtN|OQ68xcte1rw~@BZ>U&w0D>-KoZq*QxvTIv!+u zbJHc*yJ&~|@}-I123^P0yfPUi=5S%P#*ov)1In5zU~col@3P8rhC1W~#uQ!b!Yj2) zTDH8QX%SdF1!1pKFDY6p-Nxuny&p}D2J&SyCCgCLjZb;cY!SS;SlCn@2xbn4S zIz!EP`LBZZL3;toAclT_LKEf0F&n9JB?mIj5$F?{5Nopmw)@@WBF+j>jE+RVOpP2d zZ;~7wVC!6b+y6X?n&NMO`h}mac#M#HC|d^Qx6UZ=4M=gmsl{$8$jWhii#kpM^yLh= z#g;&+g^S;JOa|)dLT!2ukP|S=K@&I5TIAZ9 zVmA`H^aEl{ITLvwak_bAchCpo@i!XG6P1OTxhZD$qZ^CoIboU~3rj4<9lrz;P&>iK z@9bjpXA7Uo$*uQQ$x!|AbE@18)FtS(V>+9R-&<+zAHWOP^e^{RvuA>bUUY#d$L2qH+(kYmThVo0A7rsuIv*4+WmY6Ne7 zudBIV(vZQ)Hfp^|aTC{ELe5DLK>IRkP|dVu)9i4}$&+V@%=~nTqM#MEll!;#Y_di{ zF`3@8XG=BE>jM`**^^NrVkBR#-B@3K?W{eH_ii$ke0R?G;BCivtW}2=sI!Y9GE@&k zH;A7ZeFzLhxSfAX41^T-mM842WPMNwc z{w4DXyb*FvG<-uxF5%j@b_FG#EPMgq-<};3e1aDq2(CwDpV_^Hg0|pC{cU3(kWBnj zv`9)!{Gpi_7f71i0?SF3fFL*fSi7m8ue7s5Q6AZ8Jp5N+;iIFmwUdEN;v9XXmEzoy zd_Lfp&e;9Fa3uzL9R^(>*M3>m;%d_;-##eC<;wTJpSA#;6wzx!AdzG7*B=L>o$P4l z1!u?Le<#mfheFT_K(;-U=?|8@R(4c1Ts2Rx8)h{Pa9cx9$46cj1N)sFk~hSjTTi6{ zzf+cW=wD+_5ikMjUGmY$4)3^Hd->kC>VKrizd)F~<^38|{B)n;zGeC$c%&R<6x{#{ z+heXcK=;jlPyzrefrD-akNpFOyT+=jw9qb>gbl)#Kayg5ARsv19x7(X~R&M$v@kD=05 zJ#ENFI^<54(n&0@Q+zKMFU({NO8>-~=>eDOLMYSET{%SDu(~HN9 z5|eE{jyLk?29B4{g1$MHbIMHr`dAgh=ly~@@%p68KA|T53tB_cTrpVnh+IM{6LJ+G zDWs;?0CAhUtbq?1!4Vzqo!C4xV#>Bx_M5LKe-_4mz1q ztxwR#eX(NBJZ^q~Or?HrpX8QXN^QNA7_;*(JZ6%KHK@y9ff@^S2rmzY%l(Nv0c%CmvvNkhI8bw;+ z$Ov`TLSJ&pM3S!&XkWuE8(nT3dr3;6i^abxegm3&x%&c=4gYOFNYhW%Cj3y)%tyM( z5ex5+1CZT8`x52$EE&%UjlPXt*Mvb6&y?DP9dQAWQ@6mu{I6c!Jjp!`e+6pv( z6`@$X7XcMT*cC9_6q6=iwq%8)@u^4~nA~%>V@YeDpH_NY+n0_2G|FMk(%&;p~Hu~!d6$c>4Bu#%oiAK)gjy` za<_w}0@@b#nc5BiT~>_hi3(>ejucka3!>DmppFluX3Ivrsu;MQ|M!8`qqf%(op$Eb zdq$bb#s8E2s*HIGPU||CQ+CqusUIiZS#%`tFC_Xpl1NM4bpv#Hzy_y}l9 zUFDat4%cELEN=R}_Du8LAtg0qwDNxQ@j;-xtR%TYkp zIZ@3avbwJ$1s&_>%6=N`X07FBTPyIZjroc+unlR`s-2w5;ju+gwSBpAenvyp3zxq~ zaEGJf{9=`Xru`hlcC-4LSYAOJ7(%2eHB%GsUE_)Ds8SJ_wA!umOkJaTV%3ISwaI^1 zK*4ZC0R!1tLwKvkQ2V^bcAwC*ZuvJ#fo_ZrI#d5_}&3IW)cd;xx5m6gwqok{5S(7wY3i$tAuw(&o=XtOa{`Mnu z)sntmO6Is$SQNNns5XiQgpETy91k3gX#h8!#;AFpHm8*RzOAH2(CXxV%Om!g%meHJxwsQ8$9{I^ zEb8muY!s=Tb!hg#d4%2c|5udoe}aAu!C4~U{}cl@Oy+w3Cn|USlPv+z2$zCF_&3^@ zlAi(dZ>`|}>3t#m8$t}NNCx|N5=r&yzd`~@wPeaJ5h!$nOkL%Fdj94qHUJvYP*DFa z{$CMB`G3;p6MCm16M%zwKoNUe2SbLD+9OL+G#?aKwcOrO!PqjJQHDF`P$pP4aE}%>Z58F~FGbvQTP@Em_ljexW$U z6`S_rem2}8sL0y$QCWm(9=w}IqaOBw8Gyr@*HY|5rU%hiX3z0j&2j`8)^C5Sy$mj) zWcvVWN!=bxw|J!G_u?6J=u5pvzkP`$U9a(@3Mq_(Q&c%_G9wCa-#AhAB)A<`4)6yW4Y33C@L1w>kw%eu ze`1nSHCk#xB}_dB6<+K`nIkLCGufY!vsR+Wrgk@zkbz&FB{~TD?S;YL1PmN3IJbV> zi0XuU32ld0_v^pw=j2%IwA;}!@*rl)(akfPT29UXpy`&BrWtP{Uv4VwcFdTDZErfW zi_NT`>Ag&2P;`Jby8Hp&8l)0)eH!@4Y86f zQjR7q`7-pOv0p8_ih}+mj(y-a3ZVy;y^B%~%4kEkTu(1qGb3`{Uw>W^dz~9;srRY_ zn6J1>jTcDnyQ84ln{8Ne#?M60htZ*Skn_t%mZ-xHM;ESLXp4bq4-bJCheTzWpvTy0pqekR93^ z>eX}F`HehOC6)rV=t7Dw%w*6|jiislPhW4g~A){aVlk|gX zZgjXg0#ikYbJcN9bZUs(%B4T0?FeD%<&|miOG_|#Jtr2pBjLYybmzi^k#UUf2#YJB zbttl0Xy1(hg9%YFHsNIDpM5(f!8YhyXjt2 zV+b$y86j#;7L&5rkZ;1)py&hk5@870xiBm9FncB~j{UT5)H#Vcb#0}Bk90dXo5YL7 zsLU72nrMZy_1Aa$-AddWO1q=$jn;&irLwAHcms?ieSv@V$k&KM1pBWZvHZ>I`!798 zSQ<6~s@XbYh@t$XbWfqk2+D+&ga`{k+0`S>;L!gzW{3bwV23s-rWY~a)>US*Yxitr zjy*K<2`BJ6R8uFVSUJC+q56sV6;xz2!vKq|M_YJ)Wo6dx-R_-p_WNp0;Op(t9b}<( zDS%%lebi`t5TaPNGvt}=(Ju_yQLfkkW0jGM}FQ|A|fu(emL zQgDC}eef!q;r-Z~;;PN3kY^!2ua?1!;>v?keIsnTld>=E-MyrR+*)En?Q6fCAX^<~ zCEaR816Lq88M~u-HdZ@Xn|@t*(9ah9;IEI3;2ZrgD79D_W{|alM}Fq+lfqGbD`@oz zECW;_j~b}*%0s{-RwsUQD+S8j<>UigG zDNtayktkPqI}++u*i2F9el*xG>+3@FzWuJ=_`v@g@i)?3z_MHF74P%unjC~hpMA3j zr*bhgab@skSIeaK?L9RUVrHnbLw4ULP^c7v4pslLpchkG37n_P{C&piFcCQ-d*deQ zfjwEQ^=t)Er~U@&l^$WYJ_xQF=g**jfGPZU4K|0R{t}Ib8?17K5I5qTer|qa&ii!5 z6*+2_?}QO)K^LukfSdT1A4Th9n`{mVhF&XN-@P+b2TH}8lISE_s(ejr!itH*f$X*F z+|bG8BsKWI#&p%G&$SK~Ti&OiQkt0^d}pHke@*^2anGvoP8@IJQz?Csbe(%f>uky1 z%bNADCLG?tdYcgC5EVpu(@&h=uzCsiT6E~H;;0nME`fIvKj6(|Y_zRfzJgP4$!(0+ zr!vn|V@s{+R5{Z)?ZKlLq0wKJ075rBr$uwhxf>=|izQE;$5WL@41*vAprET#>Pb%; zol({9&z!!JEaT?4Ww`ueLRjEUwAhm+fHacwE=ZPo^o@KP#r{qz3(VDfN4>-@BqIHw zheD@pAiL=97Qu^g?4|keL)f6@6Gk4e2c#$;#Ra|~Neqiik;Tkz1dY@Z0hLPLDTlg& zcPp#O1IzuG(M;`XE=}KTL--SzgQK`e>f4O zZ3^H3V!yDvpx`d;53^4#1aYa)%S06+_mu-7U!T+bc+0&ivf4mVA;Q#m`YS+s6VmLq9As8}4f>yDrRE4ECq-XVV zRV%)Q(=^Unr&-40cAwIvu<2xMOk8d?9c6q)?);9#kT84b045%EWC->ai;PGz-(PiH zcMD{jKX2K5exAe#fNl;CQ&T!fM{KZ%|C#JR!RoGMisWIx`!nCK&JNdF0OZ56q1(5{ zm;N){-vUXufivK~US+md9fS#GE-RI1MW0Rn{}lEYP;oU&8z_vsySrQPAVCt`-62?T zcMA*_+#Q0uyIXK~3GVKiAPH~>PR@J6`~CO+vxc>XT~Af_-n+Wms;kKKX@qZ@ENlG@ zxXxnpH!C-KgXij)4eH(4H@s@#FBLSW7Fmu{P77>%>bikftv zkJHA-@!}r_T=@GDQypXz_e8ruP%cO*q8eo-9@|p_@Y2yhzfiB%$iKTuSUQpEHA2hR zXGk={u`8QdBAbYEYpxX5g`z)HHcaBpH{G0DO(i+-m%kn`py_rD)}K0Qc)!CK@R`wC z{-=W%O>YjvBC2`pS?z%0^_F)-eIwkDR*R8>7SfYe0hn~Yr0=Jb+!352?!vbTT93qW z)P7^Z0DKEiIj9!81Uu2pKF&j|l`$WUy!o zwpsIdQfKIA~>Vz z834Xw@`&6tx@xYNO%R0>jT!e!uAb}KK5@8!g9^wo@SIzOg3Dp9p|I#Y?#aLqXODJaCNQy*=nKo#c zbG+c>svgu_a~>ZaZg_Y6n%Z(D>RCKcBzSzeNHmsmTqN6oAezqkNL{<86jO#yncqyQy){mC6-J^K;bP<5 z>MeI}M!eqi1okEk%uC56PY-#Pzw?;U*UWQlz^&E9bbFszYL9Ih_Yv~BfwJVWk7HT@ zTj<&UX`#<^kB%{7fRc_J`g;t%=>%)q&>6}1@6SujX#-}3gR{h^*wJLLB)rc%mK=Gf zd`^yyUFwJAF2rsH$yn~!bC4ftw$k#vp+1D28oTU{rFookv)w%2-u=M$E^uWJBVA@7 zi^OI7O4!M`TyNXor30@L@Zk(ojZoJSXCA3rIpdc~`KL$=TtK1BDEZ1}``Yd*e|KX` z|F7kXrbQ=Cv^>`(Y{Y=y)yj$$%+lYP7MUAHqbwi^JDGPSDOV}l>VxXTBD0R6h;AC( zD8l*FM-IxvaF6FCg9~@AxUfEzH-4OO2(rW&h(IUq(Y!`G=+PkxjzEW%qaoz=q+R94 z@9$IXQDF#=Is~9^Y9J|JAsdN0)*Ii3$DEv0T-bJGHsVr5Q>*5&P>ppm;v`e-<2%pIG+bD9bu+h$(9MQrG8H7;nzB!U77l+j-L?%Pvyd8@RUM2L!|EwP zL8R2O)2T-)^kx=Ro-TPPvwdX3#4xT^ZHj3$m%jJ@H>&yr8*AV}I?FvigdS)fd)G*4 zV~7R2q5vS9RnS$cCETX|5q1H2fbhG6*g#BYdpBn{_BIZJIj2Y(nf5xaofd`hc<>-v z76-E*_BW#x!cQrgZvv(DQ?sd+jOXmgb-s4753$ovFQSYnk&JjWW)T0;zl{MTRp2Pk z_y2Xa5PR#z0MtWl0PBCci99788#JIkg2ypdV)lfD5dQ#=|8TO$;Wl>W)jQ@B!vgziS+rgyW^XU?^1HBZJIZ(@3 zgN`h6No2WG2mXd(*@Q->X${A2=Z@@?`f)Ti)RY6*37}QFW*ql0KZu@tHyUAvWK(}; zqg`P8ly~|nL9S`bq3-P3Fzem9Y#7joLZqc zuM#a%D!>cxI6&LXtcQaL{U#f0=m?(h7pl++!V`AYMuy@K*B)G^(Rr$%Qx7$1gzWnB zsLl5%f#vgY0nR7OHfp1GQOD)>4}wy4-x#wM)-_BQ)kTK`zPRB-VpzpZ)K?m8p{nA3 ze-bN#4Aeis_*K@910BtkY-NbjD>Rjya$nR{dkQEJ4lsEd^OHR%*^b>-g{`12QcG@W zyt}!{zcE2%53!tA#a~bh5fZ#y1y1xCHjU=?hS)dQPKs-C$kS!Lb|Y= zI9LN7l6DD}O|$%97G(sa)AwP={ki90Wo~p3&hq)QvcFmB3S;tWI>}UUlbAHXTPJuZ z;)GN_@sr-%V~evad=>$DfGAABA_m7jW8D#_zvHe}3!bjaZHYLNIEAv_By&?48l~jB z5-#(BZt6mQHgg!a@!*U=HIx3gn#KOd%3;TDeJ22*o=r}uU zKcw;?N^D2xD%W0=54T^!8uiv+y`46m-5N~+82(cA8Rop{HclR8c3|?=w|%?*vxRUq zY%z^_NUU|Vj3-g$Xs0u}(l3g-?MRstS(8XV{pLe+yz2T)S$9IM%2(CSc&`%D*5#&4 zH*>)yC#`hGkO;+$!8r|NE2;5k~wk{WJP+CR<0?Lp+&Y~TNJjoWL$WuJ%6m+r5im={fCtf|Oc z`o2x6c`wL>)tm;zEk$bIjQzTP>pEON6(Em}v4bk)`@6Bp70!4pgUq-*mCV{pbkqNk zMLOn$n*AJ4>C+EpqJDPcK{k%&pd)a*{sEJkUFyy33O|1Psz@C9Jay=Y?aeioOznEV zZHf7)m?TQnb;QYUc|W-(7u5wJsgQ&g2AP~FO0uOIrMuK<}@n*waz0K!qQkFP*EmlZ@*URjy&w}tsm24cn|B*5!hzDmaf!HHgXv}g;)`SO0lTSY!ISo8vW>By%f;Es zxR=&@%}!GeZ|*+{V@Y@64B9Y66@B@f` zCpk9lBu~|rVF(p$z%lh!?!gGjMq#p8CR3KH;(AH!a(W{!#pIS2%>MF`R>88A`zE`U zZL3rxCucNo$7!a_Ad3R~Mk&EaE!Y0$mTWF`Ic2VYI3e$|><{Hq`~Jy5L^Gxod$$pV zW+Lt!Tu|PCo`@caVw7_uF707>v~z$mK|TQ#r__g5D87K@P{)KU&*_lua_K7ry9j*v z5W{dmW&*{__QHWZX;xXMyv|(z2rHH<&$JE2HW25tQVA&Xdh(*>h?5xKhg>s;<;@7) zJiJpg^$GbIc`|3b!a^j*rsX1o1c~oMA`fIFoyUzd3yPNhQkagbJ5tjaKT83WCr8Vp zZxlX=N^5M!jb9B~)@PsYiAvhXKKM?xvbJ8BB@!cIW6h@x`0l7F^t#X-#ddL=N!qzQDM(+iHyFF-2>T}-4 z5m$R>gWc!AGmhVx*MQvO6<*W;zpuO=mZwB%d(v{k*q2*7_+ZWsII z5UDrQW=UY7!23Y_yQPyOt!WCjy^uM{Lac|VY4Ke-kDZZt!mV$k3X`NmwV+FcV98G> zy`6<}@NsFUiooe(>l@#8K!JcZ&8ApPvSy&a=HYyn-Bc%vqjNxs78^II&UA|0?1z$R zzomDi4x8nvwP|)vi(&JMi;yVO-Fob(p5pfU#4TyH-Zk@q^rpSc zSszK=&DOsE06~7U;SZ3j|El`&=ZVV}hNsZuK06EiuN<{#VgD$!{Z8C2K8dRbe?rQB z!N#n9VfNw;4#d5rfGa#G_WLt5IAbDuxe~1I&ai;F!G%y)!<7YE*4z=|<6roQ#~b9r zChosfv#fZWJE)yvx9x@XW?TySCN5UI1r+0w<=!A*QKBcF?=}Nq{aV$k%npv-{ri%q zt)kgVOf`j$H_(HdwhVP){8@tHB3FD#@mK)YSWLeXv}j<|JU=pG9Aem6DE8x((fWNu z^X$yL!`} z2&sHqp-+XroV)Q6XPHSq8q9 zT}g!Wws4_{y29xl<1|u>Ma4uL)>1i$%kX1~nwDOncJdkc-WHuKhTRB+yr%y1W_yUS zXnT!CZfN?tx+jpy7`xLL^%oU}JF7!6 zmu;i_VX^z89X1SRefA9SOteP~BJz&_FdR;TTN*@Mu~SR$-a+gH1b>~GdbI!bO}C%9 z3{kDmUML4z6v2iH|JZJtJG8aF>t|^+1Z=gVpZ4P{bB~}?g@5VUd&e}9(n*F{Al!m>P`$5< zL|15R{|&e<4-Mm~YH@1>9EqGjBL9CY=RfneBU#{j!ec(84lO(*jO%-dI&-Zkjy{2W zQ!*9uK?8L5r2UEmXH)0~w$U+5^pE{-nWJ6rHh^dD`)ahz!|@DAzNWG5jc~6u0iG^^ zYYD&3siOPix7C?CI%mODD|x)2<_DTuSF=SG_hkNM?%c3A!PQeGpvi zCMTj?dVls6nhVG^QcG#gOqx2Q)F z)+RlJ^7-)s!p_^g9>9bDjvM?@D%RLGBr=VdQxtx0EkQX*lpP7!k`EsTfF4(cu*CIg{tn$=~SDBu&F{rL8KYU|1 zg-MV-7g_4?6TB{(fyaCrzmHWbog(Aa;|i26imN3ek#rCoPR|11v0ZECYWdwVOd437 zz?9Z|`AeUmR|AL@d(qO|6d{K75>7*l>`)z&pr^FJ2KTix8%Wow4x%89&55H2Jw;F9 zsaSEgFwqb=easT%SXQ!rvKV7wD~^UX3bO|et9RYagv7{tZc;z%UXfboH({Xe)xMh6 z{rhmudtmf9!&q)%{J$(E+kY1m{wQNvX)Qb>=3!oTp~{kPY49V92zn8o1lHHgPGNvd z12}@UNlMxRRcnNe9@)iZH}rW{m`Q_PcuJojC$Gs^vO~MW)8p3BTacR#8AG_lSVIX54DJ9Txe zcDllh0kkdDGAB$z!^nm4ldTn3G3A$WY`- z97VaFJ?HgaaH+vnob|Z&Rx9WTBD!=JWT!47*C8%KF>#)R=eA$PST`d5n<(Bwxd_ps z)&Yd9!$^K^41$-gOUu^3ha>4bmB{!BlE}3#jgcx`g`^w`NIluj~GR+6w zpuH@*A_7_-YPPL2lDV0=ksWLVP0w_y7mw!}1}{Ivk-AOoaMk~h%H^}h{;tXhAl|MN96_p zUgDTijYxse6}f#PaV*HW4G=Mt4T3i09UN1n@FSc8WU&un%h;)ddSQuj%<~M! z>MXsEVvAx)8mV#|Z`R2s37E1%9Evb0)9!oZo|RF)>ucu!e`OSUQAR!Xzm<{wpN<>2 zwguc0=d+d)stK$65+2FQDe6-Xo-N^lZzx62=Fe>d zvyy%qDXv}0o4|Yom@!&vT~MIHcE{3i`b8a;b5!F~nMlwr!@6SRx|k$YbhQpcOKm=kmZw6J z!%@XM`#PzM0I)B45R2ap?XSX7j2{l)AD=Qat(4t5^VuL%Z#qtq1BgVli)fQM2AAsa z@b=hPd4X8~t<+YMQhuE{K_w^MrwVEH!cA7@`c%A}#8ao@Ix&Jv!8|7<=ZMc6@Nl|z zq=Ct~XP@TzhmLpDIg_}gjjk~ISSmL0P9P0*`@u9+D7!vOr*D4x^|BrhLb>yB4-C7q z1zeK<->&z6&KLeY(qpIir#t<#96B)MYyoQZ&|zLuC^#FQZ!@b(nUZALtFDB;7GMOY z6o|{(A~ls^q`1}ZC(uD`)C!J+#?fB+HLc0P{m#g)_Qgh)s&8Zo z|93kp*tx0={poH*bap z817es3nuC8K#w+|J^hLs6qdFEf=8%-nLsYuCFgW4r#9T9&!8p4M{(?|pEeD@`ac6k zXh-*WRH>qyzrC6AIe={Ro&}+V08L19f^XnkhMf?c7NYOKI#v9JJ(Q4j>>C(zEb?OeI@AXZ9z;Y=)8Bl|sgU%E zI#&l31CtG54*um)by6mQw`FB=dS&ASo~gD&wOGO6SXm!_F2;SK8v{olE@E*%_-LZ( z(L;)?tXZH}FtGP6^BmK}K1u3cHo!^VxPrR6b4H))YXNhF8)~BI8EZ$Nn}NjQz~IiW;PktSdUXfz}`j6 zUh0a#)8h|FZ~Q_e1Bkaf=T!vOgZiRK8HM5os=Wle&@2HZ++Y9E>QYsx`hiap15V{qQgr;}Di*~;y{ECqFhR}R7 zJfPk5cDMb2xE2rz8xx13=IXP9-DsrJbfNA`bm3VJ-EMY(489bFd;|#w;k1E9<3!*X zIHKNV#I?k-@9OT6NCN(teqdD2I^v92QFnUVG-lOKzf72zYEzWVW;Ok7^qh4mF#RL$ z>5!4ym@FA(!zc=U6lOqHj9IA1`!>2|xC+hMSO#vWhI&Ag#gLN+8JcW3Ek|8z4(4$DbLWtp9Ic_X0Sc+pQC&-TRLn&i?^Y*mLAfD~5HT&y z=KCUi-*5ny`@!u6yVY(}xc2>gM>xzj@FCaCZ;{s6Qn^VV`DL^Vq|dV7X4gddXY z-^QU=G;o41S?j=|ey(Y7G){V#GT!@i+r?~8VU`G%T#LZQ2Q6aNd)0JAoL=56>shn2 zob95Ak09V&aqtnoM*X;momzHkVMBBU(@UfASN?lIZf;GJ;#<0+1Ty3Bkg{8-x-@6c zAs3`%BEIZe2hiMu<*(|WaK^FZytfAqKM;d+0V%7{76@tQ?m7Qx{|76x&)%VeT(O+{hfyNsRvT}6 zx(e#O0%8CG18k{es1M+99$+f68HVTBg$lbMTaOuGws2^z4Nst5kek#=1= za86creofL(p+VosRFi$_gJ_bBD?VM@W5&}%-cxvvY7D&|=5}&|EJ%4^DFlmrvPSmQ=l>$0^W&exKd~L#{4nB2||l z$ZQU0BWVuf$sTM1US7YD)@c(swzqJ4+w__N&R1javL^1gc0xrroXDWc&^su3OG zI&J;kzh%^-9tMIoc8Pa6-e+wo4AK|p+NmFadKxmyCl}S?=%sq+Hihl9bnQF&p(9Ws zp-7Tg%B?TKluozQi(Ot<%biQCx4qvimp zj;S?yR}2uw4mjl*7L^xdl^UW-&1hk}eh!$CP_z90)BiweN3OnpF=E;xFiM!ie~B-@ zgZU%Qvho5yp`-)PTg(!s-^I4PQxwbES?YIe?hndbvmS?~SKpG`5|&k+3=$64%zjOA z(iO|5%^Il_2;PpQodlFLcf!Yo9t}(a&NBV*P@tWH75rMCNWyi-{i-K?DiEfekn47g{DAx*+0Vi~dAFtJ+ObY(zW`*hCQ#3~(|MQ%41O8X8Z0P9=T)8hjV(_m3R{7s+%@ zg(_a}A>pj9Al-hn5{2g3)`e0;x-Zf-I_MD@%E*vSJ*(H<0fSWtRN0yOpX)VtfL zmgmk%LW~jE^@Yoj<8f2~n+h0_EcqM30jU9Y0jBZ1w1a-(pM89GaPf9h2y+8)?elly z5l*o_k2B$Ps*;El8Q^!~-SloOBvt3?)a`tK=i;VtOV9r%@k<7IlHF3>j#9wjHKgrj zvQfZ2ZUqU>qVy7`4PBnf#+wV3K=zY-6FZaSU-dl1G)I^KD8931Kv#yHD+&ycY-)zE zdE8LZ6+P2n?PfQPRHR$b4rHG%jho_#SGBOlV!G66S!j$F{#7eMOg3+p=o@#;H8h8M zvIS4~;w-0+JIV*87Th28T6*0`=UROuVCAX%$8>xn=*9ON!;lEm#LbgtE)bOP@-eRw zgyx<~^u<$=h9-*k0eDC*p?%rc!KaSB&TvC9Nn6S$q_l0ha<(eb%H*6`TinL+Nz)f# zZ#cG{TDvS#>fo1=gpPyymu8*xCGf(@XFv2hS>&LW*=dl`u~Q$|HZ~*Ipe8q6WEo#S#?NgOSl4WL+oA7yWvdYjBH>|1aN2Z>=*kM+_H_xOqCSj z@Jvw?4w5?$RFGNqGxJ@3`iihsaL%PvQ@iW&wYaEBnn>avvG+Zn7zLJ+5Y&Vux)`7Y z#xt!^^1DUt*DMFZGOLA0Lg8vY-ooM2EH7TiW3mPD2tA}`vkVx&r3t?y!A7MhVVt)# zMu_KZowRQZi2;ZU6@|Y7`e0PsGsi7$?tOM|Jc8;Qjz6f?>(riZDn9Wqs@ALE)uP#i zgEV)2LX2w+Q^g|b<0l*YjKFf2C{EC}-#yAa#+B(9%8wZBf~VL9;lNwEiF7)pDHmY| z2bx_Q`sMeFfE6%S4xZ zT2@Xy=1y}?Jysf8maJM@bjlt8H>*t!)NHUmmMz0_r$?z*X-_v#M0=?==ezAI@>834?C>^C}MHXaAM2Wzd@XV-p;#d%NXQ^*Hmm)C}G9;hD@-#s*Fdu`^C-Dj}6 z2d*;aWlVX%#U^60Zoy;6+p)B4D`r<}wbOk3X^{n6EguZ>l)I zl(eSXQTff^u?N46cD`BH6sFava^K+Q#_@Leoa3YQ9z5p9qdcVKYQH(RTe7Bxkfa|U zS39ujTPLNo?$A2ji@FFuF7~B;T{^fr<(e3D{Nvm@*)Fp6`aM!x*?GLQIWyKY`U)yz@=UCBFSu5P zUh#381peG2xu#`qn?O8FzbH>KmSex+U>ugz$uyZ+S`j-g`>K%Z=`mt&&WWZa4JtBn z9d#s?k~=%0wOoWbEZ@~?J4YN`WT29miv^1nKv?cr&eYJ*9V`Z0v|{E%;Q6SzzUIQ$ zXv~m)MR$f-ek`l;6Ms9*Nh{csN=}?f7t<`|cXn}RU-+2Q;KUaFaQMrR`&2|)NX#iv z<@y8ai6@23KAS9ZVG~|Le@ORG;=+=(g7Wa$^^`6@y+z^Dn8%Me@R*u>&&NV?1z&a% z1;AP;pzOM|_H@h>?I!uig25MKaJh4^YQ?FaUyaHaZ}dU0E=!Nb=D9dp)JBNHkc zB`E1Z2Kn|Qt#1iKJ8Ph7w@IURT+#2OPXO{vV zt+Ur)I$DQeWYn}q`w=Q(8b5o+oKG~q?otuKp^Qn3L(k>oI!1b**7=SFR)>HeSC0CFdo|lp zF?mq=q%F+?|EIjPpVE(@`k_C2lz0In+xn2O(~AotZ+8%*{gYG5_YWNuq3zP6EZ1DI z>Uw&FK2)&LFW@jBVyF~Ht``}-XaRFTMM))JD6b@~T}-Fw z4&m02Yqtn9a>zsFbjTQlncgwM9H^xTHMV3xLo&mK9-Ca{UY$JAW~My{XY&BCkBTrT zej<`aYXrQ+~Wc)Zoedvf>9eDisFED~_k$e+0mp~^v6>Np4Ngc42` z(E}u$A6WG`^D{r4f`l^7H=zWpL)R|TCVwC0?4*MJqI2OYJZI2{wd>slnA?o%WdHFr zU<2ouHkgbTCO9BnNsbz6>`Ugxj-`0(KCcYskr;8_XyeRLVCBwSJc!5B!n217DM{yd z7;^>Ldwu)nW0nKkcvS{+$NHU7I%HsXRTdW$c}sX~N(wAFH_o~f7r``{vpC-!z~^Ck zzYIZSQ=)B6uzqaSW>;~ch={u#49+P9#gTWd#VK7+P zRC+%G^$TX*^2!oUq!hifVQrr06lQqC+-xIu56|bzXhAHY!v|9V0wM`&cBRuPjRdoJ zQQFe;Un=cBHp;h|LwLx=?BXy#qlSb^OW1|E+|@P6tUs10&^gkG!S*EWQIH zW=EmIEqcQ|JNh|Fg?r49ZfyiM^{29|y`*q{vgY_kY2lR4yYh4W_(*2ohp8+6hHgbcS6#S$G84Sfs_4C zhP46?VM=$Zs8tv5A^~BQxNG^BC!e8}^mcVh-O2}!jf^G&RoY`?=+=6P6>hDmJTAG3 zl5OXYBMQic9@XFNh&dwP?O3jlkL(loxr4@!UeK*|-xPl)UaOd@xlSGG7tbEtAXbE6 z>6U9fXZ%7ef0y%{<%>Z1CH1}t!j|07JZ!u`FEZWls3Bw_iZ*~`1?_LMc!BO41MtF2 z^3SP7&hAmpdEL>ZTwDS}8NDov6_p5Pc4(7Z5o`aP>7ABm+3hq%p|Gh7o}y@chqhk2d)s{ji4B4#x-E3)vsf4 zeWLk|Cfl;t@SU7~&-5Lqvj_!$p-7A|C|S@fOQ{G!zo5D~z}<&!Y5pMdljaIs@%z zGv{(!JY1ucli$4Rov$iS3*)=L!YQ5700Sxq>WP)+<^meD-f8A!C^aS%OjOJg=uW6qUX#KG zcX-`qrP_bpokETUFG!Z51Ba|s*>04uA((6`>iF*X{SEksEQN)b3~DURykpB|4MXMX zJ#D$ZYWE~FC%sehPd2R+FxxBh1XQJX*=+t-!`W$fRe)Hnk7;(LeE5Av&0hr`KgF&> z`ZOhg;TwxYr%7<0urL-6pJ79m$ks(Ssutcd{l+fU9vum%c4xneoxZH0Vq)8q0GQON ze^siRcl$JSy0oiVX~W<>sv#MNCT%?$L;JB3#LNss7>s$U`^mVhLHXEV76qQ-`1h9% zvlZ*yDKEgoZY>d;L3_R}JVQc=3Dz8@Nq@v+45qZSH4IB)m!OwojN?v%jDqC=%;0Y} zi&biiK!)CL1m7z2;h~HbZA49yq9c680bMYe|%y%hg^L_er2F z#DCp{Q&BacUo;Ycq&)lh_Kl8l$qbH2*zZlKSPKRK6TFla(8`A;TYzd8uon|fwbXv% ziff_3W*YcT(M!wnu>ZJ>CSSKw$x(oHJ(}_L&MQkr)P$_i3}VAY3FYM3 zXvH~zn`y)zNAlGWQ`lAHn1DS=S{d9M(y8!Y%;D=F{XXVk^aXuA=L~5eTYg|vQvMvG z-NslJt9%L9q@^hreF^8LDXVZmzs+YRF5quL*krdN%Csm;wD=y=5z_c1OB7DACkELK zgG1hR0xO5hm_CoCc_F2a$mMR!J$i`B)BWMqelTP9Sy!#Ra6 z84#faD_4t^kzY)9kE!`?GE~SVx^o1Mr1ZRZ2G z0qex99q6F@u4SzsU(j+uB(!SZj4dYurK#R7x4vTHx&UtaMB_O^c8?z=@B2%0m0ZxL z2Dn$1n)!v02}EbtAuYS_p+uMZ2f`h4X?pe!`X#dnJ%lrAn2vt;M^G0E6b*gm+v4XX5ld zGN%Mk-+QMd$&-oF~y8DfxnP zuh`K>urnvwpiDfnrrrH&#-qs=7g9w^AxPvR-%CbcQ1rdwOi%Y_$F@b7-a7!$GV6xe zy5hyGK;^_MUotT&?T5ngO;=0=U*^$KyptN&I7RJ)iB<|ksJe&MT)t#&(i)#g0so;i z!!LpTvtH7d#fj;yyBFl`M?uY$N^ofc9>4mCcK@aY*C8HLmJ2js&I3LhnI-rE)=Gja zH_hnC@89L7nqhgRQkpD*fw`Lid?MT=t$Mw2+>q01W?eDMeydY!7UBUjq4`ptv!t^Y zQa`37+=1Dc78P|rq_F0FG?FMYH^U5|GJ7yv^j5J5VM=FuoFmvQvAj^>+^5s}O+SFm z7PQ8db9**M!tcjV?-e4eL7J1iA*=qiSLdVxOQ7iBWLS>g`W&=$Nz{TPiS9>c7 z5!91B&LNRVL>-AdsSF6_nx#y?MIU}XWve|A4EF2yS zS@XkH3r8-KQZU*dP65@7a1X03%3n%8e0xGSKUCaDHZP?-LuT1TyiIIkEg%ugEnzIP z>g_G*Wpqie9i@2^Pissca!qd298AS!RK7=drNx(9gZfFfC4qvCFXubI(@})S70O7l z_BN{3Y+(UDz(0pyc$VoVR(bX(W7pwyON&f$?uxoE9ucKn-4TFmb{-MY)U*U@o6m|6 z?(>vK!2LQRKjh?IdNw`vLGeR&Sa<5OG?6uVyqXeZ!!%lZRKgXHW-qn^n@uLiXO^-( zDN}NrY`)fUq<000Q_9End6Kd}T+nWNx{Hxr>kdCMmuEygHso=2yr*1mluUQ;+1A_+QZ=iLz51;=WeZ z6+C7W3UHdN9))Fc)}CLuourb%Ow08ETIXF>6$I=T$dG-pC}6bL^) zc~R*>k73UPor@7VPQfdGfgbx@f$qMyA0dcR#RCr-8k1*?(*A`O9}JsHoiMt885yAQ z7+l`(iBbqP@VjQN`9|Z0=%{_P*{1F^lm#z$>YofC%b&_14~R()lAA2*@t$zqhjgyDbXk-RV>G*eGiacOG zM_^7CPqT)y@5OZ?H2a-eKlGSZi+xply0i82ZrBl5;#NjuKEZn>P*|EC1J^mF(4fRe7JLceheMhLU{L;q2pKoK9hW{}6rjF$*ghgU#2>66 zDNW6(m@B03R0tRtpk8Fr?wR_ey=`c5oz!Bor=n_pil#rCy{k56wrAsy9Vto2r&MoK z{o$7A2j?AOlTzIb^Re@mh}hPx6YftB{hqe9L!Vm79VV9a4ZVY8bp;B|7^Pp*7!D>5 zdXn_BWk@x|j~o*R%777JcvN$QHh^|m79r1t@;3S-a|+!-4!QFFMR&397jl>*WxJ3t zgOhE{<1aCAc>X`Qn<6oR?iU~>reLVucr2vlhJLR1)lDT2FU|vCD)zzjz^&jt;NL6f zfxkj8fPnBmd!X1@z0tRhWw?F|u=vmEK1Ro6S5SOY{AuI>(ku6ay zvuzcc>zp7+Oswg~KQJZpH@1v+@w`k->bnsdT|9lC@i9t*Q-CtND7;uH6p^>U2}VA>rMHVZBuu5G1MRG%U~I4mq0fxPXOec!2p))LaB@#%?Us247{ z^qPWbn6JXdj{3Jn3vh*h0De}!r4b?Lo1FX2%4BW0W;p*hhB+dqX*xbF@~(q=n9MMv zp$KeUzvX6PiNK2H;!X9NjJn)y>C|pLyo6w_MyswK?_h#^z;vUb0fr%+H1jg=v{mNO z=PAPO?V^U{^7YE~Gqn}M)j}&t>$e|o+xz7QDjqt$Zg`U(unF=Hc8m|=shP_WrmfYR z3hI!h5x;Drq)Gbg)goaacZ7KAM%^M9>Y|S;jZDJDh#SN}2%_PI!E3UHqH2eIp}NK< zxdR>K(@fqi0601)kjZ?l#kgcCldSu83)|O4dXF}Lgkgz}{2IBPhVm8QgP@JHjs4vlRV)c;! zv#4D&aithY{^VJ3>C{UsQ#J~BkX^92W8@|DSGQ7mmNjyaX z<$6XY(xQS2y!^001(kgXlJj>4ZlS~>R8WpsIvQdIc_~mZ3=j+u7#Nu6I!XPVotE#f zbhIEK8c?yQ22{_vx+zJXk$>k}<$8t8fPX;>V1kmoApetZ@IUDWpZ&uA_rXJaM!rtW zO7_C@ccQD;^>9IZ<`MaS%~JU9JNi4z)9ZSw;ygZ5@Q#!7(^yn1fW`_D)nV!lc6ARyI5ARxm3yFuVDLi}egMUrRKwsx6JJroFtJ_-nk z;42h0HLxxWiBD*tWG~1xKX3ynATk(;6n}-3VoH3E4yyVb{yZ@S9at>{j^_&>67SGK z(O!6uE8|e7fVS~1aHss0Nd&1EcrGQDpOrLm1QQhD1^suZ_t$R+^ur%Cdg5CwP_&m4 zrLlk|!U3Z!y@)|h%*6u5dLgtu6TU0_L%;+^gMT3~U;_y%W0!@s`4^O!S3uP&aNmm__~J!gp;BWKN%29IUQjC&I5$|py9EQjUg=k;llYeh>KR0o z7>*4r`MH_?9ixl;71}fL<@xXvfMUPM4Mzx0-UODO53J|wCWlu7L^8&UlX4&v)qjJ1(Y{!tcZ9&_xl{2s0Vy$&5U5)~S&603)QQG_sK97o zBrgO4A|Ro)a6weRj?|43nuY}~z{3V3;Maf?3`gib<*Brca|8OAp z)Bd0OJ_j}UC#J!xqSTlEVFdJ$CJvDREyHu=SpGVO{#p&+ul?a5N)#mhqdI?|CVq{E z-u#2cPQ)hzMSqd~_fgH)=-lmp(4qfD|9#X)@KxQ1_x_-<67R|WShW8gz5oGv{|bG6 z0z?!2DS>25hJTTRDM6`T>d~qUc>xQIeR2z&slI*~)lC250S+gjvE-#7 Wft}E^@#F*1zykrHdkYHu@BaZKfsVKU delta 25214 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 + + + + + diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml new file mode 100644 index 0000000000..9d8aea7111 --- /dev/null +++ b/quickstep/res/layout/task.xml @@ -0,0 +1,35 @@ + + + + + + + \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 8f759800a7..a0340b65ce 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -40,7 +40,7 @@ public class RecentsActivity extends ListActivity { plan.preloadPlan(new RecentsTaskLoader(this, 1, 1, 0), -1, UserHandle.myUserId()); mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); - mAdapter.addAll(plan.getTaskStack().getStackTasks()); + mAdapter.addAll(plan.getTaskStack().getTasks()); setListAdapter(mAdapter); } } diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java new file mode 100644 index 0000000000..96c93c23f9 --- /dev/null +++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.LightingColorFilter; +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Shader; +import android.util.AttributeSet; +import android.view.Display; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.widget.FrameLayout; + +import com.android.systemui.shared.recents.model.ThumbnailData; + +/** + * A task in the Recents view. + */ +public class TaskThumbnailView extends FrameLayout { + + private ThumbnailData mThumbnailData; + + private Rect mThumbnailRect = new Rect(); + private float mThumbnailScale; + + private Matrix mMatrix = new Matrix(); + private Paint mDrawPaint = new Paint(); + protected Paint mBgFillPaint = new Paint(); + protected BitmapShader mBitmapShader; + + private float mDimAlpha; + private LightingColorFilter mLightingColorFilter = new LightingColorFilter(Color.WHITE, 0); + + public TaskThumbnailView(Context context) { + this(context, null); + } + + public TaskThumbnailView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false); + setDimAlpha(1f); + setClipToOutline(true); + } + + /** + * Updates this thumbnail. + */ + public void setThumbnail(ThumbnailData thumbnailData) { + if (thumbnailData != null && thumbnailData.thumbnail != null) { + Bitmap bm = thumbnailData.thumbnail; + bm.prepareToDraw(); + mThumbnailScale = thumbnailData.scale; + mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + mDrawPaint.setShader(mBitmapShader); + mThumbnailRect.set(0, 0, + bm.getWidth() - thumbnailData.insets.left - thumbnailData.insets.right, + bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom); + mThumbnailData = thumbnailData; + updateThumbnailMatrix(); + updateThumbnailPaintFilter(); + } else { + mBitmapShader = null; + mDrawPaint.setShader(null); + mThumbnailRect.setEmpty(); + mThumbnailData = null; + } + } + + @Override + protected void onDraw(Canvas canvas) { + int viewWidth = getMeasuredWidth(); + int viewHeight = getMeasuredHeight(); + int thumbnailWidth = Math.min(viewWidth, + (int) (mThumbnailRect.width() * mThumbnailScale)); + int thumbnailHeight = Math.min(viewHeight, + (int) (mThumbnailRect.height() * mThumbnailScale)); + + if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { + int topOffset = 0; + // Draw the background, there will be some small overdraw with the thumbnail + if (thumbnailWidth < viewWidth) { + // Portrait thumbnail on a landscape task view + canvas.drawRect(Math.max(0, thumbnailWidth), topOffset, viewWidth, viewHeight, + mBgFillPaint); + } + if (thumbnailHeight < viewHeight) { + // Landscape thumbnail on a portrait task view + canvas.drawRect(0, Math.max(topOffset, thumbnailHeight), viewWidth, viewHeight, + mBgFillPaint); + } + + // Draw the thumbnail + canvas.drawRect(0, topOffset, thumbnailWidth, thumbnailHeight, mDrawPaint); + } else { + canvas.drawRect(0, 0, viewWidth, viewHeight, mBgFillPaint); + } + } + + void updateThumbnailPaintFilter() { + int mul = (int) ((1.0f - mDimAlpha) * 255); + if (mBitmapShader != null) { + mLightingColorFilter = new LightingColorFilter(Color.WHITE, + Color.argb(255, mul, mul, mul)); + mDrawPaint.setColorFilter(mLightingColorFilter); + mDrawPaint.setColor(0xFFffffff); + mBgFillPaint.setColorFilter(mLightingColorFilter); + } else { + int grey = mul; + mDrawPaint.setColorFilter(null); + mDrawPaint.setColor(Color.argb(255, grey, grey, grey)); + } + invalidate(); + } + + public void updateThumbnailMatrix() { + mThumbnailScale = 1f; + if (mBitmapShader != null && mThumbnailData != null) { + if (getMeasuredWidth() == 0) { + // If we haven't measured , skip the thumbnail drawing and only draw the background + // color + mThumbnailScale = 0f; + } else { + float invThumbnailScale = 1f / mThumbnailScale; + final Configuration configuration = + getContext().getApplicationContext().getResources().getConfiguration(); + final Point displaySize = new Point(); + getDisplay().getRealSize(displaySize); + if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { + if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) { + // If we are in the same orientation as the screenshot, just scale it to the + // width of the task view + mThumbnailScale = (float) getMeasuredWidth() / mThumbnailRect.width(); + } else { + // Scale the landscape thumbnail up to app size, then scale that to the task + // view size to match other portrait screenshots + mThumbnailScale = invThumbnailScale * + ((float) getMeasuredWidth() / displaySize.x); + } + } else { + // Otherwise, scale the screenshot to fit 1:1 in the current orientation + mThumbnailScale = invThumbnailScale; + } + } + mMatrix.setTranslate(-mThumbnailData.insets.left * mThumbnailScale, + -mThumbnailData.insets.top * mThumbnailScale); + mMatrix.postScale(mThumbnailScale, mThumbnailScale); + mBitmapShader.setLocalMatrix(mMatrix); + } + invalidate(); + } + + public void setDimAlpha(float dimAlpha) { + mDimAlpha = dimAlpha; + updateThumbnailPaintFilter(); + } +} diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java new file mode 100644 index 0000000000..ea584f0095 --- /dev/null +++ b/quickstep/src/com/android/quickstep/TaskView.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.launcher3.R; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskCallbacks; +import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.ActivityManagerWrapper; + +/** + * A task in the Recents view. + */ +public class TaskView extends FrameLayout implements TaskCallbacks { + + private Task mTask; + private TaskThumbnailView mSnapshotView; + private ImageView mIconView; + + public TaskView(Context context) { + this(context, null); + } + + public TaskView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TaskView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false); + setOnClickListener((view) -> { + if (mTask != null) { + ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key, + null, null, null); + } + }); + } + + @Override + protected void onFinishInflate() { + mSnapshotView = findViewById(R.id.snapshot); + mIconView = findViewById(R.id.icon); + } + + /** + * Updates this task view to the given {@param task}. + */ + public void bind(Task task) { + mTask = task; + task.addCallback(this); + } + + @Override + public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) { + mSnapshotView.setThumbnail(thumbnailData); + mSnapshotView.setDimAlpha(1f); + mIconView.setImageDrawable(task.icon); + } + + @Override + public void onTaskDataUnloaded() { + // Do nothing + } + + @Override + public void onTaskWindowingModeChanged() { + // Do nothing + } +} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 75968ae2bc..553a136b29 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -222,15 +222,12 @@ public class Launcher extends BaseActivity @Thunk DragLayer mDragLayer; private DragController mDragController; - public View mWeightWatcher; - private AppWidgetManagerCompat mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; private final int[] mTmpAddItemCellCoordinates = new int[2]; @Thunk Hotseat mHotseat; - private ViewGroup mOverviewPanel; private View mAllAppsButton; @@ -240,6 +237,9 @@ public class Launcher extends BaseActivity @Thunk AllAppsContainerView mAppsView; AllAppsTransitionController mAllAppsController; + // UI and state for the overview panel + private ViewGroup mOverviewPanel; + // We need to store the orientation Launcher was created with, due to a bug (b/64916689) // that results in widgets being inflated in the wrong orientation. private int mOrientation; @@ -1303,8 +1303,8 @@ public class Launcher extends BaseActivity return mHotseat; } - public ViewGroup getOverviewPanel() { - return mOverviewPanel; + public T getOverviewPanel() { + return (T) mOverviewPanel; } public DropTargetBar getDropTargetBar() { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index daa9bd0119..1f87c004b4 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -296,7 +296,6 @@ public class Workspace extends PagedView mLauncher = Launcher.getLauncher(context); mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); - final Resources res = getResources(); DeviceProfile grid = mLauncher.getDeviceProfile(); mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); mWallpaperManager = WallpaperManager.getInstance(context); From f8088eecf1939ef6ada4161f7c22c89a852fe4fd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 10 Nov 2017 14:52:00 -0800 Subject: [PATCH 087/885] Initial changes for handling touch events. When the touch passes a threashold, we take a snapshot and start the launcher activity. The launcher displays the snapshot on top of its UI. As we get further touch events, we move this snapshot and the hotseat in reponse. Change-Id: I4623676227000afd52805a414a4de499081feced --- quickstep/res/layout/overview_panel.xml | 17 +- .../NavBarSwipeInteractionHandler.java | 237 ++++++++++++++++++ .../com/android/quickstep/RecentsView.java | 1 + .../com/android/quickstep/SimpleTaskView.java | 52 ++++ .../android/quickstep/SnapshotDragView.java | 83 ++++++ .../quickstep/TouchInteractionService.java | 186 +++++++++++++- .../launcher3/AbstractFloatingView.java | 8 +- src/com/android/launcher3/Launcher.java | 3 + .../states/InternalStateHandler.java | 43 ++++ 9 files changed, 620 insertions(+), 10 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java create mode 100644 quickstep/src/com/android/quickstep/SimpleTaskView.java create mode 100644 quickstep/src/com/android/quickstep/SnapshotDragView.java create mode 100644 src/com/android/launcher3/states/InternalStateHandler.java diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml index 466470f3b4..521551c217 100644 --- a/quickstep/res/layout/overview_panel.xml +++ b/quickstep/res/layout/overview_panel.xml @@ -19,28 +19,33 @@ android:theme="@style/HomeScreenElementTheme" android:layout_width="match_parent" android:layout_height="match_parent" + android:paddingTop="20dp" + android:paddingBottom="20dp" + android:clipToPadding="false" android:layout_gravity="center_horizontal|bottom" android:gravity="top"> - - - diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java new file mode 100644 index 0000000000..caeef50778 --- /dev/null +++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.RectEvaluator; +import android.annotation.TargetApi; +import android.app.ActivityManager.RunningTaskInfo; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.os.Build; +import android.os.UserHandle; +import android.support.annotation.BinderThread; +import android.support.annotation.UiThread; +import android.util.FloatProperty; +import android.view.Choreographer; +import android.view.Choreographer.FrameCallback; +import android.view.View; +import android.view.ViewGroup; + +import com.android.launcher3.Hotseat; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.states.InternalStateHandler; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.system.ActivityManagerWrapper; + +@TargetApi(Build.VERSION_CODES.O) +public class NavBarSwipeInteractionHandler extends InternalStateHandler implements FrameCallback { + + private static FloatProperty SHIFT = + new FloatProperty("currentShift") { + @Override + public void setValue(NavBarSwipeInteractionHandler handler, float v) { + handler.setShift(v); + } + + @Override + public Float get(NavBarSwipeInteractionHandler handler) { + return handler.mCurrentShift; + } + }; + + // 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; + private static final int MIN_FLING_VELOCITY = 250; + + private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f; + + private final Rect mSourceRect = new Rect(); + private final Rect mTargetRect = new Rect(); + private final Rect mCurrentRect = new Rect(); + private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect); + + private final Bitmap mTaskSnapshot; + private final RunningTaskInfo mTaskInfo; + + private Launcher mLauncher; + private Choreographer mChoreographer; + private SnapshotDragView mDragView; + private RecentsView mRecentsView; + private Hotseat mHotseat; + + private float mStartDelta; + private float mLastDelta; + + // Shift in the range of [0, 1]. + // 0 => preview snapShot is completely visible, and hotseat is completely translated down + // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely + // visible. + private float mCurrentShift; + + // These are updated on the binder thread, and eventually picked up on doFrame + private float mCurrentDisplacement; + private boolean mTouchEnded = false; + private float mEndVelocity; + + NavBarSwipeInteractionHandler(Bitmap taskSnapShot, RunningTaskInfo taskInfo) { + mTaskSnapshot = taskSnapShot; + mTaskInfo = taskInfo; + } + + @Override + public void onLauncherResume() { + mStartDelta = mCurrentDisplacement; + mLastDelta = mStartDelta; + mChoreographer = Choreographer.getInstance(); + + scheduleNextFrame(); + } + + @Override + public void onNewIntent(Launcher launcher) { + mLauncher = launcher; + + // Go immediately + launcher.getStateManager().goToState(LauncherState.OVERVIEW, false); + + // Optimization + launcher.getAppsView().setVisibility(View.GONE); + + mDragView = new SnapshotDragView(launcher, mTaskSnapshot); + launcher.getDragLayer().addView(mDragView); + mDragView.setPivotX(0); + mDragView.setPivotY(0); + mRecentsView = launcher.getOverviewPanel(); + mRecentsView.scrollTo(0, 0); + mHotseat = launcher.getHotseat(); + } + + @BinderThread + public void updateDisplacement(float displacement) { + mCurrentDisplacement = displacement; + } + + @BinderThread + public void endTouch(float endVelocity) { + mTouchEnded = true; + mEndVelocity = endVelocity; + } + + @UiThread + private void scheduleNextFrame() { + if (!mTouchEnded) { + mChoreographer.postFrameCallback(this); + } else { + animateToFinalShift(); + } + } + + @Override + public void doFrame(long l) { + mLastDelta = mCurrentDisplacement; + + float translation = Utilities.boundToRange(mStartDelta - mLastDelta, 0, + mHotseat.getHeight()); + int hotseatHeight = mHotseat.getHeight(); + float shift = hotseatHeight == 0 ? 0 : translation / hotseatHeight; + setShift(shift); + scheduleNextFrame(); + } + + @UiThread + private void setShift(float shift) { + if (mTargetRect.isEmpty()) { + DragLayer dl = mLauncher.getDragLayer(); + + // Init target rect. + View targetView = ((ViewGroup) mRecentsView.getChildAt(0)).getChildAt(0); + dl.getViewRectRelativeToSelf(targetView, mTargetRect); + mSourceRect.set(0, 0, dl.getWidth(), dl.getHeight()); + } + + mCurrentShift = shift; + int hotseatHeight = mHotseat.getHeight(); + mHotseat.setTranslationY((1 - shift) * hotseatHeight); + + mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); + + mDragView.setTranslationX(mCurrentRect.left); + mDragView.setTranslationY(mCurrentRect.top); + mDragView.setScaleX((float) mCurrentRect.width() / mSourceRect.width()); + mDragView.setScaleY((float) mCurrentRect.width() / mSourceRect.width()); + } + + @UiThread + private void animateToFinalShift() { + float flingThreshold = Utilities.pxFromDp(FLING_THRESHOLD_VELOCITY, + mLauncher.getResources().getDisplayMetrics()); + boolean isFling = Math.abs(mEndVelocity) > flingThreshold; + + long duration = 200; + final float endShift; + if (!isFling) { + endShift = mCurrentShift >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0; + } else { + endShift = mEndVelocity < 0 ? 1 : 0; + float minFlingVelocity = Utilities.pxFromDp(MIN_FLING_VELOCITY, + mLauncher.getResources().getDisplayMetrics()); + if (Math.abs(mEndVelocity) > minFlingVelocity) { + float distanceToTravel = (endShift - mCurrentShift) * mHotseat.getHeight(); + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 5. We use 4 to make + // it a little slower. + duration = 4 * Math.round(1000 * Math.abs(distanceToTravel / mEndVelocity)); + } + } + + ObjectAnimator anim = ObjectAnimator.ofFloat(this, SHIFT, endShift) + .setDuration(duration); + anim.setInterpolator(Interpolators.SCROLL); + anim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (Float.compare(mCurrentShift, 0) == 0) { + resumeLastTask(); + } else { + mDragView.close(false); + } + } + }); + anim.start(); + } + + @UiThread + private void resumeLastTask() { + // TODO: These should be done as part of ActivityOptions#OnAnimationStarted + mHotseat.setTranslationY(0); + mLauncher.setOnResumeCallback(() -> mDragView.close(false)); + + // TODO: Task key should be received from Recents model + TaskKey taskKey = new TaskKey(mTaskInfo.id, 0, null, UserHandle.myUserId(), 0); + ActivityManagerWrapper.getInstance() + .startActivityFromRecentsAsync(taskKey, null, null, null); + } +} diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java index d85de8f405..d7559daad9 100644 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -30,6 +30,7 @@ import com.android.launcher3.R; * A placeholder view for recents */ public class RecentsView extends HorizontalScrollView implements Insettable { + public RecentsView(Context context) { this(context, null); } diff --git a/quickstep/src/com/android/quickstep/SimpleTaskView.java b/quickstep/src/com/android/quickstep/SimpleTaskView.java new file mode 100644 index 0000000000..8425fa3ba4 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SimpleTaskView.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.View; +import android.view.WindowManager; + +/** + * A simple view which keeps its size proportional to the display size + */ +public class SimpleTaskView extends View { + + private static final Point sTempPoint = new Point(); + + public SimpleTaskView(Context context) { + super(context); + } + + public SimpleTaskView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public SimpleTaskView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int height = MeasureSpec.getSize(heightMeasureSpec); + getContext().getSystemService(WindowManager.class) + .getDefaultDisplay().getRealSize(sTempPoint); + + int width = (int) ((float) height * sTempPoint.x / sTempPoint.y); + setMeasuredDimension(width, height); + } +} diff --git a/quickstep/src/com/android/quickstep/SnapshotDragView.java b/quickstep/src/com/android/quickstep/SnapshotDragView.java new file mode 100644 index 0000000000..791fe9ff17 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SnapshotDragView.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; + +/** + * Floating view which shows the task snapshot allowing it to be dragged and placed. + */ +public class SnapshotDragView extends AbstractFloatingView implements Insettable { + + private final Launcher mLauncher; + private final Bitmap mSnapshot; + + public SnapshotDragView(Launcher launcher, Bitmap snapshot) { + super(launcher, null); + mLauncher = launcher; + mSnapshot = snapshot; + setWillNotDraw(false); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mSnapshot != null) { + setMeasuredDimension(mSnapshot.getWidth(), mSnapshot.getHeight()); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + public void setInsets(Rect insets) { + + } + + @Override + protected void onDraw(Canvas canvas) { + if (mSnapshot != null) { + canvas.drawBitmap(mSnapshot, 0, 0, null); + } + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + return false; + } + + @Override + protected void handleClose(boolean animate) { + // We dont suupport animate. + mLauncher.getDragLayer().removeView(this); + } + + @Override + public void logActionCommand(int command) { + // We should probably log the weather + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_QUICKSTEP_PREVIEW) != 0; + } +} diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 091ab54989..dcfa53b41e 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -15,17 +15,201 @@ */ package com.android.quickstep; +import static android.view.MotionEvent.INVALID_POINTER_ID; + +import static com.android.launcher3.states.InternalStateHandler.EXTRA_STATE_HANDLER; + +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityOptions; import android.app.Service; +import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Display; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.WindowManager; + +import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.shared.system.ActivityManagerWrapper; /** * Service connected by system-UI for handling touch interaction. */ public class TouchInteractionService extends Service { + private static final String TAG = "TouchInteractionService"; + + private final IBinder mMyBinder = new IOverviewProxy.Stub() { + + @Override + public void onMotionEvent(MotionEvent ev) { + handleMotionEvent(ev); + } + + @Override + public void onBind(ISystemUiProxy iSystemUiProxy) throws RemoteException { + mISystemUiProxy = iSystemUiProxy; + } + }; + + private ActivityManagerWrapper mAM; + private RunningTaskInfo mRunningTask; + private Intent mHomeIntent; + private ComponentName mLauncher; + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + private int mActivePointerId = INVALID_POINTER_ID; + private VelocityTracker mVelocityTracker; + private int mTouchSlop; + private NavBarSwipeInteractionHandler mInteractionHandler; + + private ISystemUiProxy mISystemUiProxy; + + @Override + public void onCreate() { + super.onCreate(); + mAM = ActivityManagerWrapper.getInstance(); + + mHomeIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setPackage(getPackageName()) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0); + mLauncher = new ComponentName(getPackageName(), info.activityInfo.name); + mHomeIntent.setComponent(mLauncher); + } + @Override public IBinder onBind(Intent intent) { - return null; + Log.d(TAG, "Touch service connected"); + return mMyBinder; + } + + private void handleMotionEvent(MotionEvent ev) { + if (ev.getActionMasked() != MotionEvent.ACTION_DOWN && mVelocityTracker == null) { + return; + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop(); + + mRunningTask = mAM.getRunningTask(); + if (mRunningTask == null || mRunningTask.topActivity.equals(mLauncher)) { + // TODO: We could drive all-apps in this case. For now just ignore swipe. + break; + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + mVelocityTracker.addMovement(ev); + if (mInteractionHandler != null) { + mInteractionHandler.endTouch(0); + mInteractionHandler = null; + } + break; + } + case MotionEvent.ACTION_POINTER_UP: { + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); + mVelocityTracker.clear(); + } + break; + } + case MotionEvent.ACTION_MOVE: { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mVelocityTracker.addMovement(ev); + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + + float displacement = ev.getY(pointerIndex) - mDownPos.y; + if (mInteractionHandler == null) { + if (Math.abs(displacement) >= mTouchSlop) { + startTouchTracking(); + } + } else { + // Move + mInteractionHandler.updateDisplacement(displacement); + } + break; + } + case MotionEvent.ACTION_CANCEL: + // TODO: Should be different than ACTION_UP + case MotionEvent.ACTION_UP: { + + endInteraction(); + break; + } + } + } + + private void startTouchTracking() { + mInteractionHandler = new NavBarSwipeInteractionHandler(getCurrentTaskSnapshot(), mRunningTask); + + Bundle extras = new Bundle(); + extras.putBinder(EXTRA_STATE_HANDLER, mInteractionHandler); + Intent homeIntent = new Intent(mHomeIntent).putExtras(extras); + + // TODO: Call ActivityManager#startRecentsActivity instead, so that the system knows that + // recents was started and not Home. + startActivity(homeIntent, + ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle()); + } + + private void endInteraction() { + if (mInteractionHandler != null) { + mVelocityTracker.computeCurrentVelocity(1000, + ViewConfiguration.get(this).getScaledMaximumFlingVelocity()); + + mInteractionHandler.endTouch(mVelocityTracker.getXVelocity(mActivePointerId)); + mInteractionHandler = null; + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + + private Bitmap getCurrentTaskSnapshot() { + if (mISystemUiProxy == null) { + Log.e(TAG, "Never received systemUIProxy"); + return null; + } + Display display = getSystemService(WindowManager.class).getDefaultDisplay(); + Point size = new Point(); + display.getRealSize(size); + + // TODO: We are using some hardcoded layers for now, to best approximate the activity layers + try { + return mISystemUiProxy.screenshot(new Rect(), size.x, size.y, 0, 100000, false, + display.getRotation()); + } catch (RemoteException e) { + Log.e(TAG, "Error capturing snapshot", e); + return null; + } } } diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 62e0fb1bcd..26024e5310 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -20,7 +20,6 @@ import android.annotation.SuppressLint; import android.content.Context; import android.support.annotation.IntDef; import android.util.AttributeSet; -import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; @@ -42,7 +41,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch TYPE_ACTION_POPUP, TYPE_WIDGETS_BOTTOM_SHEET, TYPE_WIDGET_RESIZE_FRAME, - TYPE_WIDGETS_FULL_SHEET + TYPE_WIDGETS_FULL_SHEET, + TYPE_QUICKSTEP_PREVIEW }) @Retention(RetentionPolicy.SOURCE) public @interface FloatingViewType {} @@ -51,9 +51,11 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2; public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3; public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4; + public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 5; public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP - | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET; + | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET + | TYPE_QUICKSTEP_PREVIEW; protected boolean mIsOpen; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index baed44d75d..b1b3452e4b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -63,6 +63,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; @@ -123,6 +124,7 @@ import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.states.AllAppsState; +import com.android.launcher3.states.InternalStateHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -1405,6 +1407,7 @@ public class Launcher extends BaseActivity }); } } + InternalStateHandler.handleIntent(this, intent); TraceHelper.endSection("NEW_INTENT"); } diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java new file mode 100644 index 0000000000..a90ed36aa9 --- /dev/null +++ b/src/com/android/launcher3/states/InternalStateHandler.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.states; + +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; + +import com.android.launcher3.Launcher; +import com.android.launcher3.Launcher.OnResumeCallback; + +/** + * Utility class to sending state handling logic to Launcher from within the same process + */ +public abstract class InternalStateHandler extends Binder implements OnResumeCallback { + + public static final String EXTRA_STATE_HANDLER = "launcher.state_handler"; + + public abstract void onNewIntent(Launcher launcher); + + public static void handleIntent(Launcher launcher, Intent intent) { + IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER); + if (stateBinder instanceof InternalStateHandler) { + InternalStateHandler handler = (InternalStateHandler) stateBinder; + launcher.setOnResumeCallback(handler); + handler.onNewIntent(launcher); + } + intent.getExtras().remove(EXTRA_STATE_HANDLER); + } +} From 6c2b3ef0f37286bde43212c26189905da0e7e873 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 13 Nov 2017 09:56:06 -0800 Subject: [PATCH 088/885] Adding volatile to various variables used on multple threads. Change-Id: I143de4981461e6f07688a7ffda906fabbcc97948 --- .../quickstep/NavBarSwipeInteractionHandler.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java index caeef50778..e94d76fd17 100644 --- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java +++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java @@ -90,9 +90,9 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen private float mCurrentShift; // These are updated on the binder thread, and eventually picked up on doFrame - private float mCurrentDisplacement; - private boolean mTouchEnded = false; - private float mEndVelocity; + private volatile float mCurrentDisplacement; + private volatile float mEndVelocity; + private volatile boolean mTouchEnded = false; NavBarSwipeInteractionHandler(Bitmap taskSnapShot, RunningTaskInfo taskInfo) { mTaskSnapshot = taskSnapShot; @@ -127,6 +127,12 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen mHotseat = launcher.getHotseat(); } + /** + * This is updated on the binder thread and is picked up on the UI thread during the next + * scheduled frame. + * TODO: Instead of continuously scheduling frames, post the motion events to UI thread + * (can ignore all continuous move events until the last move). + */ @BinderThread public void updateDisplacement(float displacement) { mCurrentDisplacement = displacement; @@ -134,8 +140,8 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen @BinderThread public void endTouch(float endVelocity) { - mTouchEnded = true; mEndVelocity = endVelocity; + mTouchEnded = true; } @UiThread From ac00cba35c5c8502b051cbbca14f8e9f897c3897 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 13 Nov 2017 15:58:01 -0800 Subject: [PATCH 089/885] Simplifying workspace layout Instead of creating workspace view larger than the screen size (so that it can be zoomed out), restricting the size to the parent size and bypassing the drag events directly to Workspace (since the workspace is smaller when zoomed out, it might not qualify for all events otherwise). Change-Id: I45e213dd6d16bec5feb6e7cf90bc6f7de4c6d305 --- .../launcher3/uioverrides/OverviewState.java | 3 - .../android/launcher3/ButtonDropTarget.java | 7 +- src/com/android/launcher3/Launcher.java | 1 - src/com/android/launcher3/PagedView.java | 186 ++---------------- src/com/android/launcher3/Workspace.java | 4 - .../launcher3/dragndrop/DragController.java | 19 +- .../launcher3/uioverrides/OverviewState.java | 2 +- 7 files changed, 33 insertions(+), 189 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 9bdd7a3849..abf70aaed0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -29,9 +29,6 @@ import com.android.quickstep.RecentsView; */ public class OverviewState extends LauncherState { - // The percent to shrink the workspace during overview mode - public static final float SCALE_FACTOR = 0.7f; - private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE; public OverviewState(int id) { diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index d8c4efaa4e..cfb55ccc73 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -53,6 +53,7 @@ import com.android.launcher3.util.Thunk; public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener, OnClickListener { + private static final int[] sTempCords = new int[2]; private static final int DRAG_VIEW_DROP_DURATION = 285; private final boolean mHideParentOnDisable; @@ -257,9 +258,9 @@ public abstract class ButtonDropTarget extends TextView super.getHitRect(outRect); outRect.bottom += mBottomDragPadding; - int[] coords = new int[2]; - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, coords); - outRect.offsetTo(coords[0], coords[1]); + sTempCords[0] = sTempCords[1] = 0; + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords); + outRect.offsetTo(sTempCords[0], sTempCords[1]); } public Rect getIconRect(DragObject dragObject) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b1b3452e4b..608570a8a2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1064,7 +1064,6 @@ public class Launcher extends BaseActivity // Setup the drag controller (drop targets have to be added in reverse order in priority) mDragController.setMoveTarget(mWorkspace); - mDragController.addDropTarget(mWorkspace); mDropTargetBar.setup(mDragController); mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 4240a30b4f..6c22474fc1 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -93,7 +93,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mMinSnapVelocity; protected boolean mFirstLayout = true; - private int mNormalChildHeight; @ViewDebug.ExportedProperty(category = "launcher") protected int mCurrentPage; @@ -166,8 +165,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc @Thunk static int REORDERING_REORDER_REPOSITION_DURATION = 300; private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; - private float mMinScale = 1f; - private boolean mUseMinScale = false; @Thunk View mDragView; private Runnable mSidePageHoverRunnable; @Thunk int mSidePageHoverIndex = -1; @@ -273,12 +270,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - public void setMinScale(float f) { - mMinScale = f; - mUseMinScale = true; - requestLayout(); - } - @Override public void setScaleX(float scaleX) { super.setScaleX(scaleX); @@ -597,56 +588,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc computeScrollHelper(); } - public static class LayoutParams extends ViewGroup.LayoutParams { - public boolean isFullScreenPage = false; - - // If true, the start edge of the page snaps to the start edge of the viewport. - public boolean matchStartEdge = false; - - /** - * {@inheritDoc} - */ - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LayoutParams(ViewGroup.LayoutParams source) { - super(source); - } - } - - @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new LayoutParams(p); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - public void addFullScreenPage(View page) { - LayoutParams lp = generateDefaultLayoutParams(); - lp.isFullScreenPage = true; - super.addView(page, 0, lp); - } - public int getNormalChildHeight() { - return mNormalChildHeight; + return getViewportHeight() - getPaddingTop() - getPaddingBottom() + - mInsets.top - mInsets.bottom; } @Override @@ -662,22 +606,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); - // NOTE: We multiply by 2f to account for the fact that depending on the offset of the - // viewport, we can be at most one and a half screens offset once we scale down - DisplayMetrics dm = getResources().getDisplayMetrics(); - int maxSize = Math.max(dm.widthPixels + mInsets.left + mInsets.right, - dm.heightPixels + mInsets.top + mInsets.bottom); - int parentWidthSize = (int) (2f * maxSize); - int parentHeightSize = (int) (2f * maxSize); - int scaledWidthSize, scaledHeightSize; - if (mUseMinScale) { - scaledWidthSize = (int) (parentWidthSize / mMinScale); - scaledHeightSize = (int) (parentHeightSize / mMinScale); - } else { - scaledWidthSize = widthSize; - scaledHeightSize = heightSize; - } mViewport.set(0, 0, widthSize, heightSize); if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) { @@ -691,71 +620,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return; } - /* Allow the height to be set as WRAP_CONTENT. This allows the particular case - * of the All apps view on XLarge displays to not take up more space then it needs. Width - * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect - * each page to have the same width. - */ - final int verticalPadding = getPaddingTop() + getPaddingBottom(); - final int horizontalPadding = getPaddingLeft() + getPaddingRight(); - - int referenceChildWidth = 0; // The children are given the same width and height as the workspace // unless they were set to WRAP_CONTENT if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize); - if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize); - if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize); - if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding); - if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - // disallowing padding in paged view (just pass 0) - final View child = getPageAt(i); - if (child.getVisibility() != GONE) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - int childWidthMode; - int childHeightMode; - int childWidth; - int childHeight; + int myWidthSpec = MeasureSpec.makeMeasureSpec( + getViewportWidth() - mInsets.left - mInsets.right, MeasureSpec.EXACTLY); + int myHeightSpec = MeasureSpec.makeMeasureSpec( + getViewportHeight() - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY); - if (!lp.isFullScreenPage) { - if (lp.width == LayoutParams.WRAP_CONTENT) { - childWidthMode = MeasureSpec.AT_MOST; - } else { - childWidthMode = MeasureSpec.EXACTLY; - } - - if (lp.height == LayoutParams.WRAP_CONTENT) { - childHeightMode = MeasureSpec.AT_MOST; - } else { - childHeightMode = MeasureSpec.EXACTLY; - } - - childWidth = getViewportWidth() - horizontalPadding - - mInsets.left - mInsets.right; - childHeight = getViewportHeight() - verticalPadding - - mInsets.top - mInsets.bottom; - mNormalChildHeight = childHeight; - } else { - childWidthMode = MeasureSpec.EXACTLY; - childHeightMode = MeasureSpec.EXACTLY; - - childWidth = getViewportWidth(); - childHeight = getViewportHeight(); - } - if (referenceChildWidth == 0) { - referenceChildWidth = childWidth; - } - - final int childWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(childWidth, childWidthMode); - final int childHeightMeasureSpec = - MeasureSpec.makeMeasureSpec(childHeight, childHeightMode); - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - } - setMeasuredDimension(scaledWidthSize, scaledHeightSize); + // measureChildren takes accounts for content padding, we only need to care about extra + // space due to insets. + measureChildren(myWidthSpec, myHeightSpec); + setMeasuredDimension(widthSize, heightSize); } @SuppressLint("DrawAllocation") @@ -780,10 +657,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int verticalPadding = getPaddingTop() + getPaddingBottom(); - LayoutParams lp = (LayoutParams) getChildAt(startIndex).getLayoutParams(); - LayoutParams nextLp; - - int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft()); + int childLeft = offsetX + getPaddingLeft(); if (mPageScrolls == null || childCount != mChildCountOnLastLayout) { mPageScrolls = new int[childCount]; } @@ -791,14 +665,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc for (int i = startIndex; i != endIndex; i += delta) { final View child = getPageAt(i); if (child.getVisibility() != View.GONE) { - lp = (LayoutParams) child.getLayoutParams(); - int childTop; - if (lp.isFullScreenPage) { - childTop = offsetY; - } else { - childTop = offsetY + getPaddingTop() + mInsets.top; - childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2; - } + int childTop = offsetY + getPaddingTop() + mInsets.top; + childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding + - child.getMeasuredHeight()) / 2; final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); @@ -807,26 +676,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + childHeight); - int scrollOffsetLeft = lp.isFullScreenPage ? 0 : getPaddingLeft(); + int scrollOffsetLeft = getPaddingLeft(); mPageScrolls[i] = childLeft - scrollOffsetLeft - offsetX; - int pageGap = mPageSpacing; - int next = i + delta; - if (next != endIndex) { - nextLp = (LayoutParams) getPageAt(next).getLayoutParams(); - } else { - nextLp = null; - } - - // Prevent full screen pages from showing in the viewport - // when they are not the current page. - if (lp.isFullScreenPage) { - pageGap = getPaddingLeft(); - } else if (nextLp != null && nextLp.isFullScreenPage) { - pageGap = getPaddingRight(); - } - - childLeft += childWidth + pageGap + getChildGap(); + childLeft += childWidth + mPageSpacing + getChildGap(); } } @@ -1289,12 +1142,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } else { View child = getChildAt(index); - int scrollOffset = 0; - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (!lp.isFullScreenPage) { - scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft(); - } - + int scrollOffset = scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft(); int baselineX = mPageScrolls[index] + scrollOffset + getViewportOffsetX(); return (int) (child.getX() - baselineX); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 93fe17c22c..0db5a166c3 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -80,7 +80,6 @@ import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; -import com.android.launcher3.uioverrides.OverviewState; import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -440,10 +439,7 @@ public class Workspace extends PagedView setClipChildren(false); setClipToPadding(false); - // TODO: Remove this - setMinScale(OverviewState.SCALE_FACTOR); setupLayoutTransition(); - mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); // Set the wallpaper dimensions when Launcher starts up diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 94023833ce..818cea76c0 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -605,29 +605,32 @@ public class DragController implements DragDriver.EventListener, TouchController } private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) { - final Rect r = mRectTemp; + mDragObject.x = x; + mDragObject.y = y; + final Rect r = mRectTemp; final ArrayList dropTargets = mDropTargets; final int count = dropTargets.size(); - for (int i=count-1; i>=0; i--) { + for (int i = count - 1; i >= 0; i--) { DropTarget target = dropTargets.get(i); if (!target.isDropEnabled()) continue; target.getHitRectRelativeToDragLayer(r); - - mDragObject.x = x; - mDragObject.y = y; if (r.contains(x, y)) { - dropCoordinates[0] = x; dropCoordinates[1] = y; mLauncher.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates); - return target; } } - return null; + // Pass all unhandled drag to workspace. Workspace finds the correct + // cell layout to drop to in the existing drag/drop logic. + dropCoordinates[0] = x; + dropCoordinates[1] = y; + mLauncher.getDragLayer().mapCoordInSelfToDescendant(mLauncher.getWorkspace(), + dropCoordinates); + return mLauncher.getWorkspace(); } public void setWindowToken(IBinder token) { diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java index c339634f26..dcf7453b9e 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java @@ -34,7 +34,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; public class OverviewState extends LauncherState { // The percent to shrink the workspace during overview mode - public static final float SCALE_FACTOR = 0.7f; + private static final float SCALE_FACTOR = 0.7f; private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE; From b63b44c3d55cc49423b9c70d37f2e1d9a0799ea4 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 10 Nov 2017 17:54:44 -0800 Subject: [PATCH 090/885] Load some recent tasks Bug: 69166452 Test: Build quickstep Change-Id: Id4b0172256d6920616a6b9529d61abd1fe0c1a36 --- quickstep/libs/sysui_shared.jar | Bin 86425 -> 89285 bytes .../drawable/task_thumbnail_background.xml | 2 +- quickstep/res/layout/overview_panel.xml | 35 +---- quickstep/res/layout/task.xml | 5 +- .../launcher3/uioverrides/OverviewState.java | 6 +- .../NavBarSwipeInteractionHandler.java | 109 ++++++++++---- .../com/android/quickstep/RecentsView.java | 139 +++++++++++++++--- .../android/quickstep/SnapshotDragView.java | 9 ++ .../android/quickstep/TaskThumbnailView.java | 47 +++--- .../src/com/android/quickstep/TaskView.java | 61 +++++++- .../quickstep/TouchInteractionService.java | 72 ++++++++- res/values/config.xml | 4 + res/values/dimens.xml | 3 + src/com/android/launcher3/DeviceProfile.java | 5 +- src/com/android/launcher3/Launcher.java | 6 +- .../states/InternalStateHandler.java | 31 +++- 16 files changed, 399 insertions(+), 135 deletions(-) diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar index a76f4f9e0e2e8c39a8bd4e280217643e9d7f4657..ef50ac4326f983f7c7ce7bba2d248c01b11852ef 100644 GIT binary patch delta 20344 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 CKVi)P delta 17704 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 diff --git a/quickstep/res/drawable/task_thumbnail_background.xml b/quickstep/res/drawable/task_thumbnail_background.xml index 27efd6c68e..603380e753 100644 --- a/quickstep/res/drawable/task_thumbnail_background.xml +++ b/quickstep/res/drawable/task_thumbnail_background.xml @@ -15,5 +15,5 @@ --> - + diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml index 521551c217..a8b91c5d9d 100644 --- a/quickstep/res/layout/overview_panel.xml +++ b/quickstep/res/layout/overview_panel.xml @@ -19,35 +19,8 @@ android:theme="@style/HomeScreenElementTheme" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="20dp" - android:paddingBottom="20dp" + android:layout_gravity="center" + android:clipChildren="false" android:clipToPadding="false" - android:layout_gravity="center_horizontal|bottom" - android:gravity="top"> - - - - - - - - - - - \ No newline at end of file + android:alpha="0.0" + android:visibility="invisible" /> \ No newline at end of file diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 9d8aea7111..fdf1adcbc9 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -23,13 +23,14 @@ android:id="@+id/snapshot" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_marginTop="24dp" android:scaleType="matrix" android:background="@drawable/task_thumbnail_background" android:elevation="4dp" /> \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 9bdd7a3849..26f5d5b51b 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -46,12 +46,14 @@ public class OverviewState extends LauncherState { @Override public void onStateEnabled(Launcher launcher) { - ((RecentsView) launcher.getOverviewPanel()).setViewVisible(true); + RecentsView rv = launcher.getOverviewPanel(); + rv.setOverviewStateEnabled(true); } @Override public void onStateDisabled(Launcher launcher) { - ((RecentsView) launcher.getOverviewPanel()).setViewVisible(false); + RecentsView rv = launcher.getOverviewPanel(); + rv.setOverviewStateEnabled(false); } @Override diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java index caeef50778..0810579503 100644 --- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java +++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java @@ -40,8 +40,15 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.states.InternalStateHandler; +import com.android.launcher3.uioverrides.OverviewState; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.BackgroundExecutor; +import com.android.systemui.shared.system.WindowManagerWrapper; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; @TargetApi(Build.VERSION_CODES.O) public class NavBarSwipeInteractionHandler extends InternalStateHandler implements FrameCallback { @@ -66,13 +73,15 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f; + private final Rect mStableInsets = new Rect(); private final Rect mSourceRect = new Rect(); private final Rect mTargetRect = new Rect(); private final Rect mCurrentRect = new Rect(); private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect); private final Bitmap mTaskSnapshot; - private final RunningTaskInfo mTaskInfo; + private final int mRunningTaskId; + private Future mFutureLoadPlan; private Launcher mLauncher; private Choreographer mChoreographer; @@ -94,9 +103,10 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen private boolean mTouchEnded = false; private float mEndVelocity; - NavBarSwipeInteractionHandler(Bitmap taskSnapShot, RunningTaskInfo taskInfo) { + NavBarSwipeInteractionHandler(Bitmap taskSnapShot, RunningTaskInfo runningTaskInfo) { mTaskSnapshot = taskSnapShot; - mTaskInfo = taskInfo; + mRunningTaskId = runningTaskInfo.id; + WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); } @Override @@ -109,22 +119,39 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen } @Override - public void onNewIntent(Launcher launcher) { + public void onCreate(Launcher launcher) { mLauncher = launcher; - - // Go immediately - launcher.getStateManager().goToState(LauncherState.OVERVIEW, false); - - // Optimization - launcher.getAppsView().setVisibility(View.GONE); - - mDragView = new SnapshotDragView(launcher, mTaskSnapshot); - launcher.getDragLayer().addView(mDragView); + mDragView = new SnapshotDragView(mLauncher, mTaskSnapshot); + mLauncher.getDragLayer().addView(mDragView); mDragView.setPivotX(0); mDragView.setPivotY(0); - mRecentsView = launcher.getOverviewPanel(); - mRecentsView.scrollTo(0, 0); - mHotseat = launcher.getHotseat(); + mRecentsView = mLauncher.getOverviewPanel(); + mHotseat = mLauncher.getHotseat(); + + // Optimization + mLauncher.getAppsView().setVisibility(View.GONE); + + // Launch overview + mRecentsView.update(consumeLastLoadPlan()); + mLauncher.getStateManager().goToState(LauncherState.OVERVIEW, false /* animate */); + } + + @Override + public void onNewIntent(Launcher launcher, boolean alreadyOnHome) { + mLauncher = launcher; + mDragView = new SnapshotDragView(mLauncher, mTaskSnapshot); + mLauncher.getDragLayer().addView(mDragView); + mDragView.setPivotX(0); + mDragView.setPivotY(0); + mRecentsView = mLauncher.getOverviewPanel(); + mHotseat = mLauncher.getHotseat(); + + // Optimization + mLauncher.getAppsView().setVisibility(View.GONE); + + // Launch overview, animate if already on home + mRecentsView.update(consumeLastLoadPlan()); + mLauncher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome); } @BinderThread @@ -167,19 +194,46 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen // Init target rect. View targetView = ((ViewGroup) mRecentsView.getChildAt(0)).getChildAt(0); dl.getViewRectRelativeToSelf(targetView, mTargetRect); + mTargetRect.right = mTargetRect.left + mTargetRect.width(); + mTargetRect.bottom = mTargetRect.top + mTargetRect.height(); mSourceRect.set(0, 0, dl.getWidth(), dl.getHeight()); } - mCurrentShift = shift; - int hotseatHeight = mHotseat.getHeight(); - mHotseat.setTranslationY((1 - shift) * hotseatHeight); + if (!mSourceRect.isEmpty()) { + mCurrentShift = shift; + int hotseatHeight = mHotseat.getHeight(); + mHotseat.setTranslationY((1 - shift) * hotseatHeight); - mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); + mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); - mDragView.setTranslationX(mCurrentRect.left); - mDragView.setTranslationY(mCurrentRect.top); - mDragView.setScaleX((float) mCurrentRect.width() / mSourceRect.width()); - mDragView.setScaleY((float) mCurrentRect.width() / mSourceRect.width()); + float scale = (float) mCurrentRect.width() / mSourceRect.width(); + mDragView.setTranslationX(mCurrentRect.left - mStableInsets.left * scale * shift); + mDragView.setTranslationY(mCurrentRect.top - mStableInsets.top * scale * shift); + mDragView.setScaleX(scale); + mDragView.setScaleY(scale); + mDragView.getViewBounds().setClipTop((int) (mStableInsets.top * shift)); + mDragView.getViewBounds().setClipBottom((int) (mStableInsets.bottom * shift)); + } + } + + void setLastLoadPlan(Future futureLoadPlan) { + if (mFutureLoadPlan != null) { + mFutureLoadPlan.cancel(true); + } + mFutureLoadPlan = futureLoadPlan; + } + + private RecentsTaskLoadPlan consumeLastLoadPlan() { + try { + if (mFutureLoadPlan != null) { + return mFutureLoadPlan.get(); + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } finally { + mFutureLoadPlan = null; + } + return null; } @UiThread @@ -229,9 +283,8 @@ public class NavBarSwipeInteractionHandler extends InternalStateHandler implemen mHotseat.setTranslationY(0); mLauncher.setOnResumeCallback(() -> mDragView.close(false)); - // TODO: Task key should be received from Recents model - TaskKey taskKey = new TaskKey(mTaskInfo.id, 0, null, UserHandle.myUserId(), 0); - ActivityManagerWrapper.getInstance() - .startActivityFromRecentsAsync(taskKey, null, null, null); + // TODO: For now, assume that the task stack will have loaded in the bg, will update + // the lib api later for direct call + mRecentsView.launchTaskWithId(mRunningTaskId); } } diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java index d7559daad9..528b11d590 100644 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -19,17 +19,43 @@ package com.android.quickstep; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.widget.HorizontalScrollView; +import android.view.LayoutInflater; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; import com.android.launcher3.R; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; +import com.android.systemui.shared.recents.model.RecentsTaskLoader; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.WindowManagerWrapper; + +import java.util.ArrayList; /** - * A placeholder view for recents + * A list of recent tasks. */ -public class RecentsView extends HorizontalScrollView implements Insettable { +public class RecentsView extends PagedView { + + private boolean mOverviewStateEnabled; + private boolean mTaskStackListenerRegistered; + + private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { + @Override + public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { + for (int i = 0; i < getChildCount(); i++) { + final TaskView taskView = (TaskView) getChildAt(i); + if (taskView.getTask().key.id == taskId) { + taskView.getThumbnail().setThumbnail(snapshot); + return; + } + } + } + }; public RecentsView(Context context) { this(context, null); @@ -41,24 +67,103 @@ public class RecentsView extends HorizontalScrollView implements Insettable { public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - setAlpha(0); - setVisibility(INVISIBLE); + setWillNotDraw(false); + setPageSpacing((int) getResources().getDimension(R.dimen.recents_page_spacing)); } - public void setViewVisible(boolean isVisible) { } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // TODO: These are rough calculations which currently use the stable insets + DeviceProfile profile = Launcher.getLauncher(getContext()).getDeviceProfile(); + Rect stableInsets = new Rect(); + WindowManagerWrapper.getInstance().getStableInsets(stableInsets); + Rect padding = profile.getWorkspacePadding(null); + float taskWidth = profile.getCurrentWidth() - stableInsets.left - stableInsets.right; + float taskHeight = profile.getCurrentHeight() - stableInsets.top - stableInsets.bottom; + float overviewHeight = profile.availableHeightPx - padding.top - padding.bottom + - stableInsets.top; + float overviewWidth = taskWidth * overviewHeight / taskHeight; + padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2); + setPadding(padding.left, padding.top, padding.right, padding.bottom); + } @Override - public void setInsets(Rect insets) { - MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); - lp.topMargin = insets.top; - lp.bottomMargin = insets.bottom; - lp.leftMargin = insets.left; - lp.rightMargin = insets.right; + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + updateTaskStackListenerState(); + } - DeviceProfile dp = Launcher.getLauncher(getContext()).getDeviceProfile(); - if (!dp.isVerticalBarLayout()) { - lp.bottomMargin += dp.hotseatBarSizePx + getResources().getDimensionPixelSize( - R.dimen.dynamic_grid_min_page_indicator_size); + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + updateTaskStackListenerState(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + updateTaskStackListenerState(); + } + + public void setOverviewStateEnabled(boolean enabled) { + mOverviewStateEnabled = enabled; + updateTaskStackListenerState(); + } + + public void update(RecentsTaskLoadPlan loadPlan) { + final RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader(); + setCurrentPage(0); + TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null; + if (stack == null) { + removeAllViews(); + return; + } + + // Ensure there are as many views as there are tasks in the stack (adding and trimming as + // necessary) + final LayoutInflater inflater = LayoutInflater.from(getContext()); + final ArrayList tasks = stack.getTasks(); + for (int i = getChildCount(); i < tasks.size(); i++) { + final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false); + addView(taskView); + } + while (getChildCount() > tasks.size()) { + final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1); + removeView(taskView); + loader.unloadTaskData(taskView.getTask()); + } + + // Rebind all task views + for (int i = tasks.size() - 1; i >= 0; i--) { + final Task task = tasks.get(i); + final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1); + taskView.bind(task); + loader.loadTaskData(task); + } + } + + public void launchTaskWithId(int taskId) { + for (int i = 0; i < getChildCount(); i++) { + final TaskView taskView = (TaskView) getChildAt(i); + if (taskView.getTask().key.id == taskId) { + taskView.launchTask(false /* animate */); + return; + } + } + } + + private void updateTaskStackListenerState() { + boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow() + && getWindowVisibility() == VISIBLE; + if (registerStackListener != mTaskStackListenerRegistered) { + if (registerStackListener) { + ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + } else { + ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener); + } + mTaskStackListenerRegistered = registerStackListener; } } } diff --git a/quickstep/src/com/android/quickstep/SnapshotDragView.java b/quickstep/src/com/android/quickstep/SnapshotDragView.java index 791fe9ff17..2ef39422d2 100644 --- a/quickstep/src/com/android/quickstep/SnapshotDragView.java +++ b/quickstep/src/com/android/quickstep/SnapshotDragView.java @@ -23,6 +23,7 @@ import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; +import com.android.systemui.shared.recents.view.AnimateableViewBounds; /** * Floating view which shows the task snapshot allowing it to be dragged and placed. @@ -31,12 +32,20 @@ public class SnapshotDragView extends AbstractFloatingView implements Insettable private final Launcher mLauncher; private final Bitmap mSnapshot; + private final AnimateableViewBounds mViewBounds; public SnapshotDragView(Launcher launcher, Bitmap snapshot) { super(launcher, null); mLauncher = launcher; mSnapshot = snapshot; + mViewBounds = new AnimateableViewBounds(this, 0); setWillNotDraw(false); + setClipToOutline(true); + setOutlineProvider(mViewBounds); + } + + AnimateableViewBounds getViewBounds() { + return mViewBounds; } @Override diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java index 96c93c23f9..55d22e0323 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java @@ -22,20 +22,17 @@ import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorMatrixColorFilter; import android.graphics.LightingColorFilter; import android.graphics.Matrix; -import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Shader; import android.util.AttributeSet; -import android.view.Display; -import android.view.View; -import android.view.ViewOutlineProvider; import android.widget.FrameLayout; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; import com.android.systemui.shared.recents.model.ThumbnailData; /** @@ -95,6 +92,14 @@ public class TaskThumbnailView extends FrameLayout { } } + /** + * Sets the alpha of the dim layer on top of this view. + */ + public void setDimAlpha(float dimAlpha) { + mDimAlpha = dimAlpha; + updateThumbnailPaintFilter(); + } + @Override protected void onDraw(Canvas canvas) { int viewWidth = getMeasuredWidth(); @@ -105,43 +110,40 @@ public class TaskThumbnailView extends FrameLayout { (int) (mThumbnailRect.height() * mThumbnailScale)); if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { - int topOffset = 0; // Draw the background, there will be some small overdraw with the thumbnail if (thumbnailWidth < viewWidth) { // Portrait thumbnail on a landscape task view - canvas.drawRect(Math.max(0, thumbnailWidth), topOffset, viewWidth, viewHeight, + canvas.drawRect(Math.max(0, thumbnailWidth), 0, viewWidth, viewHeight, mBgFillPaint); } if (thumbnailHeight < viewHeight) { // Landscape thumbnail on a portrait task view - canvas.drawRect(0, Math.max(topOffset, thumbnailHeight), viewWidth, viewHeight, + canvas.drawRect(0, Math.max(0, thumbnailHeight), viewWidth, viewHeight, mBgFillPaint); } // Draw the thumbnail - canvas.drawRect(0, topOffset, thumbnailWidth, thumbnailHeight, mDrawPaint); + canvas.drawRect(0, 0, thumbnailWidth, thumbnailHeight, mDrawPaint); } else { canvas.drawRect(0, 0, viewWidth, viewHeight, mBgFillPaint); } } - void updateThumbnailPaintFilter() { - int mul = (int) ((1.0f - mDimAlpha) * 255); + private void updateThumbnailPaintFilter() { + int mul = (int) (mDimAlpha * 255); if (mBitmapShader != null) { - mLightingColorFilter = new LightingColorFilter(Color.WHITE, - Color.argb(255, mul, mul, mul)); + mLightingColorFilter = new LightingColorFilter(Color.argb(255, mul, mul, mul), 0); mDrawPaint.setColorFilter(mLightingColorFilter); mDrawPaint.setColor(0xFFffffff); mBgFillPaint.setColorFilter(mLightingColorFilter); } else { - int grey = mul; mDrawPaint.setColorFilter(null); - mDrawPaint.setColor(Color.argb(255, grey, grey, grey)); + mDrawPaint.setColor(Color.argb(255, mul, mul, mul)); } invalidate(); } - public void updateThumbnailMatrix() { + private void updateThumbnailMatrix() { mThumbnailScale = 1f; if (mBitmapShader != null && mThumbnailData != null) { if (getMeasuredWidth() == 0) { @@ -152,8 +154,7 @@ public class TaskThumbnailView extends FrameLayout { float invThumbnailScale = 1f / mThumbnailScale; final Configuration configuration = getContext().getApplicationContext().getResources().getConfiguration(); - final Point displaySize = new Point(); - getDisplay().getRealSize(displaySize); + final DeviceProfile profile = Launcher.getLauncher(getContext()).getDeviceProfile(); if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) { // If we are in the same orientation as the screenshot, just scale it to the @@ -163,23 +164,17 @@ public class TaskThumbnailView extends FrameLayout { // Scale the landscape thumbnail up to app size, then scale that to the task // view size to match other portrait screenshots mThumbnailScale = invThumbnailScale * - ((float) getMeasuredWidth() / displaySize.x); + ((float) getMeasuredWidth() / profile.getCurrentWidth()); } } else { // Otherwise, scale the screenshot to fit 1:1 in the current orientation mThumbnailScale = invThumbnailScale; } } - mMatrix.setTranslate(-mThumbnailData.insets.left * mThumbnailScale, - -mThumbnailData.insets.top * mThumbnailScale); + mMatrix.setTranslate(-mThumbnailData.insets.left, -mThumbnailData.insets.top); mMatrix.postScale(mThumbnailScale, mThumbnailScale); mBitmapShader.setLocalMatrix(mMatrix); } invalidate(); } - - public void setDimAlpha(float dimAlpha) { - mDimAlpha = dimAlpha; - updateThumbnailPaintFilter(); - } } diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java index ea584f0095..f6408a89d3 100644 --- a/quickstep/src/com/android/quickstep/TaskView.java +++ b/quickstep/src/com/android/quickstep/TaskView.java @@ -16,17 +16,26 @@ package com.android.quickstep; +import android.app.ActivityOptions; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.launcher3.R; +import com.android.launcher3.uioverrides.OverviewState; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskCallbacks; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; +import com.android.systemui.shared.recents.view.RecentsTransition; import com.android.systemui.shared.system.ActivityManagerWrapper; +import java.util.ArrayList; +import java.util.List; + /** * A task in the Recents view. */ @@ -48,10 +57,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks { super(context, attrs, defStyleAttr); setWillNotDraw(false); setOnClickListener((view) -> { - if (mTask != null) { - ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key, - null, null, null); - } + launchTask(true /* animate */); }); } @@ -65,20 +71,63 @@ public class TaskView extends FrameLayout implements TaskCallbacks { * Updates this task view to the given {@param task}. */ public void bind(Task task) { + if (mTask != null) { + mTask.removeCallback(this); + } mTask = task; task.addCallback(this); } + public Task getTask() { + return mTask; + } + + public TaskThumbnailView getThumbnail() { + return mSnapshotView; + } + + public void launchTask(boolean animate) { + if (mTask != null) { + final ActivityOptions opts; + if (animate) { + // Calculate the bounds of the thumbnail to animate from + final Rect bounds = new Rect(); + final int[] pos = new int[2]; + mSnapshotView.getLocationInWindow(pos); + bounds.set(pos[0], pos[1], + pos[0] + mSnapshotView.getWidth(), + pos[1] + mSnapshotView.getHeight()); + AppTransitionAnimationSpecsFuture animFuture = + new AppTransitionAnimationSpecsFuture(getHandler()) { + @Override + public List composeSpecs() { + ArrayList specs = + new ArrayList<>(); + specs.add(new AppTransitionAnimationSpecCompat(mTask.key.id, null, + bounds)); + return specs; + } + }; + opts = RecentsTransition.createAspectScaleAnimation( + getContext(), getHandler(), true /* scaleUp */, animFuture, null); + } else { + opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0); + } + ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key, + opts, null, null); + } + } + @Override public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) { mSnapshotView.setThumbnail(thumbnailData); - mSnapshotView.setDimAlpha(1f); mIconView.setImageDrawable(task.icon); } @Override public void onTaskDataUnloaded() { - // Do nothing + mSnapshotView.setThumbnail(null); + mIconView.setImageDrawable(null); } @Override diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index dcfa53b41e..121817698c 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -23,8 +23,10 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityOptions; import android.app.Service; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.PointF; @@ -32,6 +34,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import android.view.Display; import android.view.MotionEvent; @@ -39,9 +42,17 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowManager; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options; +import com.android.systemui.shared.recents.model.RecentsTaskLoader; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.BackgroundExecutor; + +import java.util.concurrent.Future; /** * Service connected by system-UI for handling touch interaction. @@ -50,6 +61,8 @@ public class TouchInteractionService extends Service { private static final String TAG = "TouchInteractionService"; + private static RecentsTaskLoader sRecentsTaskLoader; + private final IBinder mMyBinder = new IOverviewProxy.Stub() { @Override @@ -68,6 +81,7 @@ public class TouchInteractionService extends Service { private Intent mHomeIntent; private ComponentName mLauncher; + private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); private int mActivePointerId = INVALID_POINTER_ID; @@ -89,6 +103,14 @@ public class TouchInteractionService extends Service { ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0); mLauncher = new ComponentName(getPackageName(), info.activityInfo.name); mHomeIntent.setComponent(mLauncher); + + Resources res = getResources(); + if (sRecentsTaskLoader == null) { + sRecentsTaskLoader = new RecentsTaskLoader(this, + res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize), + res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0); + sRecentsTaskLoader.startLoader(this); + } } @Override @@ -97,6 +119,10 @@ public class TouchInteractionService extends Service { return mMyBinder; } + public static RecentsTaskLoader getRecentsTaskLoader() { + return sRecentsTaskLoader; + } + private void handleMotionEvent(MotionEvent ev) { if (ev.getActionMasked() != MotionEvent.ACTION_DOWN && mVelocityTracker == null) { return; @@ -170,16 +196,46 @@ public class TouchInteractionService extends Service { } private void startTouchTracking() { - mInteractionHandler = new NavBarSwipeInteractionHandler(getCurrentTaskSnapshot(), mRunningTask); + // Create the shared handler + mInteractionHandler = new NavBarSwipeInteractionHandler(getCurrentTaskSnapshot(), + mRunningTask); - Bundle extras = new Bundle(); - extras.putBinder(EXTRA_STATE_HANDLER, mInteractionHandler); - Intent homeIntent = new Intent(mHomeIntent).putExtras(extras); + // Preload and start the recents activity on a background thread + final Context context = this; + final int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().id; + final RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(context); + Future loadPlanFuture = BackgroundExecutor.get().submit(() -> { + // Preload the plan + RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader(); + loadPlan.preloadPlan(loader, runningTaskId, UserHandle.myUserId()); - // TODO: Call ActivityManager#startRecentsActivity instead, so that the system knows that - // recents was started and not Home. - startActivity(homeIntent, - ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle()); + // Pass the + Bundle extras = new Bundle(); + extras.putBinder(EXTRA_STATE_HANDLER, mInteractionHandler); + + // Start the activity + Intent homeIntent = new Intent(mHomeIntent); + homeIntent.putExtras(extras); + startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle()); + /* + ActivityManagerWrapper.getInstance().startRecentsActivity(null, options, + ActivityOptions.makeCustomAnimation(this, 0, 0), UserHandle.myUserId(), + null, null); + */ + + // Kick off loading of the plan while the activity is starting + Options loadOpts = new Options(); + loadOpts.runningTaskId = runningTaskId; + loadOpts.loadIcons = true; + loadOpts.loadThumbnails = true; + loadOpts.numVisibleTasks = 2; + loadOpts.numVisibleTaskThumbnails = 2; + loadOpts.onlyLoadForCache = false; + loadOpts.onlyLoadPausedActivities = false; + loader.loadTasks(loadPlan, loadOpts); + }, loadPlan); + + mInteractionHandler.setLastLoadPlan(loadPlanFuture); } private void endInteraction() { diff --git a/res/values/config.xml b/res/values/config.xml index 54328e1486..0ff0d758bf 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -134,4 +134,8 @@ + + 6 + 12 + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 957ec191cb..59736d8fde 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -232,4 +232,7 @@ 19dp 0.5dp 70dp + + + 10dp diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 6030e08865..6ddaf29db8 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -27,7 +27,6 @@ import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; @@ -681,13 +680,13 @@ public class DeviceProfile { } } - private int getCurrentWidth() { + public int getCurrentWidth() { return isLandscape ? Math.max(widthPx, heightPx) : Math.min(widthPx, heightPx); } - private int getCurrentHeight() { + public int getCurrentHeight() { return isLandscape ? Math.min(widthPx, heightPx) : Math.max(widthPx, heightPx); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b1b3452e4b..60c00fb55b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -63,7 +63,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; @@ -362,6 +361,8 @@ public class Launcher extends BaseActivity restoreState(savedInstanceState); + InternalStateHandler.handleCreate(this, getIntent()); + // We only load the page synchronously if the user rotates (or triggers a // configuration change) while launcher is in the foreground int currentScreen = PagedView.INVALID_RESTORE_PAGE; @@ -1347,7 +1348,6 @@ public class Launcher extends BaseActivity // Check this condition before handling isActionMain, as this will get reset. boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL) && AbstractFloatingView.getTopOpenView(this) == null; - boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); if (isActionMain) { if (mWorkspace == null) { @@ -1407,7 +1407,7 @@ public class Launcher extends BaseActivity }); } } - InternalStateHandler.handleIntent(this, intent); + InternalStateHandler.handleNewIntent(this, intent, alreadyOnHome); TraceHelper.endSection("NEW_INTENT"); } diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java index a90ed36aa9..c6feefc8bc 100644 --- a/src/com/android/launcher3/states/InternalStateHandler.java +++ b/src/com/android/launcher3/states/InternalStateHandler.java @@ -29,15 +29,30 @@ public abstract class InternalStateHandler extends Binder implements OnResumeCal public static final String EXTRA_STATE_HANDLER = "launcher.state_handler"; - public abstract void onNewIntent(Launcher launcher); + public abstract void onCreate(Launcher launcher); + public abstract void onNewIntent(Launcher launcher, boolean alreadyOnHome); - public static void handleIntent(Launcher launcher, Intent intent) { - IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER); - if (stateBinder instanceof InternalStateHandler) { - InternalStateHandler handler = (InternalStateHandler) stateBinder; - launcher.setOnResumeCallback(handler); - handler.onNewIntent(launcher); + public static void handleCreate(Launcher launcher, Intent intent) { + if (intent.getExtras() != null) { + IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER); + if (stateBinder instanceof InternalStateHandler) { + InternalStateHandler handler = (InternalStateHandler) stateBinder; + launcher.setOnResumeCallback(handler); + handler.onCreate(launcher); + } + intent.getExtras().remove(EXTRA_STATE_HANDLER); + } + } + + public static void handleNewIntent(Launcher launcher, Intent intent, boolean alreadyOnHome) { + if (intent.getExtras() != null) { + IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER); + if (stateBinder instanceof InternalStateHandler) { + InternalStateHandler handler = (InternalStateHandler) stateBinder; + launcher.setOnResumeCallback(handler); + handler.onNewIntent(launcher, alreadyOnHome); + } + intent.getExtras().remove(EXTRA_STATE_HANDLER); } - intent.getExtras().remove(EXTRA_STATE_HANDLER); } } From ac9408a5cd7744a8dbc66a61114665ab6e4051de Mon Sep 17 00:00:00 2001 From: Mario Bertschler Date: Wed, 18 Oct 2017 10:31:36 -0700 Subject: [PATCH 091/885] Enable work profile tab in all apps. This CL will bring two tabs to all apps: Personal and Work, currently only if the user has a workfile set up and behind a feature flag defaulting to disabled. Bug: 68713881 Change-Id: Ib5a558281ef3593359db3ad593ee1d0cf279f547 --- res/layout/all_apps.xml | 53 +- res/layout/all_apps_rv_layout.xml | 26 + res/layout/all_apps_tabs.xml | 34 ++ res/values/dimens.xml | 1 + res/values/strings.xml | 6 + .../android/launcher3/BaseRecyclerView.java | 8 +- src/com/android/launcher3/DeviceProfile.java | 6 +- .../allapps/AllAppsContainerView.java | 476 +++++++++++++++--- .../launcher3/allapps/AllAppsGridAdapter.java | 12 +- .../allapps/AllAppsRecyclerView.java | 32 +- .../allapps/AlphabeticalAppsList.java | 126 ++--- .../allapps/FloatingHeaderHandler.java | 139 +++++ .../allapps/InterceptingViewPager.java | 91 ++++ .../launcher3/allapps/PredictionRowView.java | 139 +++++ .../launcher3/allapps/SearchUiManager.java | 2 +- .../search/AppsSearchContainerLayout.java | 26 +- .../android/launcher3/config/BaseFlags.java | 2 + .../launcher3/util/ItemInfoMatcher.java | 13 + .../views/RecyclerViewFastScroller.java | 6 +- 19 files changed, 1009 insertions(+), 189 deletions(-) create mode 100644 res/layout/all_apps_rv_layout.xml create mode 100644 res/layout/all_apps_tabs.xml create mode 100644 src/com/android/launcher3/allapps/FloatingHeaderHandler.java create mode 100644 src/com/android/launcher3/allapps/InterceptingViewPager.java create mode 100644 src/com/android/launcher3/allapps/PredictionRowView.java diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index 6f837aeca3..05f509f131 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -27,25 +27,52 @@ android:focusableInTouchMode="true" android:saveEnabled="false" > - - + + + + + android:layout_height="wrap_content" + android:clickable="true" + android:paddingTop="30dp" + android:layout_below="@id/search_container_all_apps" > + + + + +